Your own server needs a little care: backups, updates and the occasional bit of troubleshooting. This page assumes the Docker Compose setup. Run all commands in the deploy/compose folder.
Backups
The backup service writes every 24 hours (BACKUP_INTERVAL_HOURS) into the folder BACKUP_DIR:
passbox-<time>.dump.age: the database, encrypted with your age key,blobs-<time>.tar.age: the file attachments, encrypted once more,last-success: the time of the last complete backup. Monitor this file.
The service deletes backups older than BACKUP_KEEP_DAYS (default 14). A backup on demand:
docker compose run --rm backup onceThe backups contain everything, including the server keys in the server_secrets table. Without that table, nobody could sign in anymore, although their data would be intact.
If you run the database without Docker Compose, back it up with pg_dump as the passbox_migrator role or as a superuser. The other roles see only part of the rows because of row-level security.
Restore
Stop Passbox and the backups:
shdocker compose stop passbox backupPick the backup time, for example
20261025T030000Zfrombackups/passbox-20261025T030000Z.dump.age.Restore with the private key. This replaces the database and the attachments:
shBACKUP_AGE_KEY_FILE=/path/to/passbox-backup-key.txt \ docker compose --profile restore run --rm restore 20261025T030000ZStart again.
migrateruns first, so an older backup is brought up to date:shdocker compose up -d
Test a restore on another computer from time to time: check out the same version, copy .env and a backup into deploy/compose, run docker compose up -d postgres and then steps 3 and 4.
Updates
Make a backup before every update. Then:
# put the new version into .env, such as PASSBOX_IMAGE=ghcr.io/passboxnet/server:0.2.0
docker compose pull
docker compose up -ddocker compose up runs the migrations first and then restarts Passbox. Caddy holds requests for up to 15 seconds during the restart.
Migrations cannot be undone. To go back to an older version, restore the backup from before the update.
With several servers behind each other, set PASSBOX_AUTO_MIGRATE=false and run passbox-server migrate once before rolling out. New servers report ready on /ready only once the database has their migrations.
Verify images
Released images are signed with cosign and come with a software bill of materials (SBOM) and build provenance:
cosign verify ghcr.io/passboxnet/server:0.2.0 \
--certificate-identity-regexp '^https://github\.com/passboxnet/server/\.github/workflows/images\.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
docker buildx imagetools inspect ghcr.io/passboxnet/server:0.2.0 --format '{{ json .SBOM }}'You can also build the image yourself: docker compose build in the deploy/compose folder.
Monitoring
GET /healthanswers as long as the process runs.GET /readyanswers 200 with the migration version when the database is ready, otherwise 503.- With
PASSBOX_METRICS_BIND, a separate port serves Prometheus metrics: requests by route and status with response times, database connection usage and the length of the email and webhook queues. The metrics contain no account data. Keep the port internal anyway. - The
last-successfile in the backup folder shows the last successful backup.
Troubleshooting
The certificate is not issued. Check that the DNS records point to the server and that ports 80 and 443 are reachable from outside. You see the messages with docker compose logs caddy.
Passbox does not start. docker compose logs migrate passbox shows why. Often it is a typo in .env, an invalid SMTP URL or an invalid sender. If a required variable is missing, Docker Compose names it at startup.
The database reports wrong passwords. The roles and passwords are created only on the very first start with an empty data folder. If you changed the passwords in .env later, set them in the database too:
docker compose exec postgres psql -U postgres -c "ALTER ROLE passbox_app PASSWORD '…'"You can restore roles and privileges at any time with this command, safely also more than once:
docker compose exec postgres psql -U postgres -d passbox -v ON_ERROR_STOP=1 -f /passbox/roles.sqlThe server warns about row-level security at startup. It then runs with a role that owns the tables, or with a superuser. Use the three separate roles.
Emails do not arrive. Open Administration → Email. It shows waiting and failed emails and the last error. Send test email checks the settings. Without PASSBOX_SMTP_URL, emails only go to the server log.
All sign-ins come from the same IP or are rate limited too early. Behind a reverse proxy, PASSBOX_TRUST_PROXY=true must be set. The Docker Compose setup already does this.
Security keys stop working after a move. Security keys and passkeys for two-factor sign-in are bound to the web app's host name. After changing the host name, users sign in with a recovery code and set up their keys again.
Nobody can sign in after restoring a backup. Check that the server_secrets table was in the backup. Backups from the backup service always contain it.