-
Notifications
You must be signed in to change notification settings - Fork 0
Production Deployment
Amanbig edited this page Jul 21, 2026
·
2 revisions
The README Quick Start gets you a working instance fast. This page covers what to change before it's actually exposed to real users.
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.
-
MASTER_KEYis 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
PGLITEdatabase, themanup_dataDocker volume (mounted at/app/manup) is your database. Back it up like you would any production database — volume snapshots or a scheduleddocker 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_SECRETandREFRESH_TOKEN_SECRETvalues — see Environment Variables for what's actually enforced at startup versus what you need to check yourself.
- Restrict inbound access to the ManUp port to your reverse proxy only (security group / firewall rule) — don't expose
7780directly 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.
- 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. - Same caveat applies to the built-in brute-force protection on
/register(5 requests/min) and/login(10 requests/min) — it's IP-based and in-memory per process, so multiple replicas each enforce it independently. -
PGLITEis 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 viaDATABASE_URLfirst.
-
MASTER_KEY,JWT_SECRET,REFRESH_TOKEN_SECRETall set to distinct, random, non-default values -
MASTER_KEYbacked up somewhere outside the deployment - TLS terminated in front of the container (reverse proxy)
-
ALLOWED_ORIGINSset 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