Skip to content

Configuration

Environment files, secrets, GitHub OAuth, optional Elasticsearch and Coolify deployment settings.

NextJudge splits configuration across the backend (Docker Compose) and the web app (Next.js). This page covers both.

FileUsed byPurpose
.env.example (repo root)ReferenceTemplate for backend secrets and security flags
.env (repo root)./deploy.shProd-like local stack; no seed by default
.env.dev (repo root)./dev-deploy.shDev stack: hot reload, SEED_DATA=true
src/web/.env.localnpm run dev / npm start on hostAuth.js, API URL, bridge secret

Generate backend secrets once:

Terminal window
./.createenv.sh > .env

For development, copy the same output to .env.dev:

Terminal window
cp .env .env.dev

Add dev-only keys to .env.dev as needed (see below). .env.dev is gitignored — create it locally; do not commit secrets.

Copy WEB_BRIDGE_SECRET from root env into src/web/.env.local whenever the web app runs outside Docker.


Required — data layer fails at startup without these:

VariablePurpose
JWT_SIGNING_SECRETSigns user and judge JWTs
JUDGE_PASSWORDJudge login (POST /v1/login_judge)
WEB_BRIDGE_SECRETWeb → API OAuth bridge
DB_PASSWORDPostgres
RABBITMQ_USER, RABBITMQ_PASSWORDMessage queue

Optional backend keys:

VariableDefaultPurpose
ADMIN_EMAILSComma-separated; matching emails get admin on register
CORS_ORIGINhttp://localhost:8080Web origin(s), comma-separated; wildcards are rejected
CORS_ALLOW_PREVIEWfalseAllow {id}-web.preview.nextjudge.net origins
TRUSTED_PROXYfalseTrust X-Forwarded-For for auth rate limits (set true behind Traefik/nginx in production)
PASSWORD_RESET_DEBUGfalseDev/E2E only: include reset token in basic_request_password_reset response — never enable in production
ALLOW_INSECURE_PASSWORD_RESETfalseDev only: allow basic_reset_password without a token — never enable in production
SEED_DATAfalsetrue in dev compose — sample users/problems/events
ELASTIC_ENABLEDfalseProblem search index (optional)
ELASTIC_ENDPOINThttp://localhost:9200Elasticsearch URL when enabled

Full lists: src/data-layer/src/config.go, judge src/judge/src/config.py.


Copy src/web/.env.examplesrc/web/.env.local:

VariableRequiredNotes
AUTH_SECRETYesAuth.js session encryption
WEB_BRIDGE_SECRETYesMust match data layer
AUTH_GITHUB_IDFor GitHub loginOAuth app client ID
AUTH_GITHUB_SECRETFor GitHub loginOAuth app client secret
AUTH_REDIRECT_PROXY_URLPR previewsProduction OAuth callback host, e.g. https://nextjudge.net/api/auth
AUTH_TRUST_HOSTPR previewstrue when behind Coolify Traefik
NEXT_PUBLIC_API_URLSelf-hosted prodSet at next build. Omit on Coolify preview web (runtime routing)
NEXTAUTH_URLSelf-hosted prodPublic HTTPS URL of the web app
RESEND_API_KEYOptionalEmail (waitlist, etc.)

Deprecated fallback on web: AUTH_PROVIDER_PASSWORD — use WEB_BRIDGE_SECRET.


Settings → Developer settings → OAuth Apps → New OAuth App

FieldLocal devProduction
Homepage URLhttp://localhost:8080https://yourdomain.com
Authorization callback URLhttp://localhost:8080/api/auth/callback/githubhttps://yourdomain.com/api/auth/callback/github

Web (src/web/.env.local):

AUTH_GITHUB_ID=...
AUTH_GITHUB_SECRET=...
AUTH_SECRET=...
WEB_BRIDGE_SECRET=... # same as data layer
NEXTAUTH_URL=http://localhost:8080

Data layer (.env or .env.dev):

WEB_BRIDGE_SECRET=... # same value

After GitHub login, Auth.js calls POST /v1/create_or_login_user with Authorization: WEB_BRIDGE_SECRET. Details: Authentication.

SymptomFix
GitHub works but no platform sessionWEB_BRIDGE_SECRET mismatch
Redirect loop after loginNEXTAUTH_URL ≠ URL in the browser
CORS errorsAdd web origin to CORS_ORIGIN on data layer

Each PR gets an isolated stack:

ServiceURL pattern
Webhttps://{PR_NUMBER}-web.preview.nextjudge.net
Docshttps://{PR_NUMBER}-docs.preview.nextjudge.net
APIhttps://{PR_NUMBER}-api.preview.nextjudge.net

Web and docs use Coolify application preview deployments (enable in Application → Advanced).

Backend is a Coolify compose service — Coolify does not provision per-PR compose previews via its deploy API. CI runs scripts/coolify-preview-backend-ssh.sh over SSH: an isolated docker compose project per PR, Traefik on the host coolify network, hostname {PR}-api.preview.nextjudge.net.

Backend preview env (preview-scoped on the compose service): SEED_DATA=true, PASSWORD_RESET_DEBUG=true, relaxed AUTH_RATE_LIMIT_*, isolated secrets. WEB_BRIDGE_SECRET must match preview web.

Web preview env: do not set NEXT_PUBLIC_API_URL{PR}-web hostnames route API calls to {PR}-api at runtime.

GitHub OAuth: one production callback (https://nextjudge.net/api/auth/callback/github). Set AUTH_REDIRECT_PROXY_URL=https://nextjudge.net/api/auth and AUTH_TRUST_HOST=true on production and preview web. Share AUTH_SECRET with production; use preview-only GitHub credentials and WEB_BRIDGE_SECRET.

Setup scripts (require coolify.env + ssh nextjudge):

Terminal window
./scripts/coolify-configure-preview-stack.sh # prod + preview env on Coolify
./scripts/setup-coolify-preview-webhooks.sh # sync GitHub webhook secrets (web/docs)

Manual deploy / cleanup:

Terminal window
PR_NUMBER=123 COOLIFY_SSH_HOST=nextjudge ./scripts/coolify-preview-backend-ssh.sh deploy
PR_NUMBER=123 COOLIFY_SSH_HOST=nextjudge ./scripts/coolify-preview-backend-ssh.sh cleanup

Seeded preview login: Alice.Smith0@example.com / test123 (when SEED_DATA=true).


Elasticsearch is off by default. Core features (problems, contests, judging) work without it.

When ELASTIC_ENABLED=true:

  • Data layer connects to ELASTIC_ENDPOINT at startup — failure to connect exits the process.
  • Indices nextjudge-problems and nextjudge-competitions are created if missing.
  • GET /v1/problems?query= uses search when enabled.
  1. Uncomment the elasticsearch service in compose/docker-compose.dev.yml (or backend compose).
  2. Uncomment depends_on: elasticsearch on the data layer.
  3. Set ELASTIC_ENABLED=true in .env.dev.
  4. Start the stack.

./dev-deploy.sh currently forces ELASTIC_ENABLED=false on the command line even when the elastic profile is enabled — override in compose or run compose manually if you need ES in dev.

Standalone definition: src/data-layer/docker-compose.elasticsearch.yml (ES 8.12, port 9200).


Use compose/docker-compose.coolify.yml for the backend stack on a Coolify host.

DB_PASSWORD
WEB_BRIDGE_SECRET
JUDGE_PASSWORD
JWT_SIGNING_SECRET
RABBITMQ_USER
RABBITMQ_PASSWORD
CORS_ORIGIN=https://yourdomain.com
CORS_ALLOW_PREVIEW=true
TRUSTED_PROXY=true
PASSWORD_RESET_DEBUG=false
ALLOW_INSECURE_PASSWORD_RESET=false
ADMIN_EMAILS=admin@example.com

Set these on the Coolify service (Docker Compose stack), not the web application. The backend service UUID is COOLIFY_BACKEND_SERVICE_UUID in GitHub Actions.

Images: ${DOCKERHUB_NAMESPACE}/nextjudge-core:latest, ${DOCKERHUB_NAMESPACE}/nextjudge-judge:latest. Build fresh with DOCKERHUB_NAMESPACE=your-namespace docker buildx bake -f docker-bake.hcl.

The Coolify compose file does not include the web app or Elasticsearch. Deploy src/web as a separate Coolify application.

Set at build time (production):

NEXT_PUBLIC_API_URL=https://api.yourdomain.com
AUTH_SECRET=...
AUTH_GITHUB_ID=...
AUTH_GITHUB_SECRET=...
WEB_BRIDGE_SECRET=...
NEXTAUTH_URL=https://yourdomain.com
AUTH_REDIRECT_PROXY_URL=https://yourdomain.com/api/auth
AUTH_TRUST_HOST=true

Preview web: same AUTH_REDIRECT_PROXY_URL, AUTH_TRUST_HOST, and AUTH_SECRET; preview GitHub creds and WEB_BRIDGE_SECRET; omit NEXT_PUBLIC_API_URL. See PR previews.

More production detail: Deployment guide.


SecretData layerJudgeWeb
JWT_SIGNING_SECRET
JUDGE_PASSWORD
WEB_BRIDGE_SECRET
DB_PASSWORD
RABBITMQ_*
TRUSTED_PROXY
PASSWORD_RESET_DEBUG
ALLOW_INSECURE_PASSWORD_RESET
AUTH_SECRET
AUTH_GITHUB_*
NEXTAUTH_URL
NEXT_PUBLIC_API_URL✓ (build)