The supplied stack runs the Node app, its built-in scheduler and a one-shot migrator. It bundles Linux agents. The Docker host needs Docker Compose, plus Node.js 22.12+ and pnpm 10.17+ for source setup.

Install

Run from a source checkout on the Docker host; keep its deploy/.env.production there too:

pnpm install --frozen-lockfile
pnpm configure

Choose Docker Compose (self-hosted). Review deploy/.env.production, set DEVPUSH_ADMIN_EMAIL and configure sign-in.

Provide an HTTPS reverse proxy pointing to host port 7429 (DEVPUSH_HTTP_PORT). Compose does not provide dashboard TLS. Compose publishes the listener on all interfaces; firewall direct access. If the proxy runs on this host, bind the app’s Compose ports entry to 127.0.0.1:7429:3000 instead. Do not enroll this host as an app server if its existing proxy occupies ports 80/443.

docker compose --env-file deploy/.env.production -f deploy/compose.production.yml up -d --build
curl --fail http://localhost:7429/api/health

Use the configured port if different. Diagnose startup with:

docker compose --env-file deploy/.env.production -f deploy/compose.production.yml logs --tail=100 app migrate

Sign in, configure the allowlist, arrange backups and connect a server.

Upgrade

Back up both databases and uploads, review migrations, check out the new release tag and rerun the start command. Keep BETTER_AUTH_SECRET and DEVPUSH_SECRET_ENCRYPTION_KEY unchanged.

All services run as UID/GID 1000. Existing volumes/bind mounts must grant that user access; do not make them world-writable. Run one app instance with SQLite on a local filesystem, never a network share.

The migrator applies the shipped main/log migration histories before startup. Never edit applied migrations or run schema push in production. Retired pre-release histories require a fresh installation; updating code does not reset data.

For a prebuilt image, set DEVPUSH_PRODUCTION_IMAGE to its reviewed tag/digest, run Compose pull, then up -d --no-build using the same file/env flags. To roll back a schema-changing release, stop the app, restore pre-upgrade databases and use the previous image tag/digest.

Backups and recovery

control_plane_data holds both SQLite stores under /app/storage. Set COMPOSE_PROFILES=backups for daily online snapshots, retained seven days by default (DEVPUSH_DB_BACKUP_RETENTION_DAYS). Snapshots live in /backups, mounted from control_plane_backups. Copy them off-host and test restoration.

Never copy only a live .sqlite file: committed data may be in WAL files. Online snapshots are consistent individually; stop the app for a coordinated snapshot of both stores and uploads. Back up app_uploads, configuration and secrets separately.

To restore, first stop all database writers:

docker compose --env-file deploy/.env.production -f deploy/compose.production.yml stop app db-backup

Replace both database files, remove stale WAL/SHM files while all connections are closed, restore uploads and start the image matching those backups.

Remote libSQL uses provider backups; the local backup profile refuses remote configurations. Changing storage settings does not transfer data.

Background work

Production Node runs a minute-based scheduler inside the app. Workers uses native cron; both call the same functions. Notification retries and enabled backup schedules run each minute; maintenance runs hourly, with failed runs retried.

Email delivery logs identify notifications without message content. Retries stop after ten attempts or 24 hours from the first attempt; there is no delivery-management screen.

/api/health checks the main database only. Inspect agent diagnostics and real app routes separately. Use deletion recovery for blocked cleanup; never delete rows to bypass it.

Uninstall

Back up the databases, uploads and secrets first. Stop and remove the dashboard containers using the same configuration:

docker compose --env-file deploy/.env.production -f deploy/compose.production.yml down

This preserves named volumes. Only remove those volumes after verifying the backups and deciding to discard the installation’s data. Application containers on agent servers keep running; remove agents separately when needed.