Django ist das populärste Python-Webframework. Für den Produktionsbetrieb benötigt es einen WSGI-Server wie Gunicorn und einen Reverse Proxy wie Nginx.

Architektur

Production-Stack

Client → Nginx (Port 80/443)
              ↓
         Gunicorn (Unix Socket)
              ↓
         Django App
              ↓
         PostgreSQL

Vorbereitung

Python installieren

apt install python3 python3-pip python3-venv

Projekt-Verzeichnis

mkdir -p /var/www/myproject
cd /var/www/myproject

Virtual Environment

python3 -m venv venv
source venv/bin/activate

Django-Projekt

Installation

pip install django gunicorn psycopg2-binary

Neues Projekt erstellen

django-admin startproject myproject .

# Oder bestehendes klonen
git clone https://github.com/user/myproject.git .
pip install -r requirements.txt

Settings anpassen

# myproject/settings.py

import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

# Security
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
DEBUG = os.environ.get('DJANGO_DEBUG', 'False') == 'True'

ALLOWED_HOSTS = ['example.de', 'www.example.de']

# Database
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('DB_NAME', 'myproject'),
        'USER': os.environ.get('DB_USER', 'myproject'),
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        'HOST': os.environ.get('DB_HOST', 'localhost'),
        'PORT': os.environ.get('DB_PORT', '5432'),
    }
}

# Static files
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'

MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

# Security headers
if not DEBUG:
    SECURE_BROWSER_XSS_FILTER = True
    SECURE_CONTENT_TYPE_NOSNIFF = True
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True
    X_FRAME_OPTIONS = 'DENY'

Datenbank einrichten

# PostgreSQL
sudo -u postgres psql

CREATE DATABASE myproject;
CREATE USER myproject WITH PASSWORD 'sicheres_passwort';
ALTER ROLE myproject SET client_encoding TO 'utf8';
ALTER ROLE myproject SET default_transaction_isolation TO 'read committed';
ALTER ROLE myproject SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE myproject TO myproject;
\q

# Migrationen
source venv/bin/activate
python manage.py migrate
python manage.py collectstatic --noinput
python manage.py createsuperuser

Gunicorn

Konfiguration

# /var/www/myproject/gunicorn.conf.py

import multiprocessing

bind = "unix:/run/gunicorn/myproject.sock"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"
worker_connections = 1000
timeout = 30
keepalive = 2

user = "www-data"
group = "www-data"

errorlog = "/var/log/gunicorn/myproject-error.log"
accesslog = "/var/log/gunicorn/myproject-access.log"
loglevel = "info"

proc_name = "myproject"

Systemd-Service

# /etc/systemd/system/gunicorn-myproject.service

[Unit]
Description=Gunicorn daemon for myproject
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myproject
RuntimeDirectory=gunicorn

Environment="DJANGO_SECRET_KEY=dein_secret_key"
Environment="DJANGO_DEBUG=False"
Environment="DB_PASSWORD=dein_db_passwort"

ExecStart=/var/www/myproject/venv/bin/gunicorn \
    --config /var/www/myproject/gunicorn.conf.py \
    myproject.wsgi:application

ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Service starten

mkdir -p /var/log/gunicorn
chown www-data:www-data /var/log/gunicorn

systemctl daemon-reload
systemctl enable gunicorn-myproject
systemctl start gunicorn-myproject

Nginx

Konfiguration

# /etc/nginx/sites-available/myproject

upstream myproject {
    server unix:/run/gunicorn/myproject.sock fail_timeout=0;
}

server {
    listen 80;
    server_name example.de www.example.de;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.de www.example.de;

    ssl_certificate /etc/letsencrypt/live/example.de/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.de/privkey.pem;

    client_max_body_size 10M;

    access_log /var/log/nginx/myproject-access.log;
    error_log /var/log/nginx/myproject-error.log;

    location /static/ {
        alias /var/www/myproject/staticfiles/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    location /media/ {
        alias /var/www/myproject/media/;
        expires 7d;
    }

    location / {
        proxy_pass http://myproject;
        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;
        proxy_redirect off;
    }
}

Aktivieren

ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx

Umgebungsvariablen

.env-Datei

# /var/www/myproject/.env

DJANGO_SECRET_KEY=dein_sehr_langes_secret_key_hier
DJANGO_DEBUG=False
DB_NAME=myproject
DB_USER=myproject
DB_PASSWORD=sicheres_passwort
DB_HOST=localhost
DB_PORT=5432

python-dotenv

# myproject/settings.py

from dotenv import load_dotenv
load_dotenv()

SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')

Systemd Environment

# /etc/systemd/system/gunicorn-myproject.service.d/override.conf

[Service]
EnvironmentFile=/var/www/myproject/.env

Celery (Background Tasks)

Installation

pip install celery redis

Konfiguration

# myproject/celery.py

import os
from celery import Celery

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

app = Celery('myproject')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
# myproject/settings.py

CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'

Celery-Service

# /etc/systemd/system/celery-myproject.service

[Unit]
Description=Celery Worker for myproject
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myproject
EnvironmentFile=/var/www/myproject/.env

ExecStart=/var/www/myproject/venv/bin/celery \
    -A myproject worker \
    --loglevel=INFO \
    --concurrency=4

Restart=on-failure

[Install]
WantedBy=multi-user.target

Deployment-Skript

#!/bin/bash
# /var/www/myproject/deploy.sh

set -e

cd /var/www/myproject

# Git Pull
git pull origin main

# Virtual Environment
source venv/bin/activate

# Dependencies
pip install -r requirements.txt

# Migrationen
python manage.py migrate --noinput

# Static Files
python manage.py collectstatic --noinput

# Services neu starten
sudo systemctl restart gunicorn-myproject
sudo systemctl restart celery-myproject

echo "Deployment abgeschlossen"
chmod +x /var/www/myproject/deploy.sh

Logging

Django Logging

# myproject/settings.py

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'file': {
            'level': 'WARNING',
            'class': 'logging.FileHandler',
            'filename': '/var/log/django/myproject.log',
            'formatter': 'verbose',
        },
    },
    'root': {
        'handlers': ['file'],
        'level': 'WARNING',
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'WARNING',
            'propagate': False,
        },
    },
}

Monitoring

Django Health Check

pip install django-health-check
# settings.py
INSTALLED_APPS = [
    # ...
    'health_check',
    'health_check.db',
    'health_check.cache',
    'health_check.storage',
]

# urls.py
urlpatterns = [
    path('health/', include('health_check.urls')),
]

Sentry

pip install sentry-sdk
# settings.py
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration

if not DEBUG:
    sentry_sdk.init(
        dsn="https://xxx@sentry.io/xxx",
        integrations=[DjangoIntegration()],
        traces_sample_rate=0.1,
    )

Sicherheit

Checkliste

# Django Security Check
python manage.py check --deploy

Security-Einstellungen

# settings.py (Production)

# HTTPS
SECURE_SSL_REDIRECT = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

# Cookies
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True

# Headers
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'

# HSTS
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

Zusammenfassung

| Komponente | Funktion | |------------|----------| | Django | Webframework | | Gunicorn | WSGI-Server | | Nginx | Reverse Proxy | | PostgreSQL | Datenbank | | Redis | Cache/Celery Broker | | Celery | Background Tasks |

| Befehl | Funktion | |--------|----------| | python manage.py migrate | Datenbank-Migration | | python manage.py collectstatic | Static Files sammeln | | python manage.py check --deploy | Security-Check |

| Datei | Funktion | |-------|----------| | /var/www/myproject/ | Projekt-Root | | /etc/systemd/system/gunicorn-*.service | Service-Definition | | /etc/nginx/sites-available/ | Nginx-Config |

Fazit

Ein Django-Deployment erfordert mehrere Komponenten, die zusammenarbeiten müssen. Gunicorn als WSGI-Server ist performant und einfach zu konfigurieren. Nginx übernimmt SSL-Terminierung und statische Dateien. Umgebungsvariablen trennen Konfiguration vom Code. Mit Celery lassen sich zeitintensive Aufgaben auslagern. Regelmäßige Updates und Security-Checks sind für den Produktionsbetrieb essentiell.