Docker Compose (Production)
This guide walks you through deploying ArchVault in production using Docker Compose. The pre-built image is published to GitHub Container Registry at ghcr.io/rubentalstra/archvault.
Prerequisites
Section titled “Prerequisites”- Docker Engine 24+ and Docker Compose v2
- A server with at least 1 GB RAM and 10 GB disk
- A domain name (recommended) with a reverse proxy for TLS
Quick Start
Section titled “Quick Start”- Create a directory for your deployment:
mkdir archvault && cd archvault- Download the production Compose file and environment template:
curl -O https://raw.githubusercontent.com/rubentalstra/Archvault/main/compose.prod.yamlcurl -o .env https://raw.githubusercontent.com/rubentalstra/Archvault/main/.env.production.example- Edit
.envand fill in the required values:
# Generate a secret keyopenssl rand -base64 32Set at minimum:
BETTER_AUTH_SECRET— paste the generated secretBETTER_AUTH_URL— your public URL (e.g.https://archvault.example.com)POSTGRES_PASSWORD— a strong database password
- Start the stack:
docker compose -f compose.prod.yaml up -d- Verify the deployment:
curl http://localhost:3000/api/healthYou should see {"status":"healthy","database":"connected",...}.
Architecture
Section titled “Architecture”The production Compose stack consists of two services:
| Service | Image | Purpose |
|---|---|---|
app | ghcr.io/rubentalstra/archvault:latest | ArchVault application server |
db | postgres:18-alpine | PostgreSQL database |
Key characteristics:
- The database is not exposed to the host — only accessible within the
archvaultbridge network - Data is persisted in a named Docker volume (
pgdata) - The app waits for the database health check before starting
- Database migrations run automatically on startup (configurable via
AUTO_MIGRATE)
Auto-Migration
Section titled “Auto-Migration”By default, the container runs pending database migrations on every start. This ensures your database schema stays in sync when upgrading to a new image version.
To disable auto-migration (e.g. if you prefer to run migrations manually):
environment: AUTO_MIGRATE: "false"Run migrations manually:
docker compose -f compose.prod.yaml exec app node migrate.mjsReverse Proxy
Section titled “Reverse Proxy”The app listens on port 3000. In production, you should place a reverse proxy in front for TLS termination — never expose port 3000 directly to the internet.
Quick setup with Traefik
Section titled “Quick setup with Traefik”The Compose file includes a built-in Traefik profile for automatic HTTPS — no config files needed:
# Set your domain and Let's Encrypt email in .envDOMAIN=archvault.example.comACME_EMAIL=admin@example.com
# Start with the traefik profiledocker compose -f compose.prod.yaml --profile traefik up -dTraefik automatically obtains and renews TLS certificates via Let’s Encrypt, redirects HTTP to HTTPS, and adds security headers.
See the Reverse Proxy guide for full configuration examples with Traefik, Nginx, and Caddy.
Pinning a Version
Section titled “Pinning a Version”Instead of latest, pin to a specific version or commit SHA for reproducible deployments:
services: app: image: ghcr.io/rubentalstra/archvault:1.0.0Available tag formats:
latest— latest build frommain1.0.0— exact semver release1.0— latest patch in minor1— latest minor in majorabc1234— specific commit SHA
Backup & Restore
Section titled “Backup & Restore”Backup
Section titled “Backup”docker compose -f compose.prod.yaml exec db \ pg_dump -U archvault archvault > backup-$(date +%Y%m%d).sqlRestore
Section titled “Restore”docker compose -f compose.prod.yaml exec -T db \ psql -U archvault archvault < backup-20260317.sqlSecurity Notes
Section titled “Security Notes”- The application runs as a non-root user (UID 1001) inside the container
- The database has no exposed ports — it is only accessible via the internal Docker network
- Always use a strong, unique
POSTGRES_PASSWORD - Always use a strong, unique
BETTER_AUTH_SECRET - Use TLS termination via a reverse proxy — never expose port 3000 directly to the internet