Install with systemd
Install separately verified native release components as hardened Linux services behind operator-managed infrastructure.
Run FFDB as native Linux services
This guide installs packaged FFDB server components as hardened systemd services with one Caddy HTTPS and static-asset gateway.
It supports operators who need native service supervision while preserving the tested binary protocol and filesystem boundaries.
Choose it when PostgreSQL, S3, Resend, Caddy, secrets, DNS, and Linux service management are operator-owned.
Requirements for Install with systemd
- Prerequisite — A current Linux distribution with systemd, Caddy, curl, rsync, and root access for reviewed installation steps.
- Prerequisite — Public DNS for the configured HTTPS origin, durable local or block storage, and an independently protected backup location.
- Required value — Matching API, database-worker, sync-worker, and web assets from one release bundle.
- Required value — The complete production environment values plus stable ffdb user and directory ownership.
Provision dependencies
This installation shape assumes a Linux host with systemd, an HTTPS reverse proxy, PostgreSQL, and S3-compatible storage already provisioned. Production FFDB rejects SMTP and requires a Resend API key.
- Use durable local or block storage for project SQLite files; verify SQLite locking before using any network filesystem.
- Use independently protected backup and organization-metrics directories, plus an independent 32-byte backup master key.
- Keep PostgreSQL and the server-facing S3 endpoint on private, allowlisted networks.
- Run the API and its database-worker binary from the same release; mixed protocol-v1 releases are unsupported.
Download and verify one component release
Choose an announced tag from the canonical GitHub Releases page and download its architecture-matched native archive, signed checksum list, and Sigstore bundle from that same tag. Verify the checksum list before trusting its archive digest; the extracted directory is ffdb-native-VERSION even though the downloaded filename includes the operating system and architecture. Configure the environment in the next step before running the installer because the installer uses its exact public S3 origin to render the gateway Content-Security-Policy.
VERSION=0.3.0
RELEASE_BASE="https://github.com/Forever-Frameworks-LLC/ffdb/releases/download/v$VERSION"
curl -fsSLO "$RELEASE_BASE/SHA256SUMS"
curl -fsSLO "$RELEASE_BASE/SHA256SUMS.sigstore.json"
curl -fsSLO "$RELEASE_BASE/ffdb-native-linux-amd64-$VERSION.tar.gz"
cosign verify-blob SHA256SUMS \
--bundle SHA256SUMS.sigstore.json \
--certificate-identity "https://github.com/Forever-Frameworks-LLC/ffdb/.github/workflows/release.yml@refs/tags/v$VERSION" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
sha256sum --check --ignore-missing SHA256SUMS
tar -xzf "ffdb-native-linux-amd64-$VERSION.tar.gz"Create the production environment file
Store secrets in your system secret manager when possible. For a fresh host, stage the complete configuration as a root-only file; the installer creates the ffdb account and copies this file to /etc/ffdb/ffdb.env with owner root:ffdb and mode 0640. Never commit either copy.
FFDB_ENVIRONMENT=production
FFDB_HTTP_BIND=127.0.0.1:8080
FFDB_PUBLIC_BASE_URL=https://ffdb.example.com
FFDB_CORS_ALLOWED_ORIGINS=https://ffdb.example.com
FFDB_TRUSTED_PROXY_CIDRS=127.0.0.1/32,::1/128
FFDB_DATABASE_URL=postgres://ffdb_runtime:REDACTED@postgres.example.net/ffdb
FFDB_POSTGRES_MAX_CONNECTIONS=20
FFDB_NODE_ID=01965555-0000-7000-8000-000000000001
FFDB_NODE_NAME=ffdb-prod-01
FFDB_DATABASE_ROOT=/var/lib/ffdb/projects
FFDB_BACKUP_ROOT=/var/lib/ffdb/backups
FFDB_METRICS_ROOT=/var/lib/ffdb/metrics
FFDB_DATABASE_WORKER=/usr/local/bin/ffdb-database-worker
FFDB_WORKER_MAX_PROCESSES=8
FFDB_WORKER_QUEUE_CAPACITY=32
FFDB_MASTER_KEY=REPLACE_WITH_BASE64_32_BYTES
FFDB_BACKUP_MASTER_KEY=REPLACE_WITH_DIFFERENT_BASE64_32_BYTES
FFDB_CURSOR_HMAC_KEY=REPLACE_WITH_AT_LEAST_32_RANDOM_CHARACTERS
FFDB_BOOTSTRAP_TOKEN=REPLACE_WITH_AT_LEAST_32_RANDOM_CHARACTERS
FFDB_S3_ENDPOINT=https://s3.us-east-1.amazonaws.com
FFDB_S3_PUBLIC_ENDPOINT=https://s3.us-east-1.amazonaws.com
FFDB_S3_PUBLIC_ORIGIN=https://s3.us-east-1.amazonaws.com
FFDB_S3_REGION=us-east-1
FFDB_S3_BUCKET=ffdb-production
FFDB_S3_ACCESS_KEY_ID=REDACTED
FFDB_S3_SECRET_ACCESS_KEY=REDACTED
FFDB_S3_ALLOW_PRIVATE_NETWORK=false
FFDB_EMAIL_TRANSPORT=resend
FFDB_RESEND_API_KEY=REDACTED
FFDB_EMAIL_FROM="FFDB <noreply@example.com>"
RUST_LOG=ffdb=info,tower_http=info
FFDB_SYNC_STATE_DIR=/var/lib/ffdb/sync
FFDB_SYNC_MAINTENANCE_INTERVAL_SECONDS=60
FFDB_SYNC_STALE_TEMPORARY_SECONDS=3600sudo chown root:root /root/ffdb.env
sudo chmod 0600 /root/ffdb.envInstall the verified release
The native installer creates the ffdb account and directories, copies the staged environment with restricted ownership, installs the version-matched binaries, units, backup helper, and static web assets, renders the Caddy site from the public and storage origins, and validates it before returning. Caddy terminates TLS, serves all three compiled sites, and proxies directly to loopback Axum; nginx is not part of this native path.
cd "ffdb-native-$VERSION"
sudo ./install-native.sh --env-file /root/ffdb.env
systemctl cat ffdb-api.service
systemctl cat ffdb-sync-worker.service
systemctl cat ffdb-gateway.serviceInstall the API unit
The API process validates and canonicalizes the database-worker binary and its project, backup, and metrics roots before listening. It spawns bounded worker children with project, database, node, route-generation, and backup-key context supplied internally.
[Unit]
Description=FFDB API and isolated SQLite worker supervisor
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=ffdb
Group=ffdb
WorkingDirectory=/var/lib/ffdb
EnvironmentFile=/etc/ffdb/ffdb.env
ExecStart=/usr/local/bin/ffdb-api
Restart=on-failure
RestartSec=5s
KillSignal=SIGINT
TimeoutStopSec=30s
UMask=0077
NoNewPrivileges=true
PrivateDevices=true
PrivateTmp=true
ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectSystem=strict
ReadWritePaths=/var/lib/ffdb/projects /var/lib/ffdb/backups /var/lib/ffdb/metrics
CapabilityBoundingSet=
AmbientCapabilities=
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictRealtime=true
RestrictSUIDSGID=true
SystemCallArchitectures=native
LimitNOFILE=65536
TasksMax=512
[Install]
WantedBy=multi-user.targetInstall the sync maintenance unit
[Unit]
Description=FFDB durable sync-state maintenance worker
Wants=network-online.target
After=network-online.target ffdb-api.service
[Service]
Type=simple
User=ffdb
Group=ffdb
WorkingDirectory=/var/lib/ffdb
EnvironmentFile=/etc/ffdb/ffdb.env
ExecStart=/usr/local/bin/ffdb-sync-worker
Restart=on-failure
RestartSec=5s
KillSignal=SIGINT
TimeoutStopSec=30s
UMask=0077
NoNewPrivileges=true
PrivateDevices=true
PrivateTmp=true
ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectSystem=strict
ReadWritePaths=/var/lib/ffdb/sync
CapabilityBoundingSet=
AmbientCapabilities=
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictRealtime=true
RestrictSUIDSGID=true
SystemCallArchitectures=native
LimitNOFILE=8192
TasksMax=64
[Install]
WantedBy=multi-user.targetVerify the installed static gateway
The native installer publishes version-matched landing, docs, and portal assets under /var/www/ffdb and renders one Caddy configuration. Caddy owns public automatic HTTPS and a loopback listener at 127.0.0.1:5173; both serve the compiled sites and proxy directly to Axum at 127.0.0.1:8080. There is no Caddy-to-nginx hop and Vite is not a runtime service.
sudo caddy validate --config /etc/ffdb/Caddyfile --adapter caddyfile
find /var/www/ffdb -maxdepth 2 -type f -name index.html -printStart and verify
From outside the host, repeat readiness through HTTPS and directly reload nested /docs/ and /app/ routes. Normal clients use the gateway; the direct Axum listener is for local service diagnostics and must remain loopback-only. On supported distributions, systemd-analyze security is a useful review aid, but its score does not replace functional or threat-model testing.
sudo systemctl daemon-reload
sudo systemctl enable --now ffdb-api.service ffdb-sync-worker.service
sudo systemctl enable --now ffdb-gateway.service
sudo systemctl --no-pager --full status \
ffdb-api.service ffdb-sync-worker.service ffdb-gateway.service
journalctl -u ffdb-api.service -u ffdb-sync-worker.service -u ffdb-gateway.service --since today
# Direct Axum service diagnostic, available only on this host.
curl --fail http://127.0.0.1:8080/readyz
# Loopback request through the same Caddy process that owns public TLS.
curl --fail http://127.0.0.1:5173/readyzUpgrade without mixing worker protocols
- Read /var/lib/ffdb/installed-version, select an exact newer tag from GitHub Releases, and review its migration and compatibility notes.
- Back up PostgreSQL, the metrics ledger, and project data; verify a recent encrypted project backup before changing binaries.
- Download and verify the target native archive exactly as above. Never rebuild or mix individual binaries on an installed host.
- Stop ffdb-api and ffdb-sync-worker, enter the target archive's ffdb-native-VERSION directory, and run sudo ./install-native.sh --start so all three binaries, units, backup helper, and web assets move together while /etc/ffdb and /var/lib/ffdb remain in place.
- Run readiness, RLS isolation, auth refresh, storage signing, sync, and restore smoke tests before discarding the previous verified archive.
Install with systemd workflow
- 1. Verify the release artifacts and install the packaged binaries without rebuilding them.
- 2. Install the supplied sysusers, tmpfiles, environment template, and service units.
- 3. Publish the packaged web assets and review the supplied single-process Caddy gateway.
- 4. Start the API and maintenance worker, then let Caddy obtain the public certificate.
- 5. Verify loopback and public TLS routes, logs, file permissions, and systemd hardening.
Verify install with systemd
The API, maintenance worker, and gateway run as the ffdb account, only approved state directories are writable, and Caddy serves both public HTTPS and loopback acceptance traffic for a ready API.
Troubleshoot install with systemd
- API and database-worker versions differ — stop and reinstall the coordinated release.
- The service cannot write state — compare ownership and ReadWritePaths with the packaged declarations.
- SIGINT drain exceeds the timeout — inspect active work before increasing a measured limit.
Continue from Install with systemd
- Run the production acceptance checks.
- Document the coordinated binary and web-asset upgrade procedure.