Skip to main content
FootyCollect uses environment variables for all configuration. This guide covers all production environment variables based on deploy/env.example.

Environment File Locations

Depending on your deployment method, environment variables are stored in different locations:
Split into two files:
  • .envs/.production/.django - Django application settings
  • .envs/.production/.postgres - PostgreSQL database settings
Both files are loaded by docker-compose.production.yml.
Use deploy/env.example as your template. It contains all available variables with descriptions.

Required Variables

These variables must be set for production deployment:

Django Core Settings

# Secret key for cryptographic signing (REQUIRED)
DJANGO_SECRET_KEY=your-secret-key-here

# Debug mode - MUST be False in production
DJANGO_DEBUG=False

# Allowed hosts - comma-separated list of domains
DJANGO_ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com

# Admin URL path (change from default 'admin/')
DJANGO_ADMIN_URL=admin/
Generate a secure SECRET_KEY:
python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'
Never use default values or commit SECRET_KEY to version control.

Database Configuration

# PostgreSQL connection string
DATABASE_URL=postgresql://footycollect:your-db-password@localhost:5432/footycollect_db

# For Docker, use service name:
# DATABASE_URL=postgresql://footycollect:your-db-password@postgres:5432/footycollect_db

# Connection pooling (seconds to keep connections alive)
CONN_MAX_AGE=60
If using Docker, also create .envs/.production/.postgres:
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_DB=footycollect_db
POSTGRES_USER=footycollect
POSTGRES_PASSWORD=your-secure-db-password

# DATABASE_URL must also be in .django file
DATABASE_URL=postgresql://footycollect:your-secure-db-password@postgres:5432/footycollect_db

Redis Configuration

# Redis connection string for cache and Celery
REDIS_URL=redis://localhost:6379/0

# For Docker, use service name:
# REDIS_URL=redis://redis:6379/0

Security Settings

Configure security headers and HTTPS enforcement:

SSL/TLS Settings

# Redirect all HTTP to HTTPS
DJANGO_SECURE_SSL_REDIRECT=True

# HTTP Strict Transport Security (HSTS)
DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS=True
DJANGO_SECURE_HSTS_PRELOAD=True

# Prevent MIME type sniffing
DJANGO_SECURE_CONTENT_TYPE_NOSNIFF=True

# Cookie security
DJANGO_SESSION_COOKIE_SAMESITE=Lax
DJANGO_CSRF_COOKIE_SAMESITE=Lax

# Referrer policy
DJANGO_REFERRER_POLICY=strict-origin-when-cross-origin

# Permissions policy
DJANGO_PERMISSIONS_POLICY=geolocation=(), microphone=(), camera=(), payment=()
These settings are validated by Django’s deployment checks in config/checks.py:334.

Content Security Policy (CSP)

# Enable CSP
DJANGO_CSP_ENABLED=True

# Image sources (include your CDN/S3 bucket)
DJANGO_CSP_IMG_SRC='self', data:, blob:, https://www.gravatar.com, https://cdn.footballkitarchive.com, https://your-bucket.s3.amazonaws.com

# Optional: Override default CSP directives
# DJANGO_CSP_DEFAULT_SRC='self'
# DJANGO_CSP_SCRIPT_SRC='self', 'unsafe-inline', 'unsafe-eval', https://cdnjs.cloudflare.com
# DJANGO_CSP_STYLE_SRC='self', 'unsafe-inline', https://cdnjs.cloudflare.com, https://fonts.googleapis.com
# DJANGO_CSP_FONT_SRC='self', https://cdnjs.cloudflare.com, https://fonts.gstatic.com
# DJANGO_CSP_CONNECT_SRC='self'
# DJANGO_CSP_FRAME_ANCESTORS='self'
# DJANGO_CSP_FORM_ACTION='self'
Update DJANGO_CSP_IMG_SRC to include your S3/R2 bucket URL and any external image sources (e.g., Football Kit Archive CDN).

API Rate Limiting

# DRF throttling rates for /api/ endpoints
DJANGO_DRF_USER_THROTTLE_RATE=100/hour
DJANGO_DRF_ANON_THROTTLE_RATE=20/hour

Email Configuration

FootyCollect uses SendGrid for email delivery:
# SendGrid API key
SENDGRID_API_KEY=your-sendgrid-api-key

# SendGrid API URL (default)
SENDGRID_API_URL=https://api.sendgrid.com/v3/

# From email addresses
DJANGO_DEFAULT_FROM_EMAIL=FootyCollect <[email protected]>
DJANGO_SERVER_EMAIL=FootyCollect <[email protected]m>

# Email subject prefix
DJANGO_EMAIL_SUBJECT_PREFIX=[FootyCollect]
  1. Sign up at SendGrid
  2. Navigate to Settings > API Keys
  3. Create a new API key with “Mail Send” permissions
  4. Copy the key (shown only once)
  5. Add to SENDGRID_API_KEY
  1. In SendGrid, go to Settings > Sender Authentication
  2. Verify your domain (yourdomain.com)
  3. Add DNS records as instructed
  4. Use verified domain in DJANGO_DEFAULT_FROM_EMAIL

Storage Configuration

Configure S3-compatible storage for static and media files:

Storage Backend Selection

# Choose storage backend: 'aws' or 'r2'
STORAGE_BACKEND=aws

AWS S3 Storage

STORAGE_BACKEND=aws

# AWS credentials
DJANGO_AWS_ACCESS_KEY_ID=your-aws-access-key-id
DJANGO_AWS_SECRET_ACCESS_KEY=your-aws-secret-access-key

# S3 bucket configuration
DJANGO_AWS_STORAGE_BUCKET_NAME=your-bucket-name
DJANGO_AWS_S3_REGION_NAME=us-east-1

# Optional: Custom domain (CloudFront CDN)
DJANGO_AWS_S3_CUSTOM_DOMAIN=cdn.yourdomain.com
  1. Log into AWS Console
  2. Navigate to S3 > Create bucket
  3. Choose a unique bucket name
  4. Select region (e.g., us-east-1)
  5. Uncheck “Block all public access” (static files need public read)
  6. Create bucket
  1. Navigate to IAM > Users > Add user
  2. Enable “Programmatic access”
  3. Attach policy: AmazonS3FullAccess (or create custom policy)
  4. Save Access Key ID and Secret Access Key
  5. Add to environment variables
Add public read policy for static files:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::your-bucket-name/static/*"
    }
  ]
}

Cloudflare R2 Storage

STORAGE_BACKEND=r2

# Cloudflare R2 credentials
CLOUDFLARE_ACCESS_KEY_ID=your-cloudflare-access-key-id
CLOUDFLARE_SECRET_ACCESS_KEY=your-cloudflare-secret-access-key

# R2 bucket configuration
CLOUDFLARE_BUCKET_NAME=your-r2-bucket-name
CLOUDFLARE_R2_ENDPOINT_URL=https://<account-id>.r2.cloudflarestorage.com
CLOUDFLARE_R2_REGION=auto

# Optional: Custom domain
CLOUDFLARE_R2_CUSTOM_DOMAIN=cdn.yourdomain.com
  1. Log into Cloudflare Dashboard
  2. Navigate to R2 > Create bucket
  3. Choose a bucket name
  4. Create bucket
  1. In R2, go to Manage R2 API Tokens
  2. Create API token
  3. Set permissions: Read and Write
  4. Save Access Key ID and Secret Access Key
  5. Note the endpoint URL (contains your account ID)
If serving fonts or static assets from a custom domain, configure CORS:
# Using Wrangler CLI
npx wrangler r2 bucket cors set your-bucket-name --file deploy/r2-cors-wrangler.json
Or manually add in Cloudflare Dashboard > R2 > bucket > Settings > CORS Policy.
  1. In R2 bucket settings, click “Connect Custom Domain”
  2. Enter your subdomain (e.g., cdn.yourdomain.com)
  3. Add CNAME record to your DNS:
    • Type: CNAME
    • Name: cdn
    • Target: (provided by Cloudflare)
  4. Set CLOUDFLARE_R2_CUSTOM_DOMAIN=cdn.yourdomain.com
Why Cloudflare R2? R2 offers S3-compatible API with free egress (no bandwidth charges), significantly reducing costs compared to AWS S3.

Error Tracking (Sentry)

Configure Sentry for error monitoring and performance tracking:
# Sentry DSN (Data Source Name)
SENTRY_DSN=your-sentry-dsn

# Environment identifier
SENTRY_ENVIRONMENT=production

# Performance monitoring sample rate (0.0 to 1.0)
SENTRY_TRACES_SAMPLE_RATE=0.0
  1. Sign up at Sentry.io
  2. Create a new project (Django)
  3. Copy the DSN from project settings
  4. Add to SENTRY_DSN
SENTRY_TRACES_SAMPLE_RATE controls performance monitoring:
  • 0.0 - Disabled (no performance tracking)
  • 0.1 - 10% of requests tracked
  • 1.0 - 100% of requests tracked (high volume)
Start with 0.0 or 0.1 to avoid quota limits.
Sentry is highly recommended for production. It provides:
  • Real-time error alerts
  • Stack traces and context
  • Performance monitoring
  • Release tracking

External Integrations

Football Kit Archive API (FKAPI)

# FKAPI server IP or hostname
FKA_API_IP=your-fkapi-server-ip

# API authentication key
API_KEY=your-fkapi-key

# Allowed image download hosts (SSRF protection)
DJANGO_ALLOWED_EXTERNAL_IMAGE_HOSTS=cdn.footballkitarchive.com,www.footballkitarchive.com
FKAPI is optional but provides Football Kit Archive integration for searching and adding kits. See FKAPI repository for setup.

Rotating Proxy (Optional)

# Rotating proxy for image downloads (avoid rate limiting)
ROTATING_PROXY_URL=http://proxy.example.com:8080
ROTATING_PROXY_USERNAME=your-proxy-username
ROTATING_PROXY_PASSWORD=your-proxy-password
Use a rotating proxy if:
  • Downloading many images from external sources
  • Getting rate-limited by image hosts
  • Need to distribute requests across multiple IPs
Supports HTTP, HTTPS, and SOCKS5 proxies.

Performance Settings

Compression

# Enable compression for responses
COMPRESS_ENABLED=True

Connection Pooling

# Persistent database connections (seconds)
CONN_MAX_AGE=60
CONN_MAX_AGE=60 keeps database connections alive for 60 seconds, reducing connection overhead. Set to 0 to disable pooling.

Complete Environment File Example

Here’s a complete production environment file (deploy/env.example:1):
# ============================================
# Django Core Settings
# ============================================
DJANGO_SECRET_KEY=your-secret-key-here-generate-with-django-admin-utils
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
DJANGO_ADMIN_URL=admin/

# ============================================
# Database
# ============================================
DATABASE_URL=postgresql://footycollect:your-db-password@localhost:5432/footycollect_db
CONN_MAX_AGE=60

# ============================================
# Redis
# ============================================
REDIS_URL=redis://localhost:6379/0

# ============================================
# Security Settings
# ============================================
DJANGO_SECURE_SSL_REDIRECT=True
DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS=True
DJANGO_SECURE_HSTS_PRELOAD=True
DJANGO_SECURE_CONTENT_TYPE_NOSNIFF=True
DJANGO_SESSION_COOKIE_SAMESITE=Lax
DJANGO_CSRF_COOKIE_SAMESITE=Lax
DJANGO_REFERRER_POLICY=strict-origin-when-cross-origin
DJANGO_PERMISSIONS_POLICY=geolocation=(), microphone=(), camera=(), payment=()

# ============================================
# Content Security Policy
# ============================================
DJANGO_CSP_ENABLED=True
DJANGO_CSP_IMG_SRC='self', data:, blob:, https://www.gravatar.com, https://cdn.footballkitarchive.com, https://your-bucket.s3.amazonaws.com

# ============================================
# API Rate Limiting
# ============================================
DJANGO_DRF_USER_THROTTLE_RATE=100/hour
DJANGO_DRF_ANON_THROTTLE_RATE=20/hour

# ============================================
# Email (SendGrid)
# ============================================
SENDGRID_API_KEY=your-sendgrid-api-key
SENDGRID_API_URL=https://api.sendgrid.com/v3/
DJANGO_DEFAULT_FROM_EMAIL=FootyCollect <[email protected]>
DJANGO_SERVER_EMAIL=FootyCollect <[email protected]m>
DJANGO_EMAIL_SUBJECT_PREFIX=[FootyCollect]

# ============================================
# Error Tracking (Sentry)
# ============================================
SENTRY_DSN=your-sentry-dsn
SENTRY_ENVIRONMENT=production
SENTRY_TRACES_SAMPLE_RATE=0.0

# ============================================
# Storage Backend
# ============================================
STORAGE_BACKEND=aws

# AWS S3 (if STORAGE_BACKEND=aws)
DJANGO_AWS_ACCESS_KEY_ID=your-aws-access-key-id
DJANGO_AWS_SECRET_ACCESS_KEY=your-aws-secret-access-key
DJANGO_AWS_STORAGE_BUCKET_NAME=your-bucket-name
DJANGO_AWS_S3_REGION_NAME=us-east-1
DJANGO_AWS_S3_CUSTOM_DOMAIN=

# Cloudflare R2 (if STORAGE_BACKEND=r2)
# CLOUDFLARE_ACCESS_KEY_ID=your-cloudflare-access-key-id
# CLOUDFLARE_SECRET_ACCESS_KEY=your-cloudflare-secret-access-key
# CLOUDFLARE_BUCKET_NAME=your-r2-bucket-name
# CLOUDFLARE_R2_ENDPOINT_URL=https://<account-id>.r2.cloudflarestorage.com
# CLOUDFLARE_R2_REGION=auto
# CLOUDFLARE_R2_CUSTOM_DOMAIN=

# ============================================
# External Integrations
# ============================================
# FKAPI (Football Kit Archive)
FKA_API_IP=your-fkapi-server-ip
API_KEY=your-fkapi-key
DJANGO_ALLOWED_EXTERNAL_IMAGE_HOSTS=cdn.footballkitarchive.com,www.footballkitarchive.com

# Rotating Proxy (optional)
ROTATING_PROXY_URL=
ROTATING_PROXY_USERNAME=
ROTATING_PROXY_PASSWORD=

# ============================================
# Performance
# ============================================
COMPRESS_ENABLED=True

Environment File Security

Protect Your Environment FilesEnvironment files contain sensitive credentials. Follow these security practices:

File Permissions

# Bare metal - restrict .env access
chmod 600 /var/www/footycollect/.env
chown footycollect:footycollect /var/www/footycollect/.env

# Docker - restrict environment directory
chmod 700 .envs/.production
chmod 600 .envs/.production/.django
chmod 600 .envs/.production/.postgres

Version Control

# NEVER commit environment files
echo ".envs/" >> .gitignore
echo ".env" >> .gitignore

# Verify not tracked
git status

Credential Rotation

  • Rotate DJANGO_SECRET_KEY periodically (requires user re-login)
  • Rotate database passwords quarterly
  • Rotate API keys when team members leave
  • Use unique credentials per environment (dev/staging/prod)

Secrets Management

For enhanced security, consider using:
  • AWS Secrets Manager - Store credentials in AWS
  • HashiCorp Vault - Centralized secrets management
  • Environment-specific encryption - Encrypt .env files at rest

Validation

Verify your environment configuration:

Django Deployment Checks

# Run all deployment checks
python manage.py check --deploy
This validates (config/checks.py:1):
  • ✓ DEBUG is disabled
  • ✓ SECRET_KEY is secure (length, uniqueness)
  • ✓ Required environment variables are set
  • ✓ Database connectivity
  • ✓ Redis connectivity
  • ✓ Storage credentials (S3/R2)
  • ✓ ALLOWED_HOSTS configured
  • ✓ SSL/HTTPS settings

Manual Verification

# Check required variables are set
grep -E '^(DJANGO_SECRET_KEY|DATABASE_URL|REDIS_URL)' .env

# Verify no default values
grep -i "change" .env
grep -i "your-" .env

# Test database connection
python manage.py dbshell

# Test Redis connection
redis-cli ping

Environment Variables Reference

Quick Reference Table

VariableRequiredDefaultDescription
DJANGO_SECRET_KEYYes-Cryptographic signing key
DJANGO_DEBUGYesFalseDebug mode (must be False)
DJANGO_ALLOWED_HOSTSYes-Allowed domain names
DATABASE_URLYes-PostgreSQL connection string
REDIS_URLYes-Redis connection string
SENDGRID_API_KEYRecommended-Email delivery API key
SENTRY_DSNRecommended-Error tracking DSN
STORAGE_BACKENDYesawsStorage backend (aws/r2)
DJANGO_AWS_*If using S3-AWS S3 credentials
CLOUDFLARE_*If using R2-Cloudflare R2 credentials
FKA_API_IPOptional-FKAPI server address
See deploy/env.example:1 for the complete list with descriptions.

Next Steps

Docker Deployment

Deploy with Docker Compose using environment files

Bare Metal Deployment

Deploy on VPS with environment file

Production Checklist

Verify configuration with deployment checklist

Build docs developers (and LLMs) love