MHOSTMHOST

Django на VPS

В этом руководстве вы развернёте Django-проект на VPS в продакшен-режиме: виртуальное окружение, Gunicorn как systemd-сервис и Nginx в роли reverse proxy с раздачей статики, а также правильные DEBUG и ALLOWED_HOSTS.

Что понадобится

  • VPS на Ubuntu 22.04/24.04, Debian 11/12 или AlmaLinux/Rocky Linux 8/9 с доступом по SSH — под root или пользователем с sudo.
  • Домен, указывающий на IP сервера (подойдёт и просто IP-адрес, если домена пока нет).
  • Свой Django-проект — написанный локально или лежащий в git-репозитории, с файлом зависимостей requirements.txt.
  • Базовые навыки работы в терминале по SSH.
Совет: если конкретный стек не принципиален, в VMmanager 6 есть готовый рецепт Django — окружение Django + uWSGI + Nginx одним кликом (как войти в панель — статья «Как войти в VMmanager 6», полный каталог — «Готовые рецепты VMmanager 6»). Ниже — ручная установка с Gunicorn и systemd вместо uWSGI: она даёт больше контроля над процессом и сервисом.

Шаг 1. Установите системные пакеты

Подключитесь к серверу по SSH и установите Python с инструментами для виртуальных окружений, а также Nginx:

bash
# Ubuntu / Debian
sudo apt update
sudo apt install -y python3-venv python3-pip nginx
bash
# AlmaLinux / Rocky
sudo dnf install -y python3 python3-pip nginx
Совет: проверьте версию — python3 --version. Современным версиям Django нужен Python 3.10 или новее. На AlmaLinux/Rocky 8 системный python3 — версии 3.6, этого недостаточно: поставьте более новую версию (например, sudo dnf install -y python3.11) и в шаге 2 создавайте окружение командой python3.11 -m venv venv вместо python3 -m venv venv. На Ubuntu/Debian и AlmaLinux/Rocky 9 системный Python обычно уже подходит.
Проверьте: точный минимум версии Python для вашей версии Django — в release notes на docs.djangoproject.com, со временем это требование растёт.

Шаг 2. Создайте пользователя, скопируйте проект и настройте виртуальное окружение

Запускать веб-приложение от root не стоит. Создайте отдельного системного пользователя для проекта:

bash
# Ubuntu / Debian
sudo adduser django
bash
# AlmaLinux / Rocky
sudo useradd -m -s /bin/bash django
sudo passwd django
Совет: если под проект уже есть подходящий пользователь (например, тот, под которым вы заходите по SSH), используйте его — тогда просто подставляйте его имя вместо django во всех командах и файлах ниже.

Переключитесь на этого пользователя и разверните проект в его домашнем каталоге — так, чтобы manage.py лежал прямо в /home/django/myproject:

bash
sudo -iu django
git clone https://github.com/yourname/myproject.git myproject
cd myproject
Совет: если проекта пока нет в git-репозитории — просто скопируйте файлы на сервер через scp или rsync, в тот же каталог /home/django/myproject.

Создайте виртуальное окружение и активируйте его:

bash
python3 -m venv venv
source venv/bin/activate

Установите зависимости проекта и Gunicorn:

bash
pip install --upgrade pip
pip install -r requirements.txt
pip install gunicorn
Совет: если Gunicorn уже указан в requirements.txt, повторная установка ничего не изменит. Если проект использует базу данных, не забудьте также выполнить python manage.py migrate — настройка самой БД выходит за рамки этой статьи.

Шаг 3. Настройте DEBUG и ALLOWED_HOSTS

Django по умолчанию настроен для разработки, а не для продакшена. Прежде чем открывать сайт наружу, отредактируйте myproject/settings.py (вместо myproject — имя пакета настроек вашего проекта):

python
# myproject/settings.py
DEBUG = False

ALLOWED_HOSTS = ["example.com", "www.example.com", "203.0.113.10"]
Важно: оставлять DEBUG = True в продакшене нельзя — при ошибке Django покажет посетителю полный traceback вместе с фрагментами исходного кода, локальными переменными, настройками и списком установленных библиотек.

При DEBUG = False Django вообще не отвечает на запросы, если ALLOWED_HOSTS пуст, — этот список защищает сайт от запросов с подделанным заголовком Host. Укажите в нём:

  • домен(ы), по которым будет доступен сайт — например, example.com и www.example.com;
  • при необходимости — IP-адрес сервера;
  • поддомены можно разрешить одной маской, поставив точку в начале значения: .example.com подойдёт и для example.com, и для любого его поддомена;
  • значение * (разрешить любой Host) технически допустимо, но тогда проверку заголовка Host придётся реализовывать самостоятельно — для обычного сайта используйте конкретный список доменов/IP.

Шаг 4. Проверьте запуск через Gunicorn

Пока вы ещё в виртуальном окружении, запустите Gunicorn вручную — это самый быстрый способ убедиться, что проект вообще поднимается, прежде чем оборачивать его в systemd:

bash
gunicorn --bind 127.0.0.1:8000 myproject.wsgi:application

В соседней SSH-сессии проверьте ответ (вместо example.com подставьте значение, указанное в ALLOWED_HOSTS):

bash
curl -I -H "Host: example.com" http://127.0.0.1:8000/

Ожидаемый результат — HTTP/1.1 200 OK (или редирект, если ваш проект так настроен). Остановите Gunicorn сочетанием Ctrl+C.

Совет: ошибка DisallowedHost означает, что переданный заголовок Host не входит в ALLOWED_HOSTS (шаг 3). Ошибка ModuleNotFoundError: No module named 'myproject' обычно означает, что команда запущена не из каталога с manage.py — перейдите в /home/django/myproject и повторите.

Когда закончите проверку, выполните deactivate и exit — дальнейшие шаги (systemd, Nginx) выполняются от имени root или sudo-пользователя.

Шаг 5. Опишите Gunicorn как systemd-сервис

От имени root/sudo создайте юнит /etc/systemd/system/myproject.service:

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

[Service]
User=django
Group=django
WorkingDirectory=/home/django/myproject
ExecStart=/home/django/myproject/venv/bin/gunicorn \
          --workers 3 \
          --bind 127.0.0.1:8000 \
          myproject.wsgi:application
Restart=on-failure

[Install]
WantedBy=multi-user.target
  • User/Group — пользователь проекта из шага 2, не root.
  • --workers — число рабочих процессов Gunicorn; отправная точка — «(2 × число ядер) + 1», число ядер сервера показывает команда nproc.
  • Gunicorn запускается по полному пути внутри venv, а не системный — так гарантированно используется окружение с зависимостями именно этого проекта.

Примените и включите сервис:

bash
sudo systemctl daemon-reload
sudo systemctl enable --now myproject
sudo systemctl status myproject
Готово: если systemctl status показывает active (running) — Gunicorn поднялся как сервис: переживёт перезагрузку сервера и автоматически перезапустится при падении (Restart=on-failure).
Совет: логи сервиса — sudo journalctl -u myproject -f. Пригодится, если сайт не откроется и нужно быстро понять, в чём дело.

Шаг 6. Настройте Nginx: reverse proxy и раздача статики

Соберите статические файлы проекта. Вернитесь к пользователю проекта и активируйте виртуальное окружение:

bash
sudo -iu django
cd myproject
source venv/bin/activate

Добавьте в myproject/settings.py:

python
# myproject/settings.py
STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"

и выполните:

bash
python manage.py collectstatic

Django скопирует все статические файлы — свои и сторонних приложений, включая админку — в /home/django/myproject/staticfiles/. Эту папку Nginx будет отдавать напрямую, не трогая Gunicorn.

Выйдите обратно к root/sudo (deactivate, затем exit) и создайте файл конфигурации сайта — например, sudo nano /etc/nginx/sites-available/myproject на Ubuntu/Debian или sudo nano /etc/nginx/conf.d/myproject.conf на AlmaLinux/Rocky — со следующим содержимым:

nginx
server {
    listen 80;
    server_name example.com www.example.com;

    location /static/ {
        alias /home/django/myproject/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;
    }
}

Замените server_name на свой домен (или IP сервера) — то же значение должно быть в ALLOWED_HOSTS из шага 3.

На Ubuntu/Debian подключите сайт симлинком и уберите дефолтный:

bash
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default

На AlmaLinux/Rocky отдельно подключать файл не нужно — всё из /etc/nginx/conf.d/ читается автоматически.

Проверьте конфигурацию и перезапустите Nginx:

bash
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl enable --now nginx
Совет: на Ubuntu/Debian Nginx уже запущен и в автозагрузке сразу после установки — enable --now в этом случае ничего не сломает. На AlmaLinux/Rocky эта команда обязательна, иначе Nginx не поднимется после установки или перезагрузки сервера.
Важно: на AlmaLinux/Rocky по умолчанию включён SELinux, и он по умолчанию блокирует обращения Nginx как обратного прокси к порту 8000 — вместо сайта вы увидите 502 Bad Gateway. Разрешите такие соединения: sudo setsebool -P httpd_can_network_connect on.

Шаг 7. Откройте порт в файрволе

Если на сервере включён файрвол, сайт снаружи не откроется, пока порт 80 закрыт.

bash
# ufw (Ubuntu/Debian)
sudo ufw allow 80/tcp
bash
# firewalld (AlmaLinux/Rocky)
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
Важно: подробнее о настройке файрвола — в статье «Настройка файрвола UFW».

Шаг 8. Проверьте результат

Откройте домен (или IP сервера) в браузере — либо проверьте из терминала:

bash
curl -I http://example.com/

Ожидаемый ответ — HTTP/1.1 200 OK. Если на странице есть стили или картинки из /static/ (например, стили админки Django на /static/admin/), убедитесь, что они тоже загрузились, — это подтверждает, что Nginx правильно раздаёт STATIC_ROOT.

Готово: сайт открывается через Nginx, статика раздаётся напрямую из staticfiles/, а Gunicorn работает в фоне под systemd с DEBUG = False и настроенным ALLOWED_HOSTS.

Что дальше

  • Добавьте HTTPS (например, через Let's Encrypt/certbot) — текущий конфиг Nginx уже готов к тому, чтобы добавить поверх него блок listen 443 ssl;.
  • Если сервер будет обслуживать несколько проектов — заведите под каждый свой каталог, виртуальное окружение, systemd-юнит и серверный блок Nginx со своим server_name.
  • Другие готовые сценарии для VPS — в статье «Готовые рецепты VMmanager 6».