Deployment Guide

This guide covers deploying Empire Builder to various platforms, from development to production environments.

Deployment Options

Platform Comparison

Render (Recommended) - ✅ Free tier available - ✅ Automatic deployments from Git - ✅ Built-in SSL certificates - ✅ Environment variable management - ✅ PostgreSQL add-on available

Heroku - ✅ Easy deployment process - ✅ Add-on ecosystem - ❌ No free tier (as of 2022) - ✅ Excellent documentation

Railway - ✅ Modern deployment platform - ✅ Git-based deployments - ✅ Competitive pricing - ✅ Good performance

DigitalOcean App Platform - ✅ Scalable infrastructure - ✅ Multiple deployment options - ✅ Integrated monitoring - ❌ More complex setup

Self-hosted (VPS) - ✅ Full control - ✅ Cost-effective for scale - ❌ Requires system administration - ❌ Manual security updates

Render Deployment

Render is the recommended platform for Empire Builder deployment due to its simplicity and Supabase compatibility.

Prerequisites

  1. Render Account: Sign up at https://render.com

  2. GitHub Repository: Code must be in a Git repository

  3. Supabase Project: Production database ready

Step-by-Step Deployment

  1. Prepare Repository

    Create render.yaml in the empire directory:

    services:
      - type: web
        name: empire-builder
        env: python
        buildCommand: pip install -r requirements.txt
        startCommand: gunicorn -w 4 -b 0.0.0.0:$PORT app_supabase:app
        envVars:
          - key: PYTHON_VERSION
            value: 3.11.0
          - key: SUPABASE_URL
            sync: false
          - key: SUPABASE_ANON_KEY
            sync: false
          - key: SUPABASE_SERVICE_KEY
            sync: false
          - key: SECRET_KEY
            generateValue: true
    
  2. Update Requirements

    Add production dependencies to requirements.txt:

    # Existing dependencies
    Flask==2.2.5
    supabase==1.0.4
    flask-socketio==5.3.4
    python-dotenv==1.0.0
    
    # Production dependencies
    gunicorn==21.2.0
    psycopg2-binary==2.9.7
    
  3. Configure Application

    Update app_supabase.py for production:

    import os
    from flask import Flask
    from dotenv import load_dotenv
    
    # Load environment variables
    load_dotenv(override=True)
    
    app = Flask(__name__)
    
    # Production configuration
    app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
    app.config['DEBUG'] = os.environ.get('DEBUG', 'False').lower() == 'true'
    
    # Get port from environment (Render sets this)
    port = int(os.environ.get('PORT', 5000))
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=port)
    
  4. Create Render Service

    • Go to Render Dashboard

    • Click “New +” → “Web Service”

    • Connect your GitHub repository

    • Select the empire directory (if monorepo)

    • Choose “Python” environment

    • Set build command: pip install -r requirements.txt

    • Set start command: gunicorn -w 4 -b 0.0.0.0:$PORT app_supabase:app

  5. Configure Environment Variables

    In Render dashboard, add environment variables:

    SUPABASE_URL=https://your-project.supabase.co
    SUPABASE_ANON_KEY=your-anon-key
    SUPABASE_SERVICE_KEY=your-service-key
    SECRET_KEY=your-production-secret-key-very-long-and-random
    DEBUG=False
    
  6. Deploy

    • Click “Create Web Service”

    • Render will automatically build and deploy

    • Monitor build logs for any issues

    • Access your app at the provided URL

Production Configuration

Environment Variables:

# Required
SUPABASE_URL=https://your-prod-project.supabase.co
SUPABASE_ANON_KEY=your-production-anon-key
SUPABASE_SERVICE_KEY=your-production-service-key
SECRET_KEY=very-long-random-production-secret-key

# Optional
DEBUG=False
PORT=5000
WORKERS=4

Gunicorn Configuration:

Create gunicorn.conf.py:

import os

# Server socket
bind = f"0.0.0.0:{os.environ.get('PORT', 5000)}"
backlog = 2048

# Worker processes
workers = int(os.environ.get('WORKERS', 4))
worker_class = "gevent"
worker_connections = 1000
timeout = 30
keepalive = 2

# Logging
accesslog = "-"
errorlog = "-"
loglevel = "info"

# Process naming
proc_name = "empire-builder"

# Server mechanics
preload_app = True
daemon = False
pidfile = "/tmp/gunicorn.pid"
user = None
group = None
tmp_upload_dir = None

Heroku Deployment

Alternative deployment to Heroku platform.

Prerequisites

  1. Heroku Account: Sign up at https://heroku.com

  2. Heroku CLI: Install from https://devcenter.heroku.com/articles/heroku-cli

Deployment Steps

  1. Create Heroku App

    # Login to Heroku
    heroku login
    
    # Create app
    heroku create empire-builder-yourname
    
  2. Configure Buildpack

    heroku buildpacks:set heroku/python
    
  3. Create Procfile

    Create Procfile in empire directory:

    web: gunicorn -w 4 -b 0.0.0.0:$PORT app_supabase:app
    worker: python worker.py  # If you have background tasks
    
  4. Set Environment Variables

    heroku config:set SUPABASE_URL=https://your-project.supabase.co
    heroku config:set SUPABASE_ANON_KEY=your-anon-key
    heroku config:set SUPABASE_SERVICE_KEY=your-service-key
    heroku config:set SECRET_KEY=your-production-secret-key-very-long-and-random
    
  5. Deploy

    # Add Heroku remote
    heroku git:remote -a empire-builder-yourname
    
    # Deploy
    git push heroku main
    

Docker Deployment

Containerized deployment for any platform supporting Docker.

Dockerfile

Create Dockerfile in empire directory:

FROM python:3.11-slim

# Set working directory
WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# Copy requirements and install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Create non-root user
RUN useradd --create-home --shell /bin/bash app \
    && chown -R app:app /app
USER app

# Expose port
EXPOSE 5000

# Health check
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:5000/health || exit 1

# Start application
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app_supabase:app"]

Docker Compose

For local development with Docker:

version: '3.8'

services:
  web:
    build: .
    ports:
      - "5000:5000"
    environment:
      - SUPABASE_URL=${SUPABASE_URL}
      - SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}
      - SUPABASE_SERVICE_KEY=${SUPABASE_SERVICE_KEY}
      - SECRET_KEY=${SECRET_KEY}
      - DEBUG=True
    volumes:
      - .:/app
    depends_on:
      - redis

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

Build and Run

# Build image
docker build -t empire-builder .

# Run container
docker run -p 5000:5000 \
  -e SUPABASE_URL=your-url \
  -e SUPABASE_ANON_KEY=your-key \
  -e SUPABASE_SERVICE_KEY=your-service-key \
  -e SECRET_KEY=your-secret \
  empire-builder

# Or use docker-compose
docker-compose up

Self-Hosted Deployment

Deploy on your own VPS or dedicated server.

Server Setup (Ubuntu 20.04+)

  1. Update System

    sudo apt update && sudo apt upgrade -y
    
  2. Install Dependencies

    # Python and pip
    sudo apt install python3.11 python3.11-pip python3.11-venv -y
    
    # Nginx (reverse proxy)
    sudo apt install nginx -y
    
    # Supervisor (process management)
    sudo apt install supervisor -y
    
    # SSL certificates
    sudo apt install certbot python3-certbot-nginx -y
    
  3. Create Application User

    sudo useradd --system --shell /bin/bash --home /opt/empire empire
    sudo mkdir -p /opt/empire
    sudo chown empire:empire /opt/empire
    
  4. Deploy Application

    # Switch to app user
    sudo -u empire -i
    
    # Clone repository
    cd /opt/empire
    git clone https://github.com/logan-code-del/empire-builder-game.git
    cd empire-builder-game
    
    # Create virtual environment
    python3.11 -m venv venv
    source venv/bin/activate
    
    # Install dependencies
    pip install -r requirements.txt
    
    # Configure environment
    cp .env.template .env
    # Edit .env with production values
    

Nginx Configuration

Create /etc/nginx/sites-available/empire-builder:

server {
    listen 80;
    server_name empire-builder.onrender.com;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /socket.io/ {
        proxy_pass http://127.0.0.1:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Enable the site:

sudo ln -s /etc/nginx/sites-available/empire-builder /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Supervisor Configuration

Create /etc/supervisor/conf.d/empire-builder.conf:

[program:empire-builder]
command=/opt/empire/strategic-pro/empire/venv/bin/gunicorn -w 4 -b 127.0.0.1:5000 app_supabase:app
directory=/opt/empire/strategic-pro/empire
user=empire
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/empire-builder.log
environment=PATH="/opt/empire/strategic-pro/empire/venv/bin"

Start the service:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start empire-builder

SSL Certificate

# Get SSL certificate
sudo certbot --nginx -d empire-builder.onrender.com

# Auto-renewal (add to crontab)
sudo crontab -e
# Add: 0 12 * * * /usr/bin/certbot renew --quiet

Monitoring and Maintenance

Health Checks

Add health check endpoint to app_supabase.py:

@app.route('/health')
def health_check():
    """Health check endpoint for monitoring."""
    try:
        # Test database connection
        result = supabase_config.get_supabase_client().table('users').select('count').limit(1).execute()

        return jsonify({
            'status': 'healthy',
            'timestamp': datetime.now().isoformat(),
            'database': 'connected' if result.data else 'disconnected'
        }), 200
    except Exception as e:
        return jsonify({
            'status': 'unhealthy',
            'error': str(e),
            'timestamp': datetime.now().isoformat()
        }), 503

Logging Configuration

import logging
from logging.handlers import RotatingFileHandler

if not app.debug:
    # File logging
    file_handler = RotatingFileHandler(
        'logs/empire.log',
        maxBytes=10240000,
        backupCount=10
    )
    file_handler.setFormatter(logging.Formatter(
        '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
    ))
    file_handler.setLevel(logging.INFO)
    app.logger.addHandler(file_handler)

    app.logger.setLevel(logging.INFO)
    app.logger.info('Empire Builder startup')

Backup Strategy

Database Backups: Supabase provides automatic backups, but you can also create manual backups:

# Create backup script
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
pg_dump $DATABASE_URL > backups/empire_backup_$DATE.sql

# Compress and upload to cloud storage
gzip backups/empire_backup_$DATE.sql
aws s3 cp backups/empire_backup_$DATE.sql.gz s3://your-backup-bucket/

Application Backups:

# Backup application files
tar -czf empire_app_backup_$(date +%Y%m%d).tar.gz \
  --exclude=venv \
  --exclude=__pycache__ \
  --exclude=.git \
  /opt/empire/strategic-pro/empire

Performance Optimization

Application Optimization

Caching:

from flask_caching import Cache

cache = Cache(app, config={'CACHE_TYPE': 'redis'})

@cache.cached(timeout=300)
def get_leaderboard():
    """Cached leaderboard data."""
    # Expensive database query
    return leaderboard_data

Database Connection Pooling:

# Configure connection pooling in supabase_config.py
def create_client_with_pooling():
    return create_client(
        url=SUPABASE_URL,
        key=SUPABASE_KEY,
        options=ClientOptions(
            postgrest_client_timeout=10,
            storage_client_timeout=10
        )
    )

Static File Optimization:

# In Nginx configuration
location /static/ {
    alias /opt/empire/strategic-pro/empire/static/;
    expires 1y;
    add_header Cache-Control "public, immutable";
    gzip_static on;
}

Security Considerations

Application Security

Environment Variables: - Never commit secrets to version control - Use different keys for each environment - Rotate keys regularly

HTTPS Enforcement:

from flask_talisman import Talisman

# Force HTTPS in production
if not app.debug:
    Talisman(app, force_https=True)

Rate Limiting:

from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(
    app,
    key_func=get_remote_address,
    default_limits=["200 per day", "50 per hour"]
)

@app.route('/api/login', methods=['POST'])
@limiter.limit("5 per minute")
def login():
    # Login logic

Server Security

Firewall Configuration:

# UFW firewall setup
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw enable

Automatic Updates:

# Install unattended-upgrades
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Fail2Ban:

# Install fail2ban
sudo apt install fail2ban

# Configure for Nginx
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# Edit jail.local to enable nginx protection

Troubleshooting

Common Deployment Issues

Build Failures: - Check Python version compatibility - Verify all dependencies in requirements.txt - Review build logs for specific errors

Runtime Errors: - Check environment variables are set - Verify database connectivity - Review application logs

Performance Issues: - Monitor resource usage - Check database query performance - Implement caching where appropriate

SSL Certificate Issues: - Verify domain DNS settings - Check certificate expiration - Ensure port 80 is accessible for renewal

This comprehensive deployment guide covers all major deployment scenarios for Empire Builder, from simple cloud deployments to complex self-hosted setups.