SQLite потерял данные после деплоя: где хранить базу

Меняете пару строк в коде, загружаете новую версию — и приложение пустое: зарегистрированные пользователи, сохранённые заказы, собранная статистика исчезли. Кажется, что хостинг сломался, но ломаться там нечему: при каждой сборке проект разворачивается заново из тех файлов, которые вы загрузили. Разбираем по шагам, как убедиться, что причина именно в этом, как перенести файл базы в постоянное хранилище и в каких случаях пора менять SQLite на полноценную базу.

Симптом: база была, после пересборки — пустая

Данные исчезают ровно в момент деплоя или перезапуска. Между обновлениями приложение работает нормально, копит записи, а в момент, когда уходит новая версия, всё возвращается в исходное состояние. Такой тайминг — главный признак, что дело не в самой базе, а в том, где лежит её файл.

Проверьте, куда приложение пишет файл базы. Если путь — это просто имя файла (app.db, db.sqlite3) без папки, значит база лежит рядом с кодом и живёт ровно до следующей сборки.

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

Причина: файл базы — часть кода

Любая платформа, которая пересобирает проект из загруженного кода, работает именно так. Пересборка — это не «обновление файлов поверх», а разворачивание проекта заново, поэтому всё, что приложение записало рядом с кодом, возвращается к состоянию на момент загрузки. Постоянным остаётся только то, что сделано постоянным специально.

На Dockbox постоянное хранилище — это папка /data. Всё, что пишется туда, переживает и обновление кода, и перезапуск контейнера. Файлы из /data видны на вкладке «Данные» в панели: их можно скачать, загрузить обратно или удалить. Папка существует с первого запуска, создавать её не нужно.

Всё остальное — временное. Файлы, созданные вне постоянного хранилища, при следующем деплое появятся заново и пустыми. Ссылка на проект при этом не меняется.

Как правильно: путь к базе в /data через переменную окружения

Плохой вариант — путь захардкожен, файл создаётся рядом с кодом:

import sqlite3
conn = sqlite3.connect("app.db")

Хороший вариант — путь берётся из переменной окружения и указывает в постоянную папку:

import os
import sqlite3
DB_PATH = os.environ.get("DATABASE_PATH", "/data/app.db")
conn = sqlite3.connect(DB_PATH)

Почему через переменную, а не жёстко прописанный путь. С одной переменной приложение одинаково запускается и у вас на ноутбуке, и на сервере: локально подставляется файл рядом с проектом, на платформе — путь в /data. Переменные окружения задаются в панели и передаются в контейнер при каждом запуске, так что править код перед деплоем не придётся.

Указывайте путь именно внутри постоянной папки, например /data/app.db: файл базы создастся сам при первом обращении, и с этого момента данные переживут обновления.

Если у проекта свой docker-compose

У проекта, который описывает сервисы собственным docker-compose, папки /data нет — постоянное хранилище там задаётся именованными томами (named volumes) в самом файле. Примонтируйте том в папку, где лежит база, и направьте путь к файлу внутрь него.

Если в один файл базы пишут несколько сервисов, это повод вынести базу отдельным сервисом, а не делить один файл между процессами.

Как спасти уже потерянное

Постоянное хранилище защищает от пересборки, но не от ошибок в вашем коде. Сломанная миграция или случайное удаление строк испортят данные так же легко в /data, как и на выделенном сервере.

Перед большой правкой скачайте копию базы. Откройте вкладку «Данные», загрузите файл базы и сохраните его у себя. Если что-то пойдёт не так, тот же файл можно вернуть обратно. Загрузку делайте при остановленном проекте, чтобы приложение не писало в базу в момент замены файла.

Если копии нет, восстанавливать придётся из внешних источников. Смотрите дампы, сделанные из старого окружения, реплику на другом сервере, выгрузку из внешней системы. Когда база состояла из справочных данных, их проще создать заново кодом при первом запуске, чем искать в потерянном файле.

Когда SQLite пора менять на PostgreSQL

SQLite отлично справляется с одним процессом, который пишет время от времени. Личный сайт, Telegram-бот, дашборд, внутренний инструмент на несколько человек — этого достаточно, и отдельный сервис базы держать не нужно.

Переходить пора, когда пишут несколько процессов одновременно, запись идёт постоянно или данных становится много. Именно тогда появляются блокировки и ожидания, а в логах — database is locked.

Дальше два пути. Первый — поднять базу отдельным сервисом рядом с приложением через docker-compose и именованный том. Второй — подключить внешнюю базу и передать строку подключения через тот же секрет. Код при этом почти не меняется, если путь или строку подключения читали из переменной окружения с самого начала.

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

Starter — 990 ₽/мес, первый месяц бесплатно: 1 vCPU, 2 ГБ RAM, 20 ГБ SSD, один проект. Для бота или небольшого сайта с SQLite этого хватает с запасом. Pro — 2 490 ₽/мес: 2 vCPU, 4 ГБ RAM, до пяти проектов — вариант для стека из нескольких сервисов. Team — 4 990 ₽/мес.

Контейнеры на Dockbox работают постоянно. Лимита по часам работы и засыпания при простое нет: ссылка отвечает в момент открытия, без «прогрева». Платформа сама перезапускает упавший процесс, собирает проект из Git или архива, показывает логи в панели и выдаёт бесплатный HTTPS. Отдельного счёта за сборки и трафик нет, тариф действует месяц и сам не списывается, код остаётся у вас.

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

Почему база обнулилась при обновлении кода?

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

Как перенести уже заполненную базу со своего компьютера?

Задеплойте проект один раз, чтобы путь к базе на сервере совпал с нужным, затем откройте вкладку «Данные» и загрузите файл под тем именем, которого ждёт приложение. Проект на время загрузки лучше остановить, чтобы он не писал в базу, пока файл заменяется. При следующем старте приложение подхватит базу вместе со всеми данными.

Нужно ли класть файл базы в архив с кодом?

Нет. Файл, загруженный вместе с кодом, окажется рядом с кодом, а не в постоянном хранилище, и следующий деплой вернёт его ровно к тому состоянию, в котором вы его загрузили. База должна жить в /data, а если нужны стартовые данные, создавайте их кодом при первом запуске.

Что значит ошибка «database is locked»?

SQLite разрешает писать в базу только одному процессу за раз, поэтому ошибка появляется, когда писать пытаются одновременно несколько: воркеры веб-сервера или фоновая задача вместе с запросом. Уменьшите число пишущих процессов или разведите записи по времени. Если это мешает, пора переходить на отдельную базу.

Правило про постоянное хранилище касается только SQLite?

Нет, это общее правило: сохраняется лишь то, что лежит в постоянном хранилище. Загрузки пользователей, сгенерированные файлы, экспорты, кеши — всё это тоже должно писаться в /data или в именованный том. Любой файл рядом с кодом при следующей пересборке будет создан заново.