Skip to content

Environment Variables Reference

Complete reference for all environment variables used in the Portugal Odyssey platform.

Overview

Environment variables are organized by: - Shared variables - Used across all environments - Environment-specific - Dev, Qual, or Prod specific - Service-specific - Variables for individual services

Template Files

Environment templates are located in infrastructure/env-templates/:

  • env.shared.template - Shared variables
  • .env.development - Development environment
  • env.qualification.template - Qualification environment
  • env.production.template - Production environment

Shared Variables

Infrastructure

Variable Description Example
POSTGRES_USER PostgreSQL admin user postgres
POSTGRES_PASSWORD PostgreSQL admin password secure-password
POSTGRES_DB Default database name portugal_odyssey
REDIS_PASSWORD Redis password redis-password
RABBITMQ_USER RabbitMQ admin user admin
RABBITMQ_PASSWORD RabbitMQ admin password rabbitmq-password

Registry & Deployment

Variable Description Example
REGISTRY_IMAGE Container registry prefix registry.gitlab.com/portugalodissey/po-platform
REGISTRY_USER Registry username gitlab-username
REGISTRY_PASSWORD Registry password/token glpat-xxxxx
DEPLOY_QUAL_HOST Qualification VPS hostname qual.portugalodyssey.pt
DEPLOY_PROD_HOST Production VPS hostname prod.portugalodyssey.pt
DEPLOY_USER SSH user for deployment root

Environment-Specific Variables

Development (ENV=dev)

Variable Description Default
POSTGRES_USER_DEV Dev PostgreSQL user postgres
POSTGRES_PASSWORD_DEV Dev PostgreSQL password devpassword
RABBITMQ_USER_DEV Dev RabbitMQ user admin
RABBITMQ_PASSWORD_DEV Dev RabbitMQ password devpassword

Qualification (ENV=qual)

Variable Description Example
POSTGRES_USER_QUAL Qual PostgreSQL user postgres_qual
POSTGRES_PASSWORD_QUAL Qual PostgreSQL password secure-password
RABBITMQ_USER_QUAL Qual RabbitMQ user admin
RABBITMQ_PASSWORD_QUAL Qual RabbitMQ password secure-password
JWT_SECRET_QUAL JWT secret for qual qual-jwt-secret

Production (ENV=prod)

Variable Description Example
POSTGRES_USER Prod PostgreSQL user postgres
POSTGRES_PASSWORD Prod PostgreSQL password very-secure-password
RABBITMQ_USER Prod RabbitMQ user admin
RABBITMQ_PASSWORD Prod RabbitMQ password very-secure-password
JWT_SECRET JWT secret for prod prod-jwt-secret

Service-Specific Variables

API Gateway

Variable Description Example
PORT Service port 3000
NODE_ENV Node environment production

Auth Service

Variable Description Example
KEYCLOAK_URL Keycloak server URL https://keycloak.portugalodyssey.pt
KEYCLOAK_REALM Keycloak realm portugal-odyssey
KEYCLOAK_CLIENT_ID Keycloak client ID auth-service
KEYCLOAK_CLIENT_SECRET Keycloak client secret client-secret
JWT_SECRET JWT signing secret jwt-secret-key
REDIS_URL Redis connection URL redis://redis:6379

Notification Service

Variable Description Example
DATABASE_URL PostgreSQL connection string postgresql://user:pass@postgres:5432/notification
RABBITMQ_URL RabbitMQ connection URL amqp://user:pass@rabbitmq:5672/vhost
SMTP_HOST SMTP server hostname smtp.example.com
SMTP_PORT SMTP server port 587
SMTP_USER SMTP username smtp-user
SMTP_PASSWORD SMTP password smtp-password
EMAIL_FROM Default sender email noreply@portugalodyssey.pt
CONTACT_FROM_EMAIL Contact-form sender-confirmation from noreply@portugalodyssey.pt
CONTACT_ADMIN_EMAIL Contact-form admin-notification recipient contact@portugalodyssey.pt

Mail addresses must use the apex portugalodyssey.pt (Riff #235, 2026-06-02). The qual. / mail. subdomains have no MX — mail to them blackholes, and they aren't Resend-verified for sending (from on qual. caused a 550 bounce, #30). This applies to the *_QUAL / *_PROD variants too. Only the apex receives (→ mailcow) and sends (Resend).

Payment Service

Variable Description Example
STRIPE_SECRET_KEY Stripe secret key sk_live_xxxxx
STRIPE_PUBLISHABLE_KEY Stripe publishable key pk_live_xxxxx
STRIPE_WEBHOOK_SECRET Stripe webhook secret whsec_xxxxx

File Service

The platform uses self-hosted MinIO across all envs (W2 B-2, Riff #36, 2026-04-30); the AWS S3 path was retired since prod adopted MinIO with its own per-env volume. The aws-sdk S3 client still drives the wire — it just talks to MinIO at http://minio-{qual,prod}:9000.

Variable Description Example
FILE_PROVIDER Storage provider minio (other values s3, gcs, local are still in the code path but unused in deployed envs)
S3_ENDPOINT MinIO endpoint (internal, per-env) http://minio-qual:9000, http://minio-prod:9000
S3_ACCESS_KEY MinIO root user ${MINIO_ROOT_USER_QUAL} / ${MINIO_ROOT_USER_PROD}
S3_SECRET_KEY MinIO root password ${MINIO_ROOT_PASSWORD_QUAL} / ${MINIO_ROOT_PASSWORD_PROD}
S3_REGION Required by aws-sdk; not enforced by MinIO us-east-1
S3_BUCKET Bucket name (per-env) po-files-qual, po-files-prod
S3_PUBLIC_URL Public-facing URL prefix for media references https://files.qual.portugalodyssey.pt/po-files-qual, https://files.portugalodyssey.pt/po-files-prod

Mail (Mailcow self-hosted; live since 2026-05-12)

Mailcow runs under compose project po-mail on the qual VPS, isolated from the rest of the platform. Variables live in /opt/mailcow-dockerized/mailcow.conf (Mailcow's native config file) AND /opt/po-platform/.env.qual (for the Resend smarthost key shared with notification-service + Keycloak). See ADR-009 and the mailcow operations runbook.

Variable Location Description Example
MAILCOW_HOSTNAME mailcow.conf MTA hostname; published as MX target mx.portugalodyssey.pt
ADDITIONAL_SERVER_NAMES mailcow.conf Extra hostnames nginx-mailcow accepts (admin/webmail/autoconfig/autodiscover) mail-admin.portugalodyssey.pt,webmail.portugalodyssey.pt,autoconfig.portugalodyssey.pt,autodiscover.portugalodyssey.pt
HTTP_PORT / HTTPS_PORT mailcow.conf nginx-mailcow exposed ports (Traefik connects on 9443) 9080 / 9443
SKIP_LETS_ENCRYPT mailcow.conf Disable Mailcow's bundled ACME (Traefik owns public certs) y
MAILCOW_ADMIN_PASSWORD_QUAL .env.qual (VPS) Mailcow admin UI password (mail-admin.portugalodyssey.pt) (secret, rotated quarterly)
RESEND_API_KEY_QUAL .env.qual (VPS) Resend API key — used by Mailcow smarthost + notification-service + Keycloak re_xxxxxxxx
Postfix relayhost inside Mailcow Resend smarthost endpoint [smtp.resend.com]:587
Postfix smtp_sasl_password_maps inside Mailcow SASL plain auth using apikey + RESEND_API_KEY_QUAL (operator config in Mailcow admin → Configuration → Routing)

Note: raw SMTP/IMAP ports (25/465/587/993/4190) bind to the host directly and bypass Traefik. The Resend smarthost key is the only mail-related secret that needs rotation coordination across three consumers (Mailcow + notification-service + Keycloak) — see the runbook's "Rotate the Resend smarthost API key" section.

Setting Up Environment Variables

1. Copy Templates

cp infrastructure/env-templates/.env.development .env.dev
cp infrastructure/env-templates/env.qualification.template .env.qual
cp infrastructure/env-templates/env.production.template .env.prod

2. Edit Configuration

nano .env.dev  # Edit development variables

3. Generate Secrets

make secrets  # Generates secure random secrets

4. Verify Configuration

# Check variables are set
docker compose config

Security Best Practices

  1. Never commit .env files - They're in .gitignore
  2. Use strong passwords - Generate secure random passwords
  3. Rotate secrets regularly - Especially in production
  4. Use different secrets per environment - Never reuse production secrets
  5. Limit access - Only grant access to those who need it

See Also