Environment Variables#

Complete reference for all environment variables used in production deployments.

Overview#

Construbot uses environment variables for configuration following the Twelve-Factor App methodology. Variables are loaded from:

  1. .env file (development/Docker)

  2. System environment (production servers)

  3. Container environment (Docker/Kubernetes)

Note

This document focuses on production-specific variables. For development configuration, see Configuration.

Required Variables#

Django Core#

DJANGO_SETTINGS_MODULE#

Type: String

Required: Yes

Description: Python path to settings module

Production value:

DJANGO_SETTINGS_MODULE=construbot.config.settings.production

Values:

  • construbot.config.settings.local - Development

  • construbot.config.settings.test - Testing

  • construbot.config.settings.production - Production

DJANGO_SECRET_KEY#

Type: String (50+ characters)

Required: Yes

Description: Cryptographic signing key for sessions, cookies, CSRF tokens

Danger

Never commit SECRET_KEY to version control! Use different keys for dev/prod.

Generate:

python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"

Example:

DJANGO_SECRET_KEY=django-insecure-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0

DJANGO_DEBUG#

Type: Boolean

Required: Yes

Description: Enable/disable debug mode

Danger

MUST be False in production! Debug mode exposes sensitive information.

Production value:

DJANGO_DEBUG=False

Effects when True:

  • Detailed error pages with tracebacks

  • Exposes settings and environment variables

  • Shows SQL queries

  • Serves static files via Django (slow)

  • Disables template caching

DJANGO_ALLOWED_HOSTS#

Type: Comma-separated string

Required: Yes (when DEBUG=False)

Description: Hosts/domains that Django serves

Example:

DJANGO_ALLOWED_HOSTS=example.com,www.example.com,api.example.com

Include:

  • Root domain

  • www subdomain

  • API subdomain (if separate)

  • Load balancer hostname

Validation:

# Test in shell
from django.conf import settings
print(settings.ALLOWED_HOSTS)
# ['example.com', 'www.example.com', 'api.example.com']

Database#

DATABASE_URL#

Type: PostgreSQL connection string

Required: Yes

Description: Complete database connection URL

Format:

postgresql://[user]:[password]@[host]:[port]/[database][?options]

Examples:

# Self-hosted
DATABASE_URL=postgresql://construbot:password@postgres:5432/construbot

# AWS RDS (with SSL)
DATABASE_URL=postgresql://construbot:pwd@db.abc123.us-east-1.rds.amazonaws.com:5432/construbot?sslmode=require

# With connection pooling options
DATABASE_URL=postgresql://user:pwd@host:5432/db?sslmode=require&connect_timeout=10&options=-c%20statement_timeout=300000

SSL modes:

  • disable - No SSL (not recommended for production)

  • require - SSL required (recommended)

  • verify-ca - Verify certificate authority

  • verify-full - Full certificate verification

Cache and Queue#

REDIS_URL#

Type: Redis connection string

Required: Yes

Description: Redis for caching and Celery broker

Format:

redis://[:password]@[host]:[port]/[database]

Examples:

# Self-hosted (no password)
REDIS_URL=redis://redis:6379/0

# With password
REDIS_URL=redis://:mypassword@redis:6379/0

# AWS ElastiCache
REDIS_URL=redis://construbot.abc123.cache.amazonaws.com:6379/0

Database numbers:

  • /0 - Cache and sessions

  • /1 - Celery broker (optional separation)

  • /2 - Celery results (optional separation)

Security Settings#

SSL/HTTPS#

DJANGO_SECURE_SSL_REDIRECT#

Type: Boolean

Default: False

Description: Redirect all HTTP requests to HTTPS

Production value:

DJANGO_SECURE_SSL_REDIRECT=True

Requires:

  • Valid SSL certificate

  • HTTPS configured on web server/load balancer

DJANGO_SECURE_PROXY_SSL_HEADER#

Type: Tuple (header name, header value)

Default: None

Description: Trust X-Forwarded-Proto header from proxy/load balancer

Production value:

DJANGO_SECURE_PROXY_SSL_HEADER=HTTP_X_FORWARDED_PROTO,https

When to use:

  • Behind nginx reverse proxy

  • Behind AWS ALB/ELB

  • Behind CloudFlare

HSTS (HTTP Strict Transport Security)#

DJANGO_SECURE_HSTS_SECONDS#

Type: Integer (seconds)

Default: 0 (disabled)

Description: Browser should only access via HTTPS for this many seconds

Production value:

DJANGO_SECURE_HSTS_SECONDS=31536000  # 1 year

Warning

Test carefully before enabling! Once set, browsers refuse HTTP connections.

DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS#

Type: Boolean

Default: False

Description: Apply HSTS to all subdomains

Production value:

DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS=True

Requires: All subdomains support HTTPS

DJANGO_SECURE_HSTS_PRELOAD#

Type: Boolean

Default: False

Description: Allow adding to browser HSTS preload lists

Production value:

DJANGO_SECURE_HSTS_PRELOAD=True

After enabling:

Submit domain to https://hstspreload.org/

Content Security#

DJANGO_SECURE_CONTENT_TYPE_NOSNIFF#

Type: Boolean

Default: False

Description: Prevent MIME type sniffing

Production value:

DJANGO_SECURE_CONTENT_TYPE_NOSNIFF=True

DJANGO_SECURE_BROWSER_XSS_FILTER#

Type: Boolean

Default: False

Description: Enable browser XSS filter

Production value:

DJANGO_SECURE_BROWSER_XSS_FILTER=True

X_FRAME_OPTIONS#

Type: String

Default: DENY

Description: Control iframe embedding

Values:

  • DENY - Cannot be embedded (recommended)

  • SAMEORIGIN - Can embed on same domain

  • ALLOW-FROM https://example.com - Allow specific domain

Production value:

X_FRAME_OPTIONS=DENY

Email Configuration#

Type: String (Python import path)

Required: Yes

Description: Email backend to use

Production values:

# Mailgun (recommended)
DJANGO_EMAIL_BACKEND=anymail.backends.mailgun.EmailBackend

# SendGrid
DJANGO_EMAIL_BACKEND=anymail.backends.sendgrid.EmailBackend

# Amazon SES
DJANGO_EMAIL_BACKEND=anymail.backends.amazon_ses.EmailBackend

# SMTP (generic)
DJANGO_EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend

Type: String

Required: If using Mailgun

Description: Mailgun API key

Example:

MAILGUN_API_KEY=key-1234567890abcdef1234567890abcdef

Get from: https://app.mailgun.com/app/account/security/api_keys

Type: String

Required: If using Mailgun

Description: Verified domain for sending

Example:

MAILGUN_SENDER_DOMAIN=mg.example.com

Type: Email address

Required: Yes

Description: Default sender for emails

Example:

DEFAULT_FROM_EMAIL=noreply@example.com

Type: Email address

Default: root@localhost

Description: Sender for error emails

Example:

SERVER_EMAIL=errors@example.com

Static and Media Files#

Type: Boolean

Default: False

Description: Use AWS S3 for media file storage

Production value:

USE_S3=True

Type: String

Required: If USE_S3=True

Description: AWS access key with S3 permissions

Example:

AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE

Type: String

Required: If USE_S3=True

Description: AWS secret key

Example:

AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Type: String

Required: If USE_S3=True

Description: S3 bucket name for media files

Example:

AWS_STORAGE_BUCKET_NAME=construbot-media-example-com

Type: String

Default: us-east-1

Description: AWS region for S3 bucket

Example:

AWS_S3_REGION_NAME=us-west-2

Type: String

Default: None (optional)

Description: CloudFront or custom domain for media files

Example:

AWS_S3_CUSTOM_DOMAIN=d111111abcdef8.cloudfront.net

Monitoring and Logging#

Type: String (URL)

Default: None (optional)

Description: Sentry error tracking DSN

Example:

SENTRY_DSN=https://abcdef1234567890@o123456.ingest.sentry.io/7654321

Get from: Sentry project settings

Type: String

Default: production

Description: Environment name in Sentry

Examples:

SENTRY_ENVIRONMENT=production
SENTRY_ENVIRONMENT=staging

Type: Float (0.0 to 1.0)

Default: 1.0

Description: Percentage of errors to send to Sentry

Example:

SENTRY_SAMPLE_RATE=1.0  # Send 100% of errors

Type: String

Default: INFO

Description: Minimum log level to record

Values:

  • DEBUG - Detailed information (development only)

  • INFO - General informational messages (production recommended)

  • WARNING - Warning messages

  • ERROR - Error messages only

  • CRITICAL - Critical errors only

Production value:

LOG_LEVEL=INFO

CORS Configuration#

Type: Comma-separated string

Default: Empty

Description: Allowed origins for CORS requests

Example:

CORS_ALLOWED_ORIGINS=https://app.example.com,https://mobile.example.com

When needed:

  • API consumed by separate frontend

  • Mobile app accessing API

  • Different subdomain for frontend

Type: Boolean

Default: False

Description: Allow CORS from any origin

Danger

Never use in production! Security risk.

Development only:

CORS_ALLOW_ALL_ORIGINS=True

Celery Configuration#

Type: Redis connection string

Default: Uses REDIS_URL

Description: Celery message broker

Example:

CELERY_BROKER_URL=redis://redis:6379/1

Usually same as REDIS_URL but can use different database number.

Type: Redis connection string

Default: Uses REDIS_URL

Description: Celery result storage

Example:

CELERY_RESULT_BACKEND=redis://redis:6379/2

Optional Variables#

Library Mode#

CONSTRUBOT_AS_LIBRARY#

Type: Boolean

Default: False

Description: Run Construbot as Django app (not standalone)

Example:

CONSTRUBOT_AS_LIBRARY=True

Effects:

  • Disables admin interface

  • Disables account management URLs

  • Disables standalone authentication

  • Allows embedding in existing Django project

See Library Mode for details.

Database Connection Pooling#

DATABASE_CONN_MAX_AGE#

Type: Integer (seconds)

Default: 0 (new connection per request)

Description: How long to reuse database connections

Production value:

DATABASE_CONN_MAX_AGE=600  # 10 minutes

Benefits:

  • Reduced database connection overhead

  • Better performance under load

  • Lower database resource usage

Session Storage#

SESSION_ENGINE#

Type: String

Default: django.contrib.sessions.backends.db

Description: Where to store sessions

Options:

# Database (default)
SESSION_ENGINE=django.contrib.sessions.backends.db

# Cache (Redis) - faster
SESSION_ENGINE=django.contrib.sessions.backends.cache

# Cached database - best of both
SESSION_ENGINE=django.contrib.sessions.backends.cached_db

Production recommendation:

SESSION_ENGINE=django.contrib.sessions.backends.cached_db

Complete Production Example#

Minimal Production .env#

# Django Core
DJANGO_SETTINGS_MODULE=construbot.config.settings.production
DJANGO_DEBUG=False
DJANGO_SECRET_KEY=<generate-strong-50+-char-key>
DJANGO_ALLOWED_HOSTS=example.com,www.example.com

# Database
DATABASE_URL=postgresql://user:pass@db.example.rds.amazonaws.com:5432/construbot?sslmode=require

# Cache/Queue
REDIS_URL=redis://:password@redis.example.cache.amazonaws.com:6379/0

# Email
DJANGO_EMAIL_BACKEND=anymail.backends.mailgun.EmailBackend
MAILGUN_API_KEY=key-xxxxxxxxxx
MAILGUN_SENDER_DOMAIN=mg.example.com
DEFAULT_FROM_EMAIL=noreply@example.com

# Storage
USE_S3=True
AWS_ACCESS_KEY_ID=AKIAXXXXXX
AWS_SECRET_ACCESS_KEY=xxxxxxxxxx
AWS_STORAGE_BUCKET_NAME=construbot-media-example
AWS_S3_REGION_NAME=us-east-1

# Security
DJANGO_SECURE_SSL_REDIRECT=True
DJANGO_SECURE_HSTS_SECONDS=31536000
DJANGO_SESSION_COOKIE_SECURE=True
DJANGO_CSRF_COOKIE_SECURE=True

# Monitoring
SENTRY_DSN=https://xxx@sentry.io/xxx

Comprehensive Production .env#

# Django Core
DJANGO_SETTINGS_MODULE=construbot.config.settings.production
DJANGO_DEBUG=False
DJANGO_SECRET_KEY=<generate-strong-50+-char-key>
DJANGO_ALLOWED_HOSTS=example.com,www.example.com,api.example.com
DJANGO_READ_DOT_ENV_FILE=True

# Database
DATABASE_URL=postgresql://user:pass@db.example.rds.amazonaws.com:5432/construbot?sslmode=require&connect_timeout=10
DATABASE_CONN_MAX_AGE=600

# Cache/Queue
REDIS_URL=redis://:password@redis.example.cache.amazonaws.com:6379/0
CELERY_BROKER_URL=redis://:password@redis.example.cache.amazonaws.com:6379/1
CELERY_RESULT_BACKEND=redis://:password@redis.example.cache.amazonaws.com:6379/2

# Sessions
SESSION_ENGINE=django.contrib.sessions.backends.cached_db

# Email
DJANGO_EMAIL_BACKEND=anymail.backends.mailgun.EmailBackend
MAILGUN_API_KEY=key-xxxxxxxxxx
MAILGUN_SENDER_DOMAIN=mg.example.com
DEFAULT_FROM_EMAIL=noreply@example.com
SERVER_EMAIL=errors@example.com

# Storage
USE_S3=True
AWS_ACCESS_KEY_ID=AKIAXXXXXX
AWS_SECRET_ACCESS_KEY=xxxxxxxxxx
AWS_STORAGE_BUCKET_NAME=construbot-media-example
AWS_S3_REGION_NAME=us-west-2
AWS_S3_CUSTOM_DOMAIN=d111111abcdef8.cloudfront.net

# Security - SSL
DJANGO_SECURE_SSL_REDIRECT=True
DJANGO_SECURE_PROXY_SSL_HEADER=HTTP_X_FORWARDED_PROTO,https

# Security - HSTS
DJANGO_SECURE_HSTS_SECONDS=31536000
DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS=True
DJANGO_SECURE_HSTS_PRELOAD=True

# Security - Cookies
DJANGO_SESSION_COOKIE_SECURE=True
DJANGO_SESSION_COOKIE_HTTPONLY=True
DJANGO_CSRF_COOKIE_SECURE=True
DJANGO_CSRF_COOKIE_HTTPONLY=True

# Security - Content
DJANGO_SECURE_CONTENT_TYPE_NOSNIFF=True
DJANGO_SECURE_BROWSER_XSS_FILTER=True
X_FRAME_OPTIONS=DENY

# Monitoring
SENTRY_DSN=https://xxx@sentry.io/xxx
SENTRY_ENVIRONMENT=production
SENTRY_SAMPLE_RATE=1.0
LOG_LEVEL=INFO

# CORS (if needed)
CORS_ALLOWED_ORIGINS=https://app.example.com

Validation#

Check Required Variables#

# Run Django checks
docker compose run --rm django python manage.py check --deploy

No warnings should appear.

Test Configuration#

# Django shell
docker compose run --rm django python manage.py shell
from django.conf import settings

# Verify critical settings
assert settings.DEBUG is False, "DEBUG must be False!"
assert settings.SECRET_KEY != 'insecure-key', "Change SECRET_KEY!"
assert len(settings.ALLOWED_HOSTS) > 0, "Set ALLOWED_HOSTS!"

print(f"Database: {settings.DATABASES['default']['NAME']}")
print(f"Redis: {settings.CACHES['default']['LOCATION']}")
print(f"Email: {settings.EMAIL_BACKEND}")
print(f"Static: {settings.STATIC_URL}")
print(f"Media: {settings.MEDIA_URL}")

See Also#