Как опубликовать проект с базой данных

Локально проект с базой поднимается одной командой docker compose up, а публикация добавляет вопросы, которых до неё не было: где база будет жить, как приложение к ней подключится, где держать пароль и что случится с данными при следующем обновлении кода. Классический путь — арендовать сервер, поставить базу, настроить доступ и не забыть про бэкапы. На Dockbox приложение и база поднимаются как один проект из docker-compose, а данные лежат в постоянном хранилище и переживают обновления. Ниже — по шагам, где именно теряются данные и как этого не допустить.

Почему проект с базой тащат на VPS

Отдельная установка базы. PostgreSQL мало поставить — надо решить, где она хранит файлы, создать пользователя, выдать права и не забыть об обновлениях безопасности.

Доступ снаружи. Приходится либо открывать порт базы в интернет, либо настраивать туннель. База, торчащая наружу, — самый частый способ потерять данные.

Данные внутри контейнера. При обновлении кода проект пересобирается, и всё, что лежало не в том месте, стирается вместе с контейнером.

Пароли и бэкапы. Пароль в docker-compose.yml, строка подключения в коде, регулярных копий нет — обычный набор в самодельном деплое.

Платформа снимает эту часть: описание сервисов уходит как есть, база поднимается рядом с приложением, а выделенный том закрывает вопрос с обновлениями. Ни сервера, ни ручной установки, ни открытых наружу портов.

Шаг 1. Опишите приложение и базу в одном файле

Положите в корень проекта docker-compose.yml и опишите в нём два сервиса: приложение и базу — PostgreSQL, MySQL, MongoDB или Redis из готового официального образа. Приложение собирается из вашего кода, база — из образа, ставить и настраивать её вручную нечего.

services:
  app:
    build: .
    environment:
      DATABASE_URL: postgres://app:***@db:5432/app
  db:
    image: postgres:16
    volumes:
      - db_data:/var/lib/postgresql/data
volumes:
  db_data:

Шаг 2. Дайте базе постоянное хранилище

Это ключевой шаг, и пропускать его нельзя. Добавьте базе именованный том (db_data выше) и привяжите его к папке, где база хранит свои файлы: у PostgreSQL это /var/lib/postgresql/data. Без тома данные лежат внутри контейнера и стираются при первом же обновлении кода. С томом они переживают перезапуски и повторные публикации — такие данные удаляются только вместе с проектом.

Шаг 3. Соединяйте сервисы по имени, а не по адресу

Внутри проекта сервисы видят друг друга по именам из описания. Если база называется db, приложение подключается к хосту db на стандартном порту базы. IP-адреса здесь не нужны и вредны: при перезапуске они меняются, а имя остаётся прежним. И выпускать базу наружу незачем — публичный адрес получает только приложение, а база доступна лишь соседнему сервису внутри проекта.

DATABASE_URL=postgres://app:***@db:5432/app

Шаг 4. Пароль базы — в секреты, а не в файл

Не пишите пароль, пользователя и строку подключения прямо в docker-compose.yml и тем более в коде — читайте их из переменных окружения. Файл описания вы почти наверняка будете коммитить, поэтому секрет в файле = секрет утёк.

Плохой вариант:

environment:
  DATABASE_URL: postgres://app:secret@db:5432/app

Хороший — значение приходит извне, а код только читает его:

import os
DATABASE_URL = os.environ["DATABASE_URL"]

В настройках проекта эти значения задаются отдельно и хранятся зашифрованными: в панели видны имена переменных, а сами значения — никому.

Шаг 5. Миграции — при старте, а не вручную

Терминала внутри проекта нет, поэтому схему накатывают из стартовой команды до запуска сервера. Тогда таблицы появляются на первом запуске, а на следующих обновлениях данные остаются на месте.

python manage.py migrate --noinput && gunicorn config.wsgi:application --bind 0.0.0.0:$PORT

Проект попадает на платформу ZIP-архивом или импортом репозитория GitHub — приватный тоже подойдёт. Платформа видит описание сервисов, поднимает базу и приложение и выдаёт публичный HTTPS-адрес, а логи сборки и запуска видны в панели в реальном времени.

Шаг 6. SQLite или PostgreSQL

SQLite годится для небольшого проекта, но только если файл базы лежит в постоянной папке /data. Файл рядом с кодом пересоздаётся при каждой публикации, и все записи в нём теряются. У проектов с собственным docker-compose папки /data нет — там постоянное хранилище задаётся именованным томом из вашего файла.

PostgreSQL стоит брать, если данных станет больше или к базе будут обращаться несколько сервисов одновременно. Меняется немногое: в описании сервисов вместо папки появляется образ postgres, и та же строка DATABASE_URL указывает уже на хост db.

Сколько это стоит и как работают контейнеры

Администрировать сервер не нужно — контейнеры работают постоянно, без ограничения по часам и без «засыпания». Упавший контейнер платформа перезапускает сама.

Starter — 990 ₽/мес. 1 vCPU, 2 GB RAM, 20 GB SSD, один проект. Первый месяц бесплатный. Для приложения с базой этого достаточно, и лимита по времени работы у тарифа нет.

Pro — 2 490 ₽/мес. 2 vCPU, 4 GB RAM, до пяти проектов — если рядом живут база, кеш и воркер.

Team — 4 990 ₽/мес — для команд, которым нужен запас ресурсов и SLA.

Тарифы и подробности — на dockbox.host.

Частые вопросы

Пропадут ли данные при обновлении кода?

Нет, если у базы подключён именованный том. Обновление тогда пересобирает только приложение, а файлы базы остаются на месте. Без тома данные лежат внутри контейнера и стираются при первой же повторной публикации, поэтому этот шаг обязателен.

Какие базы можно запустить?

Любые, для которых есть готовый образ: PostgreSQL, MySQL, MariaDB, MongoDB, Redis и другие. Достаточно описать сервис в docker-compose.yml — устанавливать и настраивать вручную нечего.

Как приложение подключается к базе?

По имени сервиса из файла описания. Если база называется db, строка подключения указывает на хост db и стандартный порт базы. IP-адреса не нужны: при перезапуске они меняются, а имя остаётся прежним.

Будет ли база доступна из интернета?

Нет. Публичный адрес получает только приложение, а база видна лишь соседним сервисам внутри проекта. Так и надо: база, открытая наружу, — самый частый способ потерять данные.

Как прогнать миграции после деплоя?

Терминала внутри проекта нет, поэтому миграции выполняются при старте. Вызовите migrate в начале стартовой команды, перед запуском сервера, — так таблицы обновляются на каждом деплое, а не при первом визите пользователя.