К этому моменту наш проект filmsite — полноценный Django-сайт с каталогом фильмов, авторизацией, формами, правами доступа и тестами. Всё это время мы работали локально: runserver, 127.0.0.1:8000, только для себя.
С этого урока мы разворачиваем filmsite на реальном сервере — с собственным доменом и постоянной работой 24/7.
Важно понимать: деплой — это не одно действие, а цепочка логически связанных шагов. Если пропустить или не понять хотя бы один из них, дальше начнутся ошибки, которые будет сложно диагностировать. Именно поэтому мы разберём деплой в трёх уроках, а не одним большим куском.
Что мы сделаем в этом уроке:
- арендуем сервер и зарегистрируем домен;
- подключимся к серверу по SSH;
- установим базовое окружение;
- перенесём код проекта через Git;
- установим зависимости — включая развилку по базе данных.
Django и Gunicorn+Nginx мы настроим в следующем уроке (34), а HTTPS — в уроке 35.
Любой Django-сайт в интернете существует потому что:
- есть сервер с публичным IP-адресом;
- есть доменное имя, удобное для человека;
- домен «указывает» на IP сервера;
- на сервере запущено ПО, которое обрабатывает HTTP-запросы;
- Django-приложение отвечает на эти запросы.
Сегодня мы реализуем первые три пункта и подготовим сервер к запуску Django.
Начиная с этого урока материал будет учитывать два возможных состояния вашего проекта:
- Вариант A — SQLite. Вы прошли модули 1–8, но не переходили на PostgreSQL (или прошли только часть модуля 8 до PostgreSQL). База данных — файл
db.sqlite3внутри проекта. - Вариант B — PostgreSQL. Вы прошли модуль 8 полностью, включая переход на PostgreSQL.
Везде, где действия отличаются, будет пометка [Только PostgreSQL] или [Только SQLite]. Всё остальное — общее для обоих вариантов.
Перенос кода на сервер мы делаем одним способом для обоих вариантов — через Git. В отличие от подхода «архив + файловый менеджер», который иногда встречается в старых туториалах, Git даёт вам куда более удобный рабочий процесс на будущее: любое обновление сайта — это
git pushлокально иgit pullна сервере.
Для примера используем хостинг-провайдера Beget — у него понятная панель управления и адекватная документация. Логика деплоя одинакова для любого хостинга, меняются только названия кнопок в интерфейсе.
Переходим на сайт:
https://beget.com
Регистрируемся, выбирая облачные сервисы, указываем реальные почту и телефон.
VPS (Virtual Private Server) — виртуальный сервер с собственной операционной системой, IP-адресом и root-доступом. Для Django-проекта это оптимальный вариант: мы сами устанавливаем нужное ПО и гибко настраиваем сервер.
В панели Beget выбираем раздел VPS/VDS. Для учебного проекта filmsite достаточно минимальной конфигурации:
- самый дешёвый тариф;
- расположение сервера — любое;
- операционная система — Ubuntu 24.04.
Все команды в этом и следующих уроках рассчитаны именно на Ubuntu.
Нажимаем «Создать виртуальный сервер». Через некоторое время сервер будет создан, а на почту придёт письмо с:
- IP-адресом сервера;
- логином (обычно
root); - паролем.
В панели Beget переходим в раздел «Домены и поддомены» → вкладка «Регистрация доменов».
Вводим желаемое имя, например:
filmsite.rufilm-site.rufilmsite-project.ru
Проверяем свободно ли оно, покупаем, указывая реальные данные администратора домена (это важно юридически — домен принадлежит указанному администратору).
После покупки домен появится во вкладке «Мои домены».
У нас уже есть сервер с IP-адресом и домен с именем — но они пока не связаны. Наша задача — сказать интернету: «когда кто-то обращается к этому домену, нужно идти на этот IP-адрес».
В разделе «Мои домены» находим нужный домен → «Редактировать DNS».
Нас интересует запись типа A — она связывает домен и IP-адрес:
filmsite.ru → 123.123.123.123
В поле значения указываем IP вашего VPS-сервера из письма. Сохраняем изменения.
DNS — не мгновенная система. После сохранения записей изменения распространяются по сети обычно 3–4 часа, иногда до 24 часов. Это нормально.
ping filmsite.ruОбратите внимание: http/https не указываем — проверяется именно DNS-разрешение. Если вы видите ответы с IP-адресом вашего сервера — домен успешно делегирован.
Наш VPS — удалённый компьютер без физического доступа. Взаимодействие происходит по протоколу SSH (Secure Shell) — защищённый способ удалённого управления сервером через командную строку.
Мы будем использовать PuTTY — один из самых распространённых и простых вариантов для Windows:
https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html
macOS и Linux могут подключаться напрямую через встроенный терминал (ssh root@ваш_ip), без дополнительных программ.
В PuTTY в поле Host Name (or IP address) указываем IP вашего VPS, порт 22. Сохраняем сессию под именем, например, filmsite_server, чтобы не вводить данные каждый раз.
При первом подключении PuTTY покажет предупреждение о неизвестном ключе сервера — это нормально, нажимаем Accept.
Вводим логин (root) и пароль из письма (пароль не отображается при вводе — это нормально).
Если всё введено правильно — вы внутри Ubuntu-сервера.
Первое, что делаем на новом сервере — обновляем список пакетов:
sudo apt updateЭта команда не устанавливает ничего, а лишь обновляет информацию о доступных версиях пакетов.
[Оба варианта] — базовый набор:
sudo apt install python3-pip python3-dev python3-venv nginx -yЧто здесь устанавливается:
python3-pip— менеджер пакетов Python;python3-venv— создание виртуальных окружений;nginx— веб-сервер.
[Только PostgreSQL] — дополнительно:
sudo apt install libpq-dev postgresql postgresql-contrib build-essential libpython3-dev -yЧто здесь добавляется:
postgresql,postgresql-contrib— сама база данных;libpq-dev— драйвер для работы Django с PostgreSQL;build-essential,libpython3-dev— компилятор и заголовочные файлы, нужны некоторым Python-пакетам для сборки.
Nginx — это веб-сервер и reverse proxy (обратный прокси), который будет принимать HTTP-запросы пользователей и передавать их нашему Django-приложению.
В нашем проекте Nginx будет стоять перед Gunicorn:
Браузер пользователя
↓
Nginx
↙ ↘
статика Gunicorn
↓
Django
То есть Nginx будет выполнять две основные задачи:
- принимать запросы от пользователей и передавать запросы к Django через Gunicorn;
- самостоятельно отдавать статические и медиафайлы — CSS, JavaScript, изображения и другие файлы.
Сам Django не должен напрямую обслуживать весь внешний HTTP-трафик в production. Для этого используется связка Nginx → Gunicorn → Django.
После установки Nginx уже запущен. Проверяем в браузере:
http://IP_АДРЕС_СЕРВЕРА
Если видите страницу Welcome to nginx — сервер доступен, Nginx работает.
Пропустите этот шаг, если работаете с SQLite — переходите сразу к Шагу 8.
Подключаемся к консоли PostgreSQL от системного пользователя postgres:
sudo -u postgres psqlПриглашение вида postgres=# означает, что вы внутри консоли.
Создаём базу и пользователя для проекта:
CREATE DATABASE filmsite_db;
CREATE USER filmsite_user WITH PASSWORD 'strong_password_here';Настраиваем пользователя:
ALTER ROLE filmsite_user SET client_encoding TO 'utf8';
ALTER ROLE filmsite_user SET default_transaction_isolation TO 'read committed';
ALTER ROLE filmsite_user SET timezone TO 'UTC';Назначаем права:
GRANT ALL PRIVILEGES ON DATABASE filmsite_db TO filmsite_user;
ALTER DATABASE filmsite_db OWNER TO filmsite_user;Выходим:
\qБаза данных готова к использованию проектом.
На сервере уже есть папка /var/www/ — именно здесь принято размещать сайты:
cd /var/wwwПрежде чем переносить код, убедитесь, что в проекте есть актуальный список зависимостей. На локальном компьютере, находясь в активированном виртуальном окружении проекта:
pip freeze > requirements.txtПроверьте, что файл лежит в корне проекта (рядом с manage.py) и закоммичен в Git.
[Только PostgreSQL] Убедитесь, что в
requirements.txtприсутствуетpsycopg2-binary— драйвер для работы Django с PostgreSQL. Если вы устанавливали его локально при переходе на PostgreSQL в модуле 8,pip freezeподхватит его автоматически.
Если проект filmsite ещё не находится под контролем версий:
git init
git add .
git commit -m "Initial commit"Переходим на https://github.com → New repository → указываем имя (например, filmsite) → создаём публичный репозиторий → не добавляем README/.gitignore/лицензию (если они уже есть локально) → Create repository.
Копируем URL вида:
https://github.com/ваш_username/filmsite.git
git remote add origin https://github.com/ваш_username/filmsite.git
git branch -M main
git push -u origin mainВозвращаемся в SSH-сессию сервера, находясь в /var/www:
git clone https://github.com/ваш_username/filmsite.git filmsite
cd filmsiteНа сервере появится точная копия проекта, идентичная локальной версии.
Создаём и активируем виртуальное окружение:
python3 -m venv venv
source venv/bin/activateВ начале строки должно появиться (venv) — это подтверждение, что окружение активно.
Устанавливаем Gunicorn:
pip install gunicorn[Только PostgreSQL] — дополнительно драйвер PostgreSQL (если он ещё не подтянется автоматически из requirements.txt):
pip install psycopg2-binaryУстанавливаем все зависимости проекта:
pip install -r requirements.txtПроверяем:
pip listDjango и все библиотеки проекта filmsite должны появиться в списке.
К этому моменту у вас должно быть:
- ✅ VPS-сервер на Ubuntu 24.04, доступный по SSH;
- ✅ домен, делегированный на IP сервера (
ping filmsite.ruотвечает нужным IP); - ✅ Nginx запущен и отдаёт страницу приветствия по IP;
- ✅ [PostgreSQL] база данных и пользователь созданы, права назначены;
- ✅ код проекта склонирован в
/var/www/filmsiteи совпадает с локальной версией; - ✅ виртуальное окружение создано, все зависимости из
requirements.txtустановлены.
Django пока не настроен и не запущен — этим займёмся в следующем уроке.
Как только сайт будет запущен (уроки 34–35), любое изменение в проекте вы будете вносить так:
Локально:
git add .
git commit -m "Описание изменений"
git pushНа сервере:
git pullЕсли в изменениях были новые модели или правки, влияющие на базу данных — не забывайте выполнять миграции после git pull:
python manage.py migrateЗабегая вперёд: после
git pullс изменениями в коде также нужно будет перезапускать Gunicorn, чтобы сервер подхватил новую версию кода — это частая причина путаницы «запушил, а на сайте всё по-старому». Разберём это подробно в чек-листе ошибок в уроке 35.