Django — deployment na VPS z Gunicorn i Nginx
Opublikowano: 9 kwietnia 2026 · Kategoria: Python / VPS
Django w trybie deweloperskim (python manage.py runserver) nie nadaje się do
produkcji — jest jednowątkowy, nie obsługuje wielu żądań jednocześnie i ma wyłączony wiele
mechanizmów bezpieczeństwa. Produkcyjny stack Django to: Gunicorn (WSGI server) obsługuje
Python, Nginx (reverse proxy) zarządza ruchem HTTP, systemd pilnuje procesów. Oto kompletna
instrukcja deployment od zera.
Przygotowanie VPS — Python, PostgreSQL, Nginx
# Ubuntu 22.04 / Debian 12 sudo apt update && sudo apt upgrade -y # Python 3.11+ i narzędzia sudo apt install python3 python3-pip python3-venv python3-dev -y # PostgreSQL sudo apt install postgresql postgresql-contrib libpq-dev -y sudo systemctl enable --now postgresql # Nginx sudo apt install nginx -y sudo systemctl enable --now nginx # Certbot (SSL) sudo apt install certbot python3-certbot-nginx -y # Utwórz użytkownika aplikacji (bez uprawnień root) sudo adduser --system --group --home /var/www/django-app django
Konfiguracja PostgreSQL — baza i użytkownik
sudo -u postgres psql CREATE DATABASE moja_baza_prod; CREATE USER moja_baza_user WITH PASSWORD 'BezpieczneHaslo456!'; ALTER ROLE moja_baza_user SET client_encoding TO 'utf8'; ALTER ROLE moja_baza_user SET default_transaction_isolation TO 'read committed'; ALTER ROLE moja_baza_user SET timezone TO 'Europe/Warsaw'; GRANT ALL PRIVILEGES ON DATABASE moja_baza_prod TO moja_baza_user; \q
Virtualenv i instalacja zależności
# Sklonuj projekt (lub skopiuj przez SFTP/rsync) sudo mkdir -p /var/www/django-app sudo chown django:django /var/www/django-app sudo -u django git clone https://github.com/twoj/projekt.git /var/www/django-app cd /var/www/django-app # Utwórz i aktywuj virtualenv sudo -u django python3 -m venv venv sudo -u django venv/bin/pip install --upgrade pip # Zainstaluj zależności sudo -u django venv/bin/pip install -r requirements.txt sudo -u django venv/bin/pip install gunicorn psycopg2-binary
settings.py dla produkcji
# settings_prod.py lub przez zmienne środowiskowe
import os
DEBUG = False
# Twoja domena / IP serwera
ALLOWED_HOSTS = ['twojadomena.pl', 'www.twojadomena.pl', '1.2.3.4']
# Baza danych (wartości z ENV — nie hardcoduj haseł!)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ['DB_NAME'],
'USER': os.environ['DB_USER'],
'PASSWORD': os.environ['DB_PASSWORD'],
'HOST': 'localhost',
'PORT': '5432',
}
}
# Pliki statyczne
STATIC_ROOT = '/var/www/django-app/staticfiles'
STATIC_URL = '/static/'
# Bezpieczeństwo HTTPS
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
# Klucz z ENV
SECRET_KEY = os.environ['DJANGO_SECRET_KEY'] # Uruchom migracje i zbierz pliki statyczne sudo -u django venv/bin/python manage.py migrate --settings=moj_projekt.settings_prod sudo -u django venv/bin/python manage.py collectstatic --noinput --settings=moj_projekt.settings_prod
Gunicorn — konfiguracja
# Plik konfiguracji Gunicorn: /var/www/django-app/gunicorn.conf.py bind = 'unix:/run/gunicorn/gunicorn.sock' # Unix socket (szybszy niż TCP) workers = 5 # (2 × CPU) + 1 worker_class = 'sync' # sync = domyślny, gevent = async I/O timeout = 30 # sekundy przed zabiciem wolnego workera keepalive = 5 # sekundy keep-alive dla połączeń max_requests = 1000 # restart workera po N requestach (memory leak prevention) max_requests_jitter = 100 # losowość restartów errorlog = '/var/log/gunicorn/error.log' accesslog = '/var/log/gunicorn/access.log' loglevel = 'warning'
systemd service dla Gunicorn
# /etc/systemd/system/gunicorn.service
[Unit]
Description=Gunicorn daemon dla Django
Requires=gunicorn.socket
After=network.target postgresql.service
[Service]
Type=notify
User=django
Group=django
RuntimeDirectory=gunicorn
WorkingDirectory=/var/www/django-app
# Zmienne środowiskowe (hasła, klucze) — poza kodem i poza git
EnvironmentFile=/etc/django-app/env
ExecStart=/var/www/django-app/venv/bin/gunicorn \
--config /var/www/django-app/gunicorn.conf.py \
moj_projekt.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
Restart=on-failure
[Install]
WantedBy=multi-user.target # /etc/systemd/system/gunicorn.socket [Unit] Description=Gunicorn socket [Socket] ListenStream=/run/gunicorn/gunicorn.sock SocketUser=www-data [Install] WantedBy=sockets.target
# Plik zmiennych środowiskowych (chmod 600!) # /etc/django-app/env DJANGO_SECRET_KEY=twoj-bardzo-dlugi-tajny-klucz-losowy-co-najmniej-50-znakow DB_NAME=moja_baza_prod DB_USER=moja_baza_user DB_PASSWORD=BezpieczneHaslo456! DJANGO_SETTINGS_MODULE=moj_projekt.settings_prod # Uprawnienia sudo chmod 600 /etc/django-app/env sudo chown root:root /etc/django-app/env # Włącz i uruchom sudo mkdir -p /var/log/gunicorn && sudo chown django:django /var/log/gunicorn sudo systemctl daemon-reload sudo systemctl enable --now gunicorn.socket gunicorn.service sudo systemctl status gunicorn
Nginx reverse proxy — konfiguracja
# /etc/nginx/sites-available/django-app
server {
listen 80;
server_name twojadomena.pl www.twojadomena.pl;
# Przekierowanie na HTTPS (certbot doda automatycznie)
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name twojadomena.pl www.twojadomena.pl;
# SSL — certbot wypełni automatycznie
ssl_certificate /etc/letsencrypt/live/twojadomena.pl/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/twojadomena.pl/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
# Pliki statyczne Django — serwowane bezpośrednio przez Nginx
location /static/ {
alias /var/www/django-app/staticfiles/;
expires 30d;
add_header Cache-Control "public, immutable";
}
location /media/ {
alias /var/www/django-app/media/;
expires 7d;
}
# Całą resztę kieruj do Gunicorn przez Unix socket
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn/gunicorn.sock;
proxy_read_timeout 30s;
proxy_connect_timeout 5s;
client_max_body_size 20M;
}
} sudo ln -s /etc/nginx/sites-available/django-app /etc/nginx/sites-enabled/ sudo nginx -t # sprawdź składnię sudo systemctl reload nginx # Uzyskaj certyfikat SSL (Let's Encrypt) sudo certbot --nginx -d twojadomena.pl -d www.twojadomena.pl # Sprawdź automatyczne odnawianie sudo certbot renew --dry-run
Weryfikacja i logi
# Status serwisów sudo systemctl status gunicorn nginx postgresql # Logi w czasie rzeczywistym sudo journalctl -u gunicorn -f # Gunicorn (systemd logi) sudo tail -f /var/log/gunicorn/error.log # Gunicorn error log sudo tail -f /var/log/nginx/error.log # Nginx error log # Test aplikacji curl -I https://twojadomena.pl # sprawdź nagłówki HTTP curl https://twojadomena.pl/api/health/ # endpoint health check