Production Deployment

Environment Variables

Set all required environment variables for production:

# Django
DJANGO_SECRET_KEY=your-secret-key-here
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com

# Database (PostgreSQL recommended)
DB_ENGINE=django.db.backends.postgresql
DB_NAME=subscription_db
DB_USER=postgres
DB_PASSWORD=
DB_HOST=localhost
DB_PORT=5432

# Celery / Redis
CELERY_BROKER_URL=redis://localhost:6379/0
CELERY_RESULT_BACKEND=redis://localhost:6379/0

# Midtrans (PRODUCTION keys)
MIDTRANS_SERVER_KEY=
MIDTRANS_CLIENT_KEY=
MIDTRANS_MERCHANT_ID=
MIDTRANS_IS_PRODUCTION=True
MIDTRANS_NOTIFICATION_URL=https://yourdomain.com/api/subscriptions/webhook/notification/

Warning

MIDTRANS_IS_PRODUCTION=True switches the API base URL from sandbox to api.midtrans.com. Only use this with production keys from Midtrans Dashboard.

Production Checklist

  • DEBUG=False

  • Strong SECRET_KEY (not the default)

  • ALLOWED_HOSTS set to your domain(s)

  • PostgreSQL database (not SQLite)

  • Redis for Celery broker

  • HTTPS enabled

  • Midtrans production keys configured

  • MIDTRANS_NOTIFICATION_URL is publicly accessible via HTTPS

  • Celery worker running

  • Celery beat running with DatabaseScheduler

  • setup_periodic_tasks command executed

  • Static files collected (collectstatic)

  • Gunicorn or uWSGI as application server

  • Nginx as reverse proxy

Gunicorn

pip install gunicorn

gunicorn config.wsgi:application \
    --bind 0.0.0.0:8000 \
    --workers 4 \
    --timeout 120

Systemd Services

Django (Gunicorn)

# /etc/systemd/system/django-subscription.service
[Unit]
Description=Django Subscription App
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/django-subscription-midtrans
Environment="PATH=/opt/django-subscription-midtrans/.venv/bin"
EnvironmentFile=/opt/django-subscription-midtrans/.env
ExecStart=/opt/django-subscription-midtrans/.venv/bin/gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 4
Restart=always

[Install]
WantedBy=multi-user.target

Celery Worker

# /etc/systemd/system/celery-worker.service
[Unit]
Description=Celery Worker
After=network.target redis.service

[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/django-subscription-midtrans
Environment="PATH=/opt/django-subscription-midtrans/.venv/bin"
EnvironmentFile=/opt/django-subscription-midtrans/.env
ExecStart=/opt/django-subscription-midtrans/.venv/bin/celery -A config worker -l info
Restart=always

[Install]
WantedBy=multi-user.target

Celery Beat

# /etc/systemd/system/celery-beat.service
[Unit]
Description=Celery Beat Scheduler
After=network.target redis.service

[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/django-subscription-midtrans
Environment="PATH=/opt/django-subscription-midtrans/.venv/bin"
EnvironmentFile=/opt/django-subscription-midtrans/.env
ExecStart=/opt/django-subscription-midtrans/.venv/bin/celery -A config beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
Restart=always

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl enable django-subscription celery-worker celery-beat
sudo systemctl start django-subscription celery-worker celery-beat

Nginx Configuration

server {
    listen 80;
    server_name yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    location /static/ {
        alias /opt/django-subscription-midtrans/staticfiles/;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        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;
    }
}

Database Migrations

Always run migrations before deploying:

python manage.py migrate
python manage.py collectstatic --noinput
python manage.py setup_periodic_tasks

Monitoring

Logging

Configure Django logging for the subscriptions and subscriptions.midtrans loggers:

LOGGING = {
    "version": 1,
    "handlers": {
        "file": {
            "class": "logging.FileHandler",
            "filename": "/var/log/django-subscription/app.log",
        },
    },
    "loggers": {
        "subscriptions": {"handlers": ["file"], "level": "INFO"},
        "subscriptions.midtrans": {"handlers": ["file"], "level": "DEBUG"},
    },
}

Celery Monitoring

Use Flower for Celery task monitoring:

pip install flower
celery -A config flower --port=5555