Native server packages¶
The initiative-server packages run the backend, browser app and document editor
directly on a Linux host or an unprivileged Proxmox LXC. They include the locked
Python dependencies and the matching signed phone/desktop update assets. The
installed server needs no Docker runtime, Node.js, Rust compiler or dependency
download step.
The existing initiative-<version>.deb is the desktop client. Choose a package
whose name starts with initiative-server for a headless server.
Supported targets¶
- Debian 13, with system Python 3.13 and systemd.
- Alpine 3.24, with system Python 3.14 and OpenRC.
- Each release builds separately for amd64/x86_64 and arm64/aarch64.
The native dependencies are built for each target's Python ABI and libc. A Debian package cannot be installed on Alpine, and a virtual environment copied between these distributions is not a supported substitute. Major distribution/Python upgrades require a matching rebuilt server package.
Install¶
Provision a PostgreSQL 17 database first, using the connection requirements in Installation. The database can be local or remote. Package installation does not create database accounts, choose database passwords or run migrations.
Download the package for your distribution and architecture from the release's Native server packages section. Check it against that release's server package SHA-256 manifest before installing.
On Debian, install the local .deb with apt install ./<downloaded-package>.deb.
APT resolves the declared distribution dependencies. The .deb and checksum
manifest are release downloads, not an APT repository.
On Alpine, install the release's initiative-server.rsa.pub into /etc/apk/keys/
before using apk add ./<downloaded-package>.alpine. Obtain and verify the public key
through a trusted channel before installing it. Keep that trust key for subsequent
upgrades. Do not use --allow-untrusted. The Alpine package is named .alpine
rather than .apk so Android updaters don't mistake it for the phone app; apk
installs it the same.
The package creates the unprivileged initiative account and installs a service,
but does not start it automatically. This gives you time to configure a private
server and take a database backup before a package transaction can trigger a
schema migration.
Configure and start¶
Edit /etc/initiative/server.env as root. Set at least:
DATABASE_URL=postgresql+asyncpg://initiative:PERCENT_ENCODED_PASSWORD@127.0.0.1:5432/initiative
SECRET_KEY=REPLACE_WITH_A_STRONG_UNIQUE_KEY
APP_URL=https://tasks.example.org
Use openssl rand -hex 32 to generate the key. Percent-encode special characters
in a database URL password. Preserve the same SECRET_KEY across upgrades: it
protects encrypted data as well as sessions.
The server listens on 127.0.0.1:8173 by default. Put a reverse proxy with trusted
HTTPS and WebSocket support in front of it, and set BEHIND_PROXY=true. A proxy on
this machine or your LAN, and Cloudflare in front of it, need nothing more; see
Running behind a reverse proxy for the one setup that does. To serve another
interface, explicitly set INITIATIVE_HOST; do not expose an unconfigured server
on a public network.
Public registration is disabled in the package's initial configuration. Supply
FIRST_OWNER_EMAIL and FIRST_OWNER_PASSWORD to bootstrap the first owner, or
temporarily enable registration while creating your first account privately.
Remove the bootstrap password from the configuration once the owner exists, and
leave public registration disabled for an invite-only instance.
The environment file is configuration data, not a shell script. Write dotenv assignments; shell commands in it are not executed. See Configuration for the other options and Single sign-on for connecting Authentik or another OIDC provider after the public URL works.
On Debian:
systemctl enable --now initiative-server
systemctl status initiative-server --no-pager
journalctl -u initiative-server -n 100 --no-pager
On Alpine:
rc-update add initiative-server default
rc-service initiative-server start
rc-service initiative-server status
tail -n 100 /var/log/initiative/server.err
Check readiness locally:
The first explicit service start runs Initiative's normal database bootstrap and migrations. A package transaction itself does not contact the database.
Files, upgrades and removal¶
| Location | Purpose |
|---|---|
/usr/lib/initiative |
Package-owned application, dependencies and release assets |
/etc/initiative/server.env |
Operator-owned configuration and secrets |
/var/lib/initiative/uploads |
Uploaded files |
/var/lib/initiative |
Root-owned service home; initiative has read/traverse access |
/var/log/initiative |
OpenRC service output; Debian also uses the journal |
Keep local uploads under /var/lib/initiative/uploads, which the service owns.
The parent /var/lib/initiative is owned by root with mode 0750; upgrades also
secure the parent of an older installation. For a custom
storage or export directory under that parent, create it as root and grant the
initiative account access before configuring it. On Debian, a custom
UPLOADS_DIR outside /var/lib/initiative also needs a systemd override granting
ReadWritePaths for the chosen directory.
Before upgrading, back up the database, PostgreSQL roles, configuration and uploads as described in Backups and updates. Install the new package with your distribution's package manager. It stops the running service so old Python processes do not keep running against replaced files. Configuration and uploaded files are preserved. Review the release notes, then explicitly start the service again to apply migrations.
Removing the package stops the service and preserves operator configuration and state. Debian purge removes packaged configuration, but deliberately preserves uploaded files, database contents and the service account. Delete those separately only when you intend to destroy the instance.
Installing an older package does not undo a database migration. Restore the matching pre-upgrade database backup before starting an older application version if a newer version changed its schema.
Building and releasing packages¶
The recipes and lifecycle tests live in packaging/server/, and
server-packages.yml builds each package on its own distribution, then installs,
upgrades and removes it in a clean one, on both architectures.
| Build | Where it runs | Browser app, editor and app update |
|---|---|---|
| Release | Release Candidate, on the release branch | Copied from that release's Docker image |
| Test | A pull request touching the packaging, or a run by hand | Built from source by scripts/build-server-assets.sh and signed with a key made for the run |
A release's packages carry exactly the web assets of the image the walk and the journeys checked, and the candidate is checked only once its packages pass too. Build and Release then attaches that run's packages to the release; without them, it builds them from the image it publishes.
Signing an Alpine package needs an RSA PEM SERVER_APK_SIGNING_KEY in the
ota-release environment, and a release candidate fails without it. Its public
half is published beside the packages.
Test packages carry a -server-test- version suffix. They exercise the packaging
and never stand in for a release's packages.