Production env vars

Secrets, JWT, database — the must-set list.

5 mineasy

Last lesson — the pre-launch env vars checklist. Miss one of these and prod either won't boot or will boot in an insecure state. Five minutes to read, save hours of debugging.

The must-set list

VarRequiredIf missing / wrong
APP_ENVMust be productionGin in debug mode; verbose error responses; secrets leaked
DATABASE_URLYesBoot fails at config load
JWT_SECRET≥32 charsBoot rejected; existing tokens become invalid
SENTINEL_PASSWORDRandomBoot rejected with default password
SENTINEL_SECRET_KEY≥32 charsSame as above
PULSE_PASSWORDRandomBoot rejected with default
CORS_ORIGINSYour real domainsFrontend can't call API; CORS errors
APP_URLhttps://your-domainOAuth redirects break; absolute URLs in emails are wrong

The should-set list

VarWhy
RESEND_API_KEYReal email sends; without it, mail is silently dropped
STORAGE_DRIVER + credsFile uploads work and survive container restart
AI_GATEWAY_API_KEYAI features stop working if you removed dev defaults
OAUTH_FRONTEND_URLOAuth post-login redirect points to your real frontend
GORM_STUDIO_ENABLED=falseDon't expose the DB browser in production

Generating strong values

Terminal
# JWT_SECRET + SENTINEL_SECRET_KEY — 32 bytes hex
$openssl rand -hex 32
# SENTINEL_PASSWORD + PULSE_PASSWORD — 16 bytes hex (short but strong)
$openssl rand -hex 16
# Strong DB password — 24+ chars, no shell-special characters
$openssl rand -base64 24 | tr -d '/+='

v3.25.1+ of Grit auto-generates these at scaffold time. If you scaffolded older, regenerate before going to prod.

OAuth — production callback URLs

Each OAuth provider needs your production callback URL added to its allow list:

  • Google: https://api.acme.com/api/auth/google/callback
  • GitHub: https://api.acme.com/api/auth/github/callback

Both providers let you list multiple URLs — keep your localhost dev URLs too so dev still works.

Don't reuse JWT_SECRET across environments. If staging's secret leaks, your prod tokens are exposed. Different random values per environment. Same for SENTINEL_SECRET_KEY.

Database backups

Not strictly an env var, but: set up daily backups before going to prod. Five-minute setup, prevents catastrophic data loss. Options:

  • pg_dump + cron — simplest. Dumps DB nightly, ships to S3 / R2.
  • Backrest / pgBackRest — production-grade PITR (point-in-time recovery).
  • Managed Postgres (Neon, Supabase) — backups are automatic. Skip the self-host.

The pre-flight checklist

Before grit deploy --domain ... in production:

  • .env.production exists and has every required var
  • ☐ All passwords are random (not change-me)
  • APP_ENV=production
  • CORS_ORIGINS only lists real frontend domains
  • GORM_STUDIO_ENABLED=false
  • ☐ OAuth callback URLs are added to provider allow lists
  • ☐ DNS A record points to VPS IP
  • ☐ DB backup plan exists
  • ☐ You can SSH to the VPS

Quick check

You ran `grit deploy` to production. The Sentinel dashboard is reachable at /sentinel/ui with credentials `admin/sentinel`. What's the immediate priority?

Try it

Build the pre-flight checklist for your bench-api's production env. In notes.md:

  1. List every env var your .env.production sets
  2. For each, write "random", "copied from dev", or "from a service dashboard"
  3. Confirm no value is change-me, your-super-secret-jwt-key, or similar template text

That's the chapter 6 assignment partially done — the second half is the actual deploy + HTTPS verification.

You finished Building a Go API 🎉

Six chapters, 19 lessons. You can now scaffold a Go API, model the data layer, add auth with JWT + OAuth + 2FA + RBAC, integrate background jobs / mail / storage / AI, run a real WAF + observability + audit log, and deploy to a VPS with HTTPS.

Next: pick the frontend that fits your product — Mobile (Expo), Web (Next.js), Web (TanStack), Desktop (Wails), or Multi-platform. All assume the API you just built.

Spot a typo? Have an idea?

Help us improve this lesson. One click opens a GitHub issue with the lesson URL pre-filled — suggest clearer wording, report a bug, or request more depth. The course keeps improving thanks to learners like you.

Suggest an improvement on GitHub