Production env vars
Secrets, JWT, database — the must-set list.
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
| Var | Required | If missing / wrong |
|---|---|---|
| APP_ENV | Must be production | Gin in debug mode; verbose error responses; secrets leaked |
| DATABASE_URL | Yes | Boot fails at config load |
| JWT_SECRET | ≥32 chars | Boot rejected; existing tokens become invalid |
| SENTINEL_PASSWORD | Random | Boot rejected with default password |
| SENTINEL_SECRET_KEY | ≥32 chars | Same as above |
| PULSE_PASSWORD | Random | Boot rejected with default |
| CORS_ORIGINS | Your real domains | Frontend can't call API; CORS errors |
| APP_URL | https://your-domain | OAuth redirects break; absolute URLs in emails are wrong |
The should-set list
| Var | Why |
|---|---|
| RESEND_API_KEY | Real email sends; without it, mail is silently dropped |
| STORAGE_DRIVER + creds | File uploads work and survive container restart |
| AI_GATEWAY_API_KEY | AI features stop working if you removed dev defaults |
| OAUTH_FRONTEND_URL | OAuth post-login redirect points to your real frontend |
| GORM_STUDIO_ENABLED=false | Don't expose the DB browser in production |
Generating strong values
# 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.
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.productionexists and has every required var - ☐ All passwords are random (not
change-me) - ☐
APP_ENV=production - ☐
CORS_ORIGINSonly 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
Try it
Build the pre-flight checklist for your bench-api's production env. In notes.md:
- List every env var your
.env.productionsets - For each, write "random", "copied from dev", or "from a service dashboard"
- 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