Production deployment¶
This is the checklist for running Reqmango on a server that other people can reach. It assumes Docker Compose, but the settings apply to a bare binary as well. For the local demo, the README quickstart is enough — this page is about not getting owned.
Related: security and operations, security policy, configuration.
0. Pre-flight checklist¶
-
SEED_DEMO=false(no published demo accounts) -
SECRET_KEYis a fresh 64-character random value, stored outside git -
DB_PASSWORDis notreqmango -
ADMIN_PASSWORDis set, or the generated one was captured from the log - PostgreSQL and the backend are not published to the internet
- TLS terminates in front of the app
- Backups are scheduled and a restore has been tested once
1. Secrets¶
SECRET_KEY signs access tokens. If it is missing, the backend generates a
random key per process and prints a warning; every restart then invalidates all
sessions. Generate it once and keep it:
Store it in .env (git-ignored), a Docker secret, or your orchestrator's secret
store. Rotating it logs everybody out, which is the correct behaviour after a
suspected leak.
Also change DB_PASSWORD. The Compose default (reqmango) is only acceptable
on a loopback-only database.
2. Turn the demo data off¶
SEED_DEMO=true creates sample workspaces plus the accounts advertised in the
README (admin@reqmango.com, demo@example.com, password demo1234). Those
passwords are in the public repository, so an instance with this flag on is an
instance anyone can administer.
With the demo off, the first boot on an empty database creates a single superuser:
| Setting | Effect |
|---|---|
ADMIN_EMAIL (default admin@reqmango.com) |
Login address for the bootstrap admin |
ADMIN_PASSWORD set |
That exact password is used |
ADMIN_PASSWORD empty |
A random 24-character password is generated and printed once to the backend log |
The bootstrap only runs when no superuser exists, so restarts never recreate or
modify it. It also refuses to promote an already-registered account: if a user
with ADMIN_EMAIL already exists and no superuser does, the server logs an
instruction to pick another address instead of silently granting admin to
whoever signed up first.
Capture the generated password:
Change it after the first sign-in (account settings → password).
Already deployed an older version? An instance that has been running with the old defaults still contains
admin@reqmango.com/demo1234andqa_tester@reqmango.com/Test@12345. ChangingSEED_DEMOdoes not delete existing rows: sign in and change those passwords, or delete the accounts if they are not needed.
3. Network exposure¶
The stock docker-compose.yml publishes PostgreSQL (5432) and the API
(8000) on the host. That is convenient for development and wrong for a public
server. Either bind them to loopback:
…or drop the backend port mapping entirely and let the frontend container (or your proxy) reach it over the Compose network. Only the HTTP(S) entry point should be reachable from outside.
TLS¶
Terminate TLS in a reverse proxy. Caddy gets a certificate automatically:
With nginx, forward the usual headers and disable buffering for the event
stream (/api/v1/sse/*), otherwise notifications arrive in batches:
location / {
proxy_pass http://frontend:80;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
location /api/v1/sse/ {
proxy_pass http://frontend:80;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_read_timeout 3600s;
}
Set a generous read timeout: SSE connections are long-lived.
Open registration¶
POST /api/v1/auth/register is public. If the instance is not meant to accept
sign-ups from the internet, block that path at the proxy or keep the whole app
behind your SSO/VPN.
4. Backups¶
Everything lives in PostgreSQL. Take a logical dump on a schedule:
Restore into a fresh database and start the backend against it:
Keep dumps off the host (object storage or another machine), and restore one into a scratch database periodically — an untested backup is a guess.
5. Upgrades¶
- Back up first (see above).
- Pick a release tag — pin it, don't float on
latestin production:
Building from a checkout (git pull && docker compose up -d --build) is
equivalent, just slower.
3. Confirm the running build, then watch the logs:
On boot the server applies AutoMigrate and the idempotent SQL baseline. If you
prefer an explicit migration step (cmd/migrate), see the README — the two
paths converge on the same schema, and cmd/migrate up is safe to re-run.
Read CHANGELOG.md (or the GitHub release notes) before a minor bump for
configuration changes.
6. Health, metrics, logs¶
| Endpoint | Use |
|---|---|
GET /health |
liveness; container healthcheck |
GET /readyz |
readiness; load balancer / rollout gate (pings the database) |
GET /version |
build identity — version, commit, build date, Go version |
GET /api/v1/metrics |
in-process request metrics; superuser only |
DB_LOG_LEVEL=warn keeps SQL parameters out of the logs. Requests slower than
SLOW_REQUEST_MS (default 1000) are logged as [SLOW] with the route template,
not the concrete URL.
7. Hardening checklist (optional but cheap)¶
-
RATE_LIMIT_REQUESTStuned to your traffic (default 500 / 60 s per IP) - Security headers at the proxy (
Strict-Transport-Security,X-Content-Type-Options,Referrer-Policy, a CSP you have tested) - Restrict outbound network access if you do not use AI providers, webhooks or MCP runtimes
- Rotate
SECRET_KEY,DB_PASSWORDand AI provider keys on a schedule - Watch for releases: security fixes land on
masterand in release notes