Как развернуть Django на VPS с Gunicorn, systemd и Nginx
Разворачиваем Django на Ubuntu: настройки приложения, миграции, сбор статики, служба Gunicorn и прокси Nginx.

Django-приложение на VPS можно запустить как службу systemd, принимать HTTP-запросы через Gunicorn и отдавать публичный трафик через Nginx. Эта схема подходит обычному WSGI-проекту на Ubuntu. Если приложение использует WebSocket или требует ASGI, выберите ASGI-сервер и настройте его отдельно.
Подготовить проект и окружение
В примере manage.py лежит в /srv/myapp/app, модуль проекта называется config, а домен — example.com. Подставьте свои значения. Gunicorn должен быть в зафиксированных зависимостях проекта. Перед выкладкой проверьте, что версия Python на VPS подходит этим зависимостям. Если вы ещё выбираете машину, начните с подбора VPS для первого проекта.
sudo apt update
sudo apt install python3-venv nginx
sudo adduser --system --group --home /srv/myapp djangoapp
sudo install -d -o djangoapp -g djangoapp -m 0755 /srv/myapp
sudo install -d -o djangoapp -g djangoapp -m 0755 /srv/myapp/app /srv/myapp/static
sudo install -d -o djangoapp -g djangoapp -m 0750 /var/lib/myapp
Перенесите в app исходники своего проекта и дайте djangoapp доступ к ним. Файлы должны включать manage.py, requirements.txt и пакет с wsgi.py. Для SQLite задайте в настройках абсолютный путь /var/lib/myapp/db.sqlite3, чтобы база и файлы её журнала оставались в каталоге данных при замене кода. Если база уже существует, сначала сделайте согласованную копию и перенесите данные отдельно. Для PostgreSQL пригодится инструкция по установке и подключению базы.
sudo -u djangoapp python3 -m venv /srv/myapp/venv
sudo -u djangoapp /srv/myapp/venv/bin/python -m pip install \
-r /srv/myapp/app/requirements.txt
Задать параметры Django
В выделенных production-настройках укажите домен, параметры статики и источник секрета. DJANGO_SECRET_KEY ниже — переменная этого примера. Django прочитает её благодаря строке os.environ.
import os
DEBUG = False
SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
ALLOWED_HOSTS = ['example.com']
STATIC_URL = '/static/'
STATIC_ROOT = '/srv/myapp/static'
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Храните постоянный случайный ключ в /etc/myapp.env с владельцем root и правами 0600. Укажите строку DJANGO_SECRET_KEY=... и нужные вашему проекту параметры базы. Сгенерировать ключ можно командой python3 -c 'import secrets; print(secrets.token_urlsafe(64))'. Перенесите его прямо в закрытый файл и сохраните при следующих выкладках. Если настройки выбираются через DJANGO_SETTINGS_MODULE, задайте и эту переменную.
Заголовку X-Forwarded-Proto можно доверять, если Gunicorn доступен только локально, а Nginx всегда подставляет собственное значение. Иначе клиент сможет повлиять на то, считает ли Django запрос защищённым.
Применить миграции и собрать статику
Запускайте команды с теми же настройками и от того же пользователя, что и службу. systemd-run позволяет передать закрытый файл окружения без ослабления его прав. Для проекта с данными проверьте резервную копию до migrate.
run_manage() {
sudo systemd-run --wait --pipe --collect \
-p User=djangoapp -p Group=djangoapp \
-p WorkingDirectory=/srv/myapp/app \
-p EnvironmentFile=/etc/myapp.env \
/srv/myapp/venv/bin/python manage.py "$@"
}
run_manage check --deploy
run_manage migrate --plan
run_manage migrate
run_manage collectstatic --noinput
check --deploy показывает рекомендации по настройкам. На промежуточном HTTP-этапе предупреждения об HTTPS ожидаемы. Вернитесь к ним после установки сертификата и разберите каждое по условиям своего проекта.
Запустить Gunicorn через systemd
Создайте /etc/systemd/system/myapp.service. Если пакет с вашим wsgi.py называется иначе, исправьте config.wsgi:application.
[Unit]
Description=Django application myapp
After=network.target
[Service]
User=djangoapp
Group=djangoapp
WorkingDirectory=/srv/myapp/app
EnvironmentFile=/etc/myapp.env
ExecStart=/srv/myapp/venv/bin/gunicorn --bind 127.0.0.1:8001 --workers 2 --access-logfile - config.wsgi:application
Restart=on-failure
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
sudo systemctl status myapp.service --no-pager
curl -i -H 'Host: example.com' http://127.0.0.1:8001/
Проверьте маршрут, который действительно существует в вашем проекте. Если корневого URL нет, подставьте готовую страницу или /health/. 400 на локальной проверке часто связан с ALLOWED_HOSTS. При падении службы начните с sudo journalctl -u myapp.service -n 50 --no-pager и проверьте пути, переменные окружения и WSGI-модуль.
Подключить Nginx и HTTPS
На Ubuntu с пакетным Nginx создайте /etc/nginx/sites-available/myapp.
server {
listen 80;
server_name example.com;
location /static/ {
alias /srv/myapp/static/;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:8001;
}
}
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myapp
sudo nginx -t && sudo systemctl reload nginx
curl -i -H 'Host: example.com' http://127.0.0.1/
Проверьте права Nginx на чтение статики и проход по родительским каталогам. Для проекта с админкой Django контрольный ресурс — /static/admin/css/base.css. Доступ к загрузкам пользователей проектируйте отдельно. Публичный alias для каталога с приватными вложениями обойдёт проверки Django.
Направьте домен на сервер и подключите сертификат. После запуска HTTPS настройте SESSION_COOKIE_SECURE, CSRF_COOKIE_SECURE и перенаправление HTTP по правилам вашего vhost. Проверьте через браузер публичную страницу, статику и отправку формы. Затем повторите run_manage check --deploy. При 502 используйте проверку маршрута до Gunicorn, а при задержках — разбор 504.
На стенде с Ubuntu 24.04 и Django 5.2 мы применили миграции и собрали статику. Nginx вернул 200 для страницы приложения и CSS админки. Запрос с неподходящим заголовком Host при прямом обращении к Gunicorn вернул 400.
Первоисточники
Сообщите адрес статьи и фрагмент через контакты редакции.


