Skip to main content

Overview

SASCOP BME SubTec is a Django application designed to be deployed on multiple platforms including AWS, Vercel, and traditional WSGI servers. This guide covers deployment strategies, requirements, and best practices.

Deployment Options

The application supports several deployment methods:

WSGI Server

Deploy using Gunicorn behind Nginx or Apache
  • Traditional deployment
  • Full control over infrastructure
  • Recommended for AWS EC2, DigitalOcean, etc.

Vercel

Serverless deployment on Vercel
  • Zero configuration
  • Automatic scaling
  • Built-in CI/CD

Docker

Containerized deployment
  • Consistent environments
  • Easy scaling
  • Works with Kubernetes, ECS, etc.

Platform as a Service

Deploy to Heroku, Railway, or Render
  • Managed infrastructure
  • Simple deployment process
  • Automatic backups

WSGI Application

The application provides a WSGI entry point in bme_subtec/wsgi.py:1-8:
bme_subtec/wsgi.py
import os
from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'bme_subtec.settings')

application = get_wsgi_application()

app = application
The app variable is used by some platforms like Vercel that expect this naming convention.

Production Server

The application uses Gunicorn as its WSGI HTTP server:
requirements.txt
gunicorn==21.2.0

Running with Gunicorn

1

Basic Command

gunicorn bme_subtec.wsgi:application
2

Production Configuration

gunicorn bme_subtec.wsgi:application \
  --bind 0.0.0.0:8000 \
  --workers 4 \
  --timeout 120 \
  --access-logfile - \
  --error-logfile -
3

Using Gunicorn Config File

Create gunicorn_config.py:
gunicorn_config.py
import multiprocessing

# Server socket
bind = "0.0.0.0:8000"
backlog = 2048

# Worker processes
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"
worker_connections = 1000
timeout = 120
keepalive = 2

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

# Process naming
proc_name = "sascop_bme_subtec"

# Server mechanics
daemon = False
pidfile = None
user = None
group = None
tmp_upload_dir = None
Run with config:
gunicorn -c gunicorn_config.py bme_subtec.wsgi:application

Static Files

The application uses WhiteNoise for serving static files in production:
bme_subtec/settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  # Must be after SecurityMiddleware
    # ...
]

STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [BASE_DIR / 'operaciones' / 'static',]

Collecting Static Files

Before deployment, collect all static files:
python manage.py collectstatic --noinput
WhiteNoise serves static files directly from the Django app, eliminating the need for Nginx or Apache to serve static content.

Optional: Enable Compression

For better performance, enable WhiteNoise compression:
bme_subtec/settings.py
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

Database Considerations

Production Database

The application uses PostgreSQL with SSL in production:
bme_subtec/settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('RDS_DB_NAME', 'postgres'),
        'USER': os.environ.get('RDS_USERNAME', 'postgres'),
        'PASSWORD': os.environ.get('RDS_PASSWORD', ''),
        'HOST': os.environ.get('RDS_HOSTNAME', 'localhost'),
        'PORT': os.environ.get('RDS_PORT', '5432'),
        'OPTIONS': {
            'sslmode': 'require',
        }
    }
}

Database Migrations

Run migrations on production database:
python manage.py migrate --noinput
See Database Migrations for detailed information.

Environment Variables

Critical environment variables for production:
.env.production
# Django Core
SECRET_KEY=your-production-secret-key-here
DEBUG=False
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com

# Database
RDS_DB_NAME=production_db
RDS_USERNAME=db_user
RDS_PASSWORD=secure_password
RDS_HOSTNAME=db.example.com
RDS_PORT=5432

# Email
EMAIL_HOST=smtp.sendgrid.net
EMAIL_PORT=587
EMAIL_HOST_USER=apikey
EMAIL_HOST_PASSWORD=your-sendgrid-api-key
DEFAULT_FROM_EMAIL=SASCOP <[email protected]>
Critical Security Settings:
  • Set DEBUG=False in production
  • Use a strong, unique SECRET_KEY
  • Configure ALLOWED_HOSTS properly
  • Never commit .env files to version control
See Environment Variables for complete reference.

Security Checklist

1

DEBUG Mode

Ensure DEBUG=False in production:
DEBUG = os.getenv('DEBUG', 'True') == 'True'
Set environment variable: DEBUG=False
2

Secret Key

Generate a strong secret key:
python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'
Set in environment:
SECRET_KEY=your-generated-key-here
3

Allowed Hosts

Configure allowed hosts:
bme_subtec/settings.py
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '').split(',')
Set environment:
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
4

HTTPS Configuration

Enable HTTPS security settings:
bme_subtec/settings.py
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
5

Database SSL

Ensure SSL is enabled for database connections:
'OPTIONS': {
    'sslmode': 'require',
}

Deployment Workflow

1

Prepare Code

git checkout main
git pull origin main
2

Install Dependencies

pip install -r requirements.txt
3

Set Environment Variables

Configure all required environment variables in your deployment platform.
4

Collect Static Files

python manage.py collectstatic --noinput
5

Run Migrations

python manage.py migrate --noinput
6

Create Superuser (First Time Only)

python manage.py createsuperuser --noinput \
  --username admin \
  --email [email protected]
7

Start Application

gunicorn bme_subtec.wsgi:application --bind 0.0.0.0:8000 --workers 4

Build Script

The application includes a build_files.sh script for deployment automation:
build_files.sh
#!/bin/bash
# Currently empty - add your build commands here

# Example build steps:
# pip install -r requirements.txt
# python manage.py collectstatic --noinput
# python manage.py migrate --noinput
Make it executable:
chmod +x build_files.sh

Monitoring and Logging

Application Logging

Configure Django logging for production:
bme_subtec/settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
        'file': {
            'class': 'logging.FileHandler',
            'filename': '/var/log/sascop/django.log',
            'formatter': 'verbose',
        },
    },
    'root': {
        'handlers': ['console', 'file'],
        'level': 'INFO',
    },
    'loggers': {
        'django': {
            'handlers': ['console', 'file'],
            'level': 'INFO',
            'propagate': False,
        },
    },
}

Health Check Endpoint

Create a health check endpoint for monitoring:
core/views.py
from django.http import JsonResponse
from django.db import connection

def health_check(request):
    try:
        # Check database connection
        with connection.cursor() as cursor:
            cursor.execute("SELECT 1")
        
        return JsonResponse({
            'status': 'healthy',
            'database': 'connected'
        })
    except Exception as e:
        return JsonResponse({
            'status': 'unhealthy',
            'error': str(e)
        }, status=500)

Performance Optimization

Database Connection Pooling

For high-traffic applications, consider using pgBouncer or django-db-connection-pool.

Caching

Implement Redis caching:
bme_subtec/settings.py
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': os.getenv('REDIS_URL', 'redis://127.0.0.1:6379/1'),
    }
}

Gunicorn Workers

Calculate optimal worker count:
workers = (2 * multiprocessing.cpu_count()) + 1

Rollback Strategy

1

Database Backup

Always backup database before deployment:
pg_dump -h $RDS_HOSTNAME -U $RDS_USERNAME $RDS_DB_NAME > backup_$(date +%Y%m%d_%H%M%S).sql
2

Git Tag Release

Tag releases for easy rollback:
git tag -a v1.0.0 -m "Release version 1.0.0"
git push origin v1.0.0
3

Rollback Commands

If issues occur:
# Rollback code
git checkout v1.0.0

# Rollback migrations
python manage.py migrate app_name migration_name

# Restore database (if needed)
psql -h $RDS_HOSTNAME -U $RDS_USERNAME $RDS_DB_NAME < backup.sql

Next Steps

Environment Variables

Complete reference for all environment variables

Database Migrations

Learn about managing migrations in production

Static Files

Configure static file serving

Build docs developers (and LLMs) love