etherpad-lite/packaging
John McLear 6bb879ed03
feat(packaging): add Debian (.deb) build via nfpm with systemd unit (#7559)
* feat(packaging): add Debian (.deb) build via nfpm with systemd unit

First-class Debian packaging for Etherpad, producing signed-ready
etherpad-lite_<version>_<arch>.deb artefacts for amd64 and arm64 from a
single nfpm manifest. Installing the package gives users:

- /opt/etherpad-lite with a prebuilt, self-contained node_modules/ — no
  pnpm required at runtime, just `nodejs (>= 20)`.
- etherpad system user/group, created via `adduser` in preinst.
- /etc/etherpad-lite/settings.json seeded from the template on first
  install, preserved across upgrades, removed on `purge`.
- /var/lib/etherpad-lite owned by etherpad:etherpad, with the default
  dirty-DB retargeted there so ProtectSystem=strict works.
- /lib/systemd/system/etherpad-lite.service — hardened unit
  (NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp,
  RestrictAddressFamilies) with Restart=on-failure.
- /usr/bin/etherpad-lite CLI wrapper running `node --import tsx/esm`.

CI (.github/workflows/deb-package.yml) triggers on v* tags, builds both
arches via native runners (ubuntu-latest + ubuntu-24.04-arm), smoke-tests
the amd64 package end-to-end (install → systemctl start → curl /health
→ purge → confirm user removed), and attaches the artefacts to the
GitHub Release.

Publishing to an APT repo (Cloudsmith, Launchpad PPA, self-hosted
reprepro) is intentionally out of scope — needs a governance decision on
who holds the signing key. Recipes are documented in packaging/README.md.

Refs #7529

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(deb): fail smoke test on /health timeout, tighten default-file perms, 2-space indent

Addresses Qodo review feedback on #7559:

1. Smoke test false-positive: the `for` loop polling /health never failed
   the job if the endpoint stayed down — `curl && break || sleep 2`
   keeps returning 0 from the trailing `sleep`, so `set -e` never
   trips. CI could attach a broken .deb to a release. Fix: track
   success explicitly and exit 1 (plus dump journald logs for
   diagnostics) when the service never becomes healthy.

2. /etc/default/etherpad-lite was world-readable (0644). systemd loads
   it via `EnvironmentFile=…`, and Etherpad supports
   ${ENV_VAR}-substitution for secrets (DB_PASSWORD etc.), so any
   local user could read anything admins drop there. Fix: install the
   conffile as root:etherpad 0640 — only root and the service user can
   read it.

3. Indentation: reflow maintainer scripts from 4-space to 2-space to
   match the repo style rule.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 18:45:50 +01:00
..
bin feat(packaging): add Debian (.deb) build via nfpm with systemd unit (#7559) 2026-04-22 18:45:50 +01:00
scripts feat(packaging): add Debian (.deb) build via nfpm with systemd unit (#7559) 2026-04-22 18:45:50 +01:00
systemd feat(packaging): add Debian (.deb) build via nfpm with systemd unit (#7559) 2026-04-22 18:45:50 +01:00
nfpm.yaml feat(packaging): add Debian (.deb) build via nfpm with systemd unit (#7559) 2026-04-22 18:45:50 +01:00
README.md feat(packaging): add Debian (.deb) build via nfpm with systemd unit (#7559) 2026-04-22 18:45:50 +01:00

Etherpad Debian / RPM packaging

Produces native .deb (and, with the same manifest, .rpm / .apk) packages for Etherpad using nfpm.

Layout

packaging/
  nfpm.yaml                   # nfpm package manifest
  bin/etherpad-lite           # /usr/bin launcher
  scripts/                    # preinst / postinst / prerm / postrm
  systemd/etherpad-lite.service
  systemd/etherpad-lite.default
  etc/settings.json.dist      # populated in CI from settings.json.template

Built artefacts land in ./dist/.

Building locally

Prereqs: Node 22, pnpm 10+, nfpm.

pnpm install --frozen-lockfile
pnpm run build:etherpad

# Stage the tree the way CI does:
STAGE=staging/opt/etherpad-lite
mkdir -p "$STAGE"
cp -a src bin package.json pnpm-workspace.yaml README.md LICENSE \
      node_modules "$STAGE/"
printf 'packages:\n  - src\n  - bin\n' > "$STAGE/pnpm-workspace.yaml"
cp settings.json.template packaging/etc/settings.json.dist

VERSION=$(node -p "require('./package.json').version") \
ARCH=amd64 \
    nfpm package --packager deb -f packaging/nfpm.yaml --target dist/

Installing

sudo apt install ./dist/etherpad-lite_2.6.1_amd64.deb
sudo systemctl start etherpad-lite
curl http://localhost:9001/health

apt will pull in nodejs (>= 20); on Ubuntu 22.04 add NodeSource first:

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -

Configuration

  • Edit /etc/etherpad-lite/settings.json, then sudo systemctl restart etherpad-lite.
  • Environment overrides: /etc/default/etherpad-lite.
  • Logs: journalctl -u etherpad-lite -f.
  • Data (dirty-DB default): /var/lib/etherpad-lite/.

Upgrading

dpkg --install etherpad-lite_<new>.deb (or apt install) replaces the app tree under /opt/etherpad-lite while preserving /etc/etherpad-lite/* and /var/lib/etherpad-lite/*. The service is restarted automatically.

Removing

  • sudo apt remove etherpad-lite — keeps config and data.
  • sudo apt purge etherpad-lite — also removes config, data, and the etherpad system user.

Publishing to an APT repository (follow-up)

Out of scope here — requires credentials and ownership decisions. Recipes once a repo is picked:

  • Cloudsmith (easiest, free OSS tier): cloudsmith push deb ether/etherpad-lite/any-distro/any-version dist/*.deb
  • Launchpad PPA: requires signed source packages (a debian/ tree), which nfpm does not produce — use debuild separately.
  • Self-hosted reprepro: reprepro -b /srv/apt includedeb stable dist/*.deb

Wire the chosen option into .github/workflows/deb-package.yml after the release job.