Как развернуть Django-приложение на VPS с Gunicorn и nginx
Чтобы развернуть Django-приложение на VPS, запустите его на продакшн-сервере, поставьте перед ним обратный прокси, подключите базу данных и настройте HTTPS. В этом руководстве используются Ubuntu 24.04 LTS, Django 5.2, PostgreSQL, Gunicorn, systemd и nginx. Адаптируйте пути и имя модуля под свой проект.
Вам понадобятся VPS с SSH и sudo-доступом, домен, направленный на него, рабочий Django-репозиторий и файл блокировки зависимостей или зафиксированные версии requirements.txt. В примере используются example.com, /srv/myapp и myproject.wsgi:application как заполнители. Описано развертывание WSGI-приложения; если вашему приложению нужен ASGI, используйте соответствующую настройку. Ссылки проверены 9 сентября 2026 года.
1. Подготовка сервера и пользователя приложения
Установите базовые пакеты на новый сервер Ubuntu:
sudo apt update
sudo apt install -y python3-venv python3-dev build-essential libpq-dev postgresql nginx git
sudo adduser --system --group --home /srv/myapp django
sudo install -d -o django -g django /srv/myappПользователь django запускает приложение без привилегий root. Административный SSH-доступ оставьте для своей обычной учётной записи. Разрешите своё SSH-соединение в файрволе перед включением правил, а также входящий HTTP и HTTPS. Держите PostgreSQL доступным только там, где это требуется приложению.
2. Создание учётных данных PostgreSQL
Создайте роль базы данных с паролем, который зададите при запросе, а затем создайте её базу данных:
sudo -u postgres createuser --pwprompt myapp
sudo -u postgres createdb --owner=myapp myappПриложение будет подключаться к 127.0.0.1, который использует аутентификацию по паролю при стандартной настройке PostgreSQL в Ubuntu. Проверьте ваш pg_hba.conf, если подключение не работает. Не открывайте порт базы данных публично для решения локальной проблемы аутентификации.
3. Установка приложения и зависимостей
Замените пример URL репозитория. Для частного репозитория организуйте доступ на чтение, не помещая токен в URL или в коммиты.
sudo -u django git clone https://github.com/your-org/your-app.git /srv/myapp/app
sudo -u django python3 -m venv /srv/myapp/venv
sudo -u django /srv/myapp/venv/bin/pip install -r /srv/myapp/app/requirements.txtВаши требования должны включать версию Django, Gunicorn и драйвер PostgreSQL, который использует ваш проект. Используйте существующий менеджер пакетов проекта, если у него другой файл блокировки. Убедитесь, что модуль настроек и путь импорта WSGI соответствуют вашему репозиторию.
4. Явная загрузка продакшн-настроек
Создайте /etc/myapp.env в вашем редакторе. Храните реальные значения там, а не в Git. В этом примере предполагается, что ваши настройки читают эти имена:
DJANGO_SECRET_KEY='replace-with-a-long-random-secret'
DJANGO_ALLOWED_HOSTS='example.com'
DJANGO_CSRF_TRUSTED_ORIGINS='https://example.com'
DB_NAME='myapp'
DB_USER='myapp'
DB_PASSWORD='replace-with-the-password-you-set'
DB_HOST='127.0.0.1'
DB_PORT='5432'Используйте простые значения в кавычках, совместимые и с оболочкой, и с синтаксисом environment-file systemd. Защитите файл:
sudo chown root:django /etc/myapp.env
sudo chmod 640 /etc/myapp.envВ продакшн-настройках настройте соответствующее чтение. Этот фрагмент предполагает, что BASE_DIR уже существует:
import os
DEBUG = False
SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
ALLOWED_HOSTS = os.environ['DJANGO_ALLOWED_HOSTS'].split(',')
CSRF_TRUSTED_ORIGINS = os.environ['DJANGO_CSRF_TRUSTED_ORIGINS'].split(',')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ['DB_NAME'],
'USER': os.environ['DB_USER'],
'PASSWORD': os.environ['DB_PASSWORD'],
'HOST': os.environ['DB_HOST'],
'PORT': os.environ['DB_PORT'],
}
}
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'Держите загруженные медиа-файлы отдельно от собранных статических ассетов. Выбирайте надёжное хранилище, политику бэкапов и контроль доступа, подходящие для загружаемого контента. См. чек-лист развертывания Django.
5. Проверка настроек и подготовка базы данных
Выполняйте команды управления от имени пользователя приложения с тем же окружением, что и у сервиса:
sudo -u django bash -c 'set -a; . /etc/myapp.env; set +a; cd /srv/myapp/app; /srv/myapp/venv/bin/python manage.py check --deploy'
sudo -u django bash -c 'set -a; . /etc/myapp.env; set +a; cd /srv/myapp/app; /srv/myapp/venv/bin/python manage.py migrate --noinput'
sudo -u django bash -c 'set -a; . /etc/myapp.env; set +a; cd /srv/myapp/app; /srv/myapp/venv/bin/python manage.py collectstatic --noinput'Проверьте предупреждения развертывания. Настройки, связанные с HTTPS, настраиваются после того, как у прокси будет рабочий сертификат. Для существующей продакшн-базы сделайте и протестируйте бэкап перед применением изменений схемы. Запускайте миграции один раз как часть релиза, а не в каждом воркере Gunicorn.
6. Запуск Gunicorn под systemd
Создайте /etc/systemd/system/myapp.service:
[Unit]
Description=Django application
After=network.target postgresql.service
[Service]
User=django
Group=django
WorkingDirectory=/srv/myapp/app
EnvironmentFile=/etc/myapp.env
ExecStart=/srv/myapp/venv/bin/gunicorn myproject.wsgi:application --bind 127.0.0.1:8001 --workers 2 --access-logfile - --error-logfile -
Restart=on-failure
RestartSec=5
PrivateTmp=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.targetЗначение в два воркера — лишь начальный пример, а не формула расчёта. Измерьте потребление памяти, соединения с БД и время отклика перед увеличением. Gunicorn слушает на loopback, чтобы посетители попадали к приложению через nginx.
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
sudo systemctl status myapp --no-pager
curl -I -H 'Host: example.com' http://127.0.0.1:8001/Используйте journalctl -u myapp для ошибок процесса. Ошибка импорта обычно указывает на путь к модулю, рабочую директорию или установку зависимостей. См. руководство Django по WSGI и Gunicorn.
7. Установка nginx перед приложением
Создайте /etc/nginx/sites-available/myapp:
server {
listen 80;
server_name example.com;
location /static/ {
alias /srv/myapp/app/staticfiles/;
}
location / {
proxy_pass http://127.0.0.1:8001;
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;
}
}Включите новый сайт один раз, проверьте конфигурацию и перезагрузите:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myapp
sudo nginx -t
sudo systemctl reload nginxУбедитесь, что nginx может читать собранные статические файлы и обходить их родительские директории. Держите конфигурацию сайта по умолчанию согласованной с вашими другими хостируемыми доменами; не удаляйте не связанные сайты. Руководство nginx объясняет настройку обратного прокси и статических файлов.
8. Добавление HTTPS и повторная проверка Django
Установите сертификат с помощью выбранного ACME-клиента и его актуальных инструкций. С установленным Certbot для nginx запрос сертификата для этого примера выглядит так:
sudo certbot --nginx -d example.com
sudo certbot renew --dry-runИспользуйте инструкции Certbot для установки на вашей ОС. Проверьте DNS домена, доступность по HTTP и продление перед тем, как считать TLS готовым.
После того как HTTPS заработает, установите эти опции Django:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = TrueДоверяйте заголовку forwarded-protocol только потому, что nginx контролирует его, а Gunicorn не экспощен напрямую. Перезапустите сервис, снова выполните check --deploy и протестируйте вход и отправку формы через HTTPS. Отдельно проверьте HSTS после верификации HTTPS на всех задействованных хостах.
9. Делайте будущие релизы повторяемыми
Для каждого релиза выбирайте проверенную ревизию, устанавливайте её заблокированные зависимости, применяйте совместимые миграции, собирайте статические файлы и перезапускайте сервис. Тестируйте живое приложение и следите за ошибками после изменений.
Сохраняйте предыдущую ревизию доступной, но помните: откат кода может не отменить миграцию базы данных. Документируйте путь восстановления при изменениях схемы и тестируйте восстановление данных в отдельную базу.
Если приложение использует Celery, выделите ему отдельное определение сервиса, команду и конфигурацию брокера. Не запускайте воркер как фоновую shell-команду внутри сервиса Gunicorn.
Когда выбрать управляемый хостинг
VPS уместен, когда вам нужен контроль над сервером и вы можете его поддерживать. Если вы хотите передать больше операций по управлению хостом — сравните варианты хостинга Python-приложений. На Lizard приложение может использовать Managed Postgres и отдельный воркер, пока вы сохраняете контроль над кодом и конфигурацией.
Документация по развертыванию охватывает этот путь. Вам всё равно понадобятся правильные настройки Django, процесс релиза и проверенный план восстановления данных.
FAQ
Почему я получаю ошибку 502? Проверьте, запущен ли Gunicorn и слушает ли он на 127.0.0.1:8001, затем изучите его логи и лог ошибок nginx. Обратный прокси не исправит процесс, который не запускается.
Почему статические файлы отсутствуют? Проверьте STATIC_ROOT, вывод collectstatic, путь alias в nginx и права доступа к файлам. Загруженные медиа требуют отдельной настройки.
Можно ли использовать SQLite вместо PostgreSQL? Некоторые приложения могут, но оцените требования к параллелизму, персистентности и бэкапам. Не меняйте движок базы без тестирования нагрузки вашего приложения.
Достаточно ли успешной проверки развертывания? Нет. Протестируйте реальный пользовательский сценарий, записи в БД, доступ к файлам и восстановление. Проверки конфигурации отлавливают лишь часть продакшн-поведения.
Разрабатывайте с ИИ. Развёртывайте с Lizard.
Вам не нужна платформенная команда, чтобы выйти в продакшн. Весь ваш облак — в одной команде CLI.
- Рабочие пространства
- —
- Сервисы
- —
- Аддоны
- —
- Развёртывания
- —