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.
Related references¶
- Secrets & Environment — the single-source-of-truth strategy and where each secret is defined.
- Secrets Matrix — GitHub Secrets vs Variables vs server-side
.env. - Pipeline Overview — how these variables flow through build and deploy.
- Supabase (Hosted) — anon vs service key, RLS, and connection strings.