In this final part of our series, we'll prepare our Project Budget Manager for production deployment. We'll cover security configurations, performance optimizations, and deployment steps.

Production Settings

  1. Update config/settings/production.py with production-specific settings:
from .base import *

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False

# SECURITY WARNING: configure the secret key for production
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')

# Database configuration
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': os.environ.get('DB_NAME'),
        'USER': os.environ.get('DB_USER'),
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        'HOST': os.environ.get('DB_HOST'),
        'PORT': os.environ.get('DB_PORT', '3306'),
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
            'charset': 'utf8mb4',
        }
    }
}

# Email settings
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = os.environ.get('EMAIL_HOST', 'smtp.gmail.com')
EMAIL_PORT = int(os.environ.get('EMAIL_PORT', 587))
EMAIL_USE_TLS = True
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')
DEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL', EMAIL_HOST_USER)

# Security settings
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True

# Static files configuration
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

# Enable WhiteNoise for static file serving
MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

# Site settings
SITE_ID = 1
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')
USE_X_FORWARDED_HOST = True

Environment Variables

Create a production .env file template (.env.example):

# Django settings
DJANGO_SETTINGS_MODULE=config.settings.production
DJANGO_SECRET_KEY=your-secret-key-here
ALLOWED_HOSTS=your-domain.com,www.your-domain.com

# Database settings
DB_NAME=budget_db
DB_USER=budget_user
DB_PASSWORD=your-secure-password
DB_HOST=localhost
DB_PORT=3306

# Email settings
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
[email protected]
EMAIL_HOST_PASSWORD=your-app-specific-password
[email protected]

# Security settings
SECURE_SSL_REDIRECT=True
SESSION_COOKIE_SECURE=True
CSRF_COOKIE_SECURE=True

Production Dependencies

Update requirements.txt with production dependencies:

Django==5.0.2
django-allauth==0.61.1
django-crispy-forms==2.3
mysqlclient==2.2.4
whitenoise==6.9.0
python-dotenv==1.0.1
gunicorn==21.2.0

Setting Up MySQL Database

  1. Install MySQL server:
sudo apt update
sudo apt install mysql-server
  1. Create database and user:
CREATE DATABASE budget_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'budget_user'@'localhost' IDENTIFIED BY 'your-secure-password';
GRANT ALL PRIVILEGES ON budget_db.* TO 'budget_user'@'localhost';
FLUSH PRIVILEGES;

Nginx Configuration

  1. Install Nginx:
sudo apt install nginx
  1. Create Nginx configuration (/etc/nginx/sites-available/project-budget):
server {
    listen 80;
    server_name your-domain.com www.your-domain.com;

    location = /favicon.ico { access_log off; log_not_found off; }

    location /static/ {
        root /path/to/your/project;
    }

    location /media/ {
        root /path/to/your/project;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}
  1. Enable the site:
sudo ln -s /etc/nginx/sites-available/project-budget /etc/nginx/sites-enabled
sudo nginx -t
sudo systemctl restart nginx

Gunicorn Service

  1. Create a systemd service file (/etc/systemd/system/gunicorn.service):
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=your-user
Group=www-data
WorkingDirectory=/path/to/your/project
ExecStart=/path/to/your/venv/bin/gunicorn \
    --access-logfile - \
    --workers 3 \
    --bind unix:/run/gunicorn.sock \
    config.wsgi:application

[Install]
WantedBy=multi-user.target
  1. Create socket file (/etc/systemd/system/gunicorn.socket):
[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target
  1. Start and enable the service:
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket

SSL Certificate with Let's Encrypt

  1. Install Certbot:
sudo apt install certbot python3-certbot-nginx
  1. Obtain SSL certificate:
sudo certbot --nginx -d your-domain.com -d www.your-domain.com

Deployment Checklist

  1. Prepare the environment:
# Create and activate virtual environment
python -m venv venv
source venv/bin/activate

# Install dependencies
pip install -r requirements.txt
  1. Set up the database:
python manage.py migrate
python manage.py createsuperuser
  1. Collect static files:
python manage.py collectstatic --noinput
  1. Set up environment variables:
cp .env.example .env
# Edit .env with your production values
  1. Test the application:
python manage.py check --deploy

Monitoring and Maintenance

  1. Set up logging in production settings:
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'ERROR',
            'class': 'logging.FileHandler',
            'filename': '/path/to/django-error.log',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'ERROR',
            'propagate': True,
        },
    },
}
  1. Regular maintenance tasks:
# Backup database
mysqldump -u budget_user -p budget_db > backup.sql

# Check for updates
pip list --outdated

# Monitor logs
tail -f /path/to/django-error.log

Performance Optimization Tips

  1. Enable caching:
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
    }
}
  1. Use database connection pooling:
DATABASES = {
    'default': {
        # ... other settings ...
        'CONN_MAX_AGE': 60,
    }
}

Resources


This article is part of the "Building a Project Budget Manager with Django" series. Check out Part 1, Part 2, and Part 3 if you haven't already!