Skip to content

Environment Variable Matrix

Every environment variable used across the app-name stack — frontend, backend, build, and deploy — in one consolidated reference. Use it to answer "what is this variable, who reads it, and where does it live?" at a glance.

Two environments only

This template targets test and production. Each runtime variable is defined per-environment, but the source of truth (server .env, GitHub secret, or CI-generated) is identical across both. See Secrets & Environment for the full strategy.

How to read the "Where it lives" column

Value Meaning
Local .env Set by each developer in the repo-root .env for local dev only. Never committed.
Server .env Lives in /opt/app-name/.env (test) or /opt/app-name-prod/.env (production). Hand-managed on the server; never stored in GitHub.
GitHub Secret Encrypted secret in Settings → Secrets and variables → Actions. Injected at deploy time only.
GitHub Variable Plain (non-secret) repository/environment variable (vars.*).
CI-generated Produced inside the workflow run (e.g. from github.sha) and written to a transient .env.deploy file on the server.

Database

Variable Used by Where it lives Description Example / placeholder
DATABASE_URL backend Local .env, Server .env Primary connection string. Points at the transaction pooler (port 6543) for standard app queries. postgresql://postgres.<ref>:<pw>@<host>:6543/postgres
DATABASE_URL_DIRECT backend (migrations) Local .env, Server .env Direct connection (port 5432) used for migrations and heavy batch jobs that the pooler cannot serve. postgresql://postgres.<ref>:<pw>@<host>:5432/postgres
DB_SSL_MODE backend Local .env, Server .env Postgres SSL enforcement mode. Use require against hosted/managed Postgres. require

Pooler vs direct

Use DATABASE_URL (6543) for the app and DATABASE_URL_DIRECT (5432) for migrations. Migrations run over the pooler can fail or hang — see Troubleshooting.


Supabase / Auth

Variable Used by Where it lives Description Example / placeholder
VITE_SUPABASE_URL frontend, build Local .env, Server .env Supabase project URL. Publicly visible in the frontend bundle. Injected into config.js at container startup. https://<ref>.supabase.co
VITE_SUPABASE_ANON_KEY frontend, build Local .env, Server .env Anonymous (anon) key. Publicly visible; obeys Row Level Security. eyJhbGciOiJI...
SUPABASE_SERVICE_KEY backend Local .env, Server .env service_role key. Bypasses RLS — critical secret. Never prefix with VITE_. eyJhbGciOiJI...

Never prefix the service key with VITE_

Any variable prefixed VITE_ is bundled into the frontend and shipped to every browser. Prefixing SUPABASE_SERVICE_KEY with VITE_ would leak a key that bypasses all RLS. See Supabase (Hosted).


Email

Variable Used by Where it lives Description Example / placeholder
SENDGRID_API_KEY backend Local .env, Server .env SendGrid API key for transactional email. SG.xxxxxxxx
SENDGRID_FROM_EMAIL backend Local .env, Server .env Verified sender address used as the From: on outgoing mail. noreply@<your-domain>

App / Session

Variable Used by Where it lives Description Example / placeholder
NODE_ENV backend, build Local .env, Server .env Runtime mode. development locally; production on servers. production
PORT backend Local .env, Server .env Port the backend API listens on. 5001
SESSION_SECRET backend Local .env, Server .env Secret used to sign/encrypt session tokens. Min 32 chars. Generate with openssl rand -base64 32. <32+ random chars>
VITE_API_URL frontend, build Local .env, Server .env Public HTTPS URL of the backend API. https://<your-domain>
VITE_APP_URL frontend, build Local .env, Server .env Self-address used to build links in emails. Must match the public site domain. https://<your-domain>

Test

Variable Used by Where it lives Description Example / placeholder
TEST_USER_EMAIL backend (smoke/E2E) Local .env Test user that smoke/E2E tests authenticate as, directly against Supabase. test-user@example.com
TEST_USER_PASSWORD backend (smoke/E2E) Local .env Password for the test user. Tests still pass without it, but auth tests are skipped. <test password>

Tests authenticate directly with Supabase

Because the test user logs in against Supabase, VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY must point at the project where TEST_USER_EMAIL exists.


CI / Deploy

These variables exist only inside the GitHub Actions runner and the transient .env.deploy file that the deploy step writes to the server. They are not part of the persistent server .env.

Variable Used by Where it lives Description Example / placeholder
REGISTRY build, deploy Workflow env: Container registry host. ghcr.io
REGISTRY_OWNER deploy CI-generated (.env.deploy) Owner namespace used to pull images. Maps to <org>. <org>
IMAGE_TAG build, deploy CI-generated Image tag to deploy. Test uses github.sha; production uses the git tag (github.ref_name, e.g. v1.4.0). a1b2c3d / v1.4.0
ENV deploy CI-generated (.env.deploy) Target environment passed to docker compose. test / production
GITHUB_TOKEN build, deploy GitHub Secret (auto) Auto-provided token. Logs in to GHCR to push (build) and pull (server) images. Injected into .env.deploy. (auto-generated per run)
GITHUB_ACTOR deploy CI-generated (.env.deploy) GitHub username performing the run; used as the GHCR docker login username. <username>
BUILD_TIME build CI-generated (step output) UTC build timestamp stamped into the image. 2026-06-30T12:00:00Z
IMAGE_BACKEND build Workflow env: Backend image repository path. ghcr.io/<org>/app-name-backend
IMAGE_FRONTEND build Workflow env: Frontend image repository path. ghcr.io/<org>/app-name-frontend
DEPLOY_PATH deploy Workflow env: Absolute path on the server where the stack lives. /opt/app-name (test) · /opt/app-name-prod (prod)
SONAR_TOKEN build (gate) GitHub Secret Token for the SonarCloud quality-gate scan. <token>
SONAR_HOST_URL build (gate) Workflow env: SonarCloud host. https://sonarcloud.io

CI/deploy variables never replace the server .env

The deploy step refuses to run if /opt/app-name/.env is missing. Runtime secrets (DATABASE_URL, SUPABASE_SERVICE_KEY, SESSION_SECRET, …) must already be present on the server — CI only layers IMAGE_TAG, ENV, REGISTRY_OWNER, GITHUB_TOKEN, and GITHUB_ACTOR on top via .env.deploy.