Environments
| Production | Beta | |
|---|---|---|
| Branch | main |
beta |
| Web | tcgkl.com |
beta.tcgkl.com |
| API | api.tcgkl.com |
api-staging.tcgkl.com |
| Docs | docs.tcgkl.com |
— |
| R2 bucket | tcgkl |
tcgkl-beta |
| Env file | .env |
.env.local / .env.beta |
.env.example is the source of truth for the full list. What follows is only
the parts that cause incidents.
Database URLs
Section titled “Database URLs”DATABASE_URL— the pooled Neon endpoint (-pooler), withsslmode=require&channel_binding=require. postgres.js runsprepare: falsebecause of PgBouncer.DATABASE_URL_DIRECT— the non-pooler endpoint, used for migrations only. The migrate script refuses to run against a-poolerhost.
ECONNREFUSED or CONNECT_TIMEOUT locally is nearly always one of those two
being wrong, or a dev server that needs a restart.
Other gotchas
Section titled “Other gotchas”BETTER_AUTH_TRUSTED_ORIGINSis comma-separated. A domain missing from it cannot sign in.CLOUDFLARE_R2_PUBLIC_URLhas no trailing slash.API_INTERNAL_URLset → the web app proxies/api/trpc,/api/auth, webhooks, cron and uploads toapps/api. Unset → the local thin routes serve. That switch is the rollback path.REDIS_URLexists on the API only. Every web-side Redis consumer needs anullor database fallback.APP_VARIANT≠productionmakes the OTP666666. Production always sends a real one.
Which database is this script about to write to?
Section titled “Which database is this script about to write to?”Scripts run by a bare bun run load .env.local, which points at beta. For
production, pin the env file and read the host the script prints:
bunx env-cmd -f .env -- bun run apps/web/scripts/<script>.ts