# Production Deployment ## Environment Variables Set all required environment variables for production: ```env # 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 ```bash pip install gunicorn gunicorn config.wsgi:application \ --bind 0.0.0.0:8000 \ --workers 4 \ --timeout 120 ``` ## Systemd Services ### Django (Gunicorn) ```ini # /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 ```ini # /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 ```ini # /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: ```bash sudo systemctl enable django-subscription celery-worker celery-beat sudo systemctl start django-subscription celery-worker celery-beat ``` ## Nginx Configuration ```nginx 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: ```bash 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: ```python 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](https://flower.readthedocs.io/) for Celery task monitoring: ```bash pip install flower celery -A config flower --port=5555 ```