Olympus Docs
DeployDatabase

Database, Self-hosted

Running Postgres on the same VPS as the Olympus apps

Self-hosting Postgres alongside Olympus is straightforward, compose.prod.yml ships a Postgres container, and you can opt into it via a Daedalus wizard toggle.

Recommendation: prefer managed Postgres (Neon) unless one of the specific reasons below applies. The operational savings outweigh the cost.

Reasons to self-host

  • Air-gapped deployment, no outbound from the VPS to a SaaS Postgres.
  • Data residency, you need the database physically in the same jurisdiction as the app, with no possibility of provider routing through other regions.
  • You're already operating Postgres at scale and have ops capacity to spare.
  • Cost at large scale, once your database CPU consistently exceeds a few hundred dollars/month on managed, self-hosting starts to win.

What you take on

  • Backups. Nightly pg_dump, off-host storage, restore drills.
  • TLS certs. Generate, distribute, rotate (see Operate, Cert Rotation).
  • Major version upgrades. Postgres 17 → 18 → 19 every 12-18 months.
  • Monitoring. Disk fill alerts, replication lag (if you go HA), connection pool saturation.
  • Tuning. shared_buffers, effective_cache_size, max_connections, Olympus's defaults work but might not be optimal for your workload.

What Daedalus configures

When you select self-hosted in the Database step:

  1. The postgres service stays enabled in compose.prod.yml.
  2. A volume is created for /var/lib/postgresql/data.
  3. init-db.sql runs on first start, creating the five Olympus databases and their per-database users.
  4. The Postgres container generates a self-signed TLS cert via the Daedalus Secrets step; the apps trust the cert via the local CA bundle.

Sizing the host

For 1k MAU with self-hosted Postgres on the same VPS:

  • VPS: at least 4GB RAM (2GB Postgres, 2GB Olympus apps)
  • Disk: 20GB minimum; Olympus's data growth is modest (~10MB per 1000 identities)

For 10k MAU:

  • VPS: 8GB RAM
  • Disk: 50GB

Above 10k MAU, consider splitting Postgres onto a separate host or moving to managed.

Backup strategy

Minimum viable:

# /etc/cron.daily/olympus-pgbackup
podman exec olympus-postgres pg_dumpall -U postgres \
  | gzip > /backups/$(date +%Y%m%d).sql.gz

# Off-host: rclone to S3, B2, or rsync

See Operate, Backups (Postgres) for the full procedure including restore drill cadence.

TLS configuration

Production Olympus requires sslmode=verify-full. With self-hosted Postgres, you must:

  1. Generate a CA, server cert, and client certs (Daedalus does this).
  2. Configure postgresql.conf:
    ssl = on
    ssl_cert_file = '/etc/postgresql/server.crt'
    ssl_key_file = '/etc/postgresql/server.key'
    ssl_ca_file = '/etc/postgresql/ca.crt'
  3. Configure pg_hba.conf to require hostssl for app connections.
  4. Distribute the CA cert to every app container so verify-full can validate the chain.

See Deploy, Database SSL verify-full for the full setup.

Migrations

Olympus migrations run automatically, Kratos and Hydra each have their own migrate containers that run before the main service starts. For PostgreSQL major version upgrades, see Operate, Logs and observability for the recommended pg_upgrade procedure.

Where next

On this page