Skip to content

Production Deployment

Amanbig edited this page Jul 20, 2026 · 2 revisions

Production Deployment

The README Quick Start gets you a working instance fast. This page covers what to change before it's actually exposed to real users.

TLS / reverse proxy

ManUp's container speaks plain HTTP on PORT (7780 by default) — it does not terminate TLS itself. Put it behind a reverse proxy (Caddy, Traefik, nginx, or your cloud provider's load balancer) that handles HTTPS, and point the proxy at the container's port.

Once you're behind a real domain, set ALLOWED_ORIGINS to that domain — otherwise CORS falls back to localhost origins only and the dashboard won't be able to call the API from its real URL.

Secrets and backups

  • MASTER_KEY is the single point of failure. It encrypts every secret value at rest. If it's lost, stored secrets are unrecoverable — there is no recovery path. Store it in a password manager or your infra's own secrets store, separately from the ManUp deployment itself.
  • If using the embedded PGLITE database, the manup_data Docker volume (mounted at /app/manup) is your database. Back it up like you would any production database — volume snapshots or a scheduled docker cp/tarball, not just "the volume exists so it's fine."
  • If using an external Postgres (DATABASE_URL), back it up with your normal Postgres backup strategy (pg_dump, managed-DB snapshots, etc.) — ManUp doesn't manage this for you.
  • Set real JWT_SECRET and REFRESH_TOKEN_SECRET values — see Environment Variables for what's actually enforced at startup versus what you need to check yourself.

Networking

  • Restrict inbound access to the ManUp port to your reverse proxy only (security group / firewall rule) — don't expose 7780 directly to the internet.
  • If you're running project/environment-scoped API keys for CI, consider putting them in your CI provider's own secret store rather than plaintext in pipeline YAML.

Scaling notes

  • API key rate limiting is enforced per server process, in memory (see API Keys). If you run multiple replicas behind a load balancer, each replica enforces its own limit independently — a key's effective global rate limit is roughly rateLimit × replica count, not a shared cap.
  • PGLITE is an embedded, single-process database — it's not meant for multi-replica deployments. If you need to scale the server horizontally, switch to an external Postgres via DATABASE_URL first.

Pre-launch checklist

  • MASTER_KEY, JWT_SECRET, REFRESH_TOKEN_SECRET all set to distinct, random, non-default values
  • MASTER_KEY backed up somewhere outside the deployment
  • TLS terminated in front of the container (reverse proxy)
  • ALLOWED_ORIGINS set to your real domain
  • Database backups scheduled (volume snapshot for PGlite, or pg_dump/managed snapshots for external Postgres)
  • Inbound access to the app port restricted to the reverse proxy

Clone this wiki locally