SQLite между Windows и Linux: как не потерять данные при деплое

Когда SQLite на Windows встречает Linux: история одного деплоя
Проект ai-agents-admin-agent был почти готов к запуску на сервере. Восемь n8n-воркфлоу, собирающих и обрабатывающих данные, уже прошли тестирование локально. На машине разработчика всё работало идеально. Но только до того момента, когда мы выложили их на Linux-сервер.
Первый боевой запуск воркфлоу завершился криком ошибки: no such table: users. Логи были красноречивы — все SQLite-ноды искали базу данных по пути C:\projects\ai-agents\admin-agent\database\admin_agent.db. Локальный Windows-путь. На сервере такого вообще не существовало.
Первый инстинкт: просто заменить пути
Звучит логично, но дьявол, как всегда, в деталях. Я начал рассматривать варианты.
Вариант первый — использовать относительный путь типа ./data/admin_agent.db. Звучит мобильно и красиво, но это ловушка для новичков. Относительный путь разрешается от текущей рабочей директории процесса n8n. А откуда запущен n8n? Из Docker-контейнера? Из systemd? Из скрипта? Результат абсолютно непредсказуем.
Вариант второй — абсолютный путь для каждого окружения. Надёжнее, но требует подготовки на сервере: скопировать схему БД, запустить миграции. Более сложно, зато предсказуемо.
Я выбрал комбинированный подход.
Как мы это реализовали
Локально в docker-compose.yml добавил переменную окружения DATABASE_PATH=/data/admin_agent.db — чтобы разработка была удобной и воспроизводимой. Затем создал развёртывающий скрипт, который при деплое проходит по всем восьми воркфлоу и заменяет выражение $env.DATABASE_PATH на реальный абсолютный путь /var/lib/n8n/data/admin_agent.db.
Но первое время я попытался обойтись выражениями n8n. Логика казалась неубиваемой: задаёшь переменную в окружении, ссылаешься на неё в воркфлоу, всё просто. На практике выяснилось, что в n8n v2.4.5 таск-раннер не передавал переменные окружения в SQLite-ноду так, как ожидалось. Выражение хранилось в конфигурации, но при выполнении всё равно искал исходный Windows-путь.
Пришлось идти в лоб — строковые замены при деплое. Развёртывающий скрипт deploy/deploy-n8n.js перехватывает JSON каждого воркфлоу и подставляет правильный путь перед загрузкой.
Ещё одна подводная скала: n8n хранит две версии каждого воркфлоу — stored (в базе данных) и active (загруженная в памяти). Когда вы обновляете конфигурацию через API, обновляется только stored-версия. Active может остаться со старыми параметрами. Это сделано для того, чтобы текущие выполнения не прерывались, но создаёт рассинхронизацию между кодом и поведением. Решение: явная деактивация и активация воркфлоу после обновления.
Добавили в процесс и инициализацию БД: скрипт SSH копирует на сервер миграции (schema.sql, seed_questions.sql) и выполняет их через n8n API перед активацией воркфлоу. В будущем, когда потребуется изменить схему (например, добавить колонку phone в таблицу users), достаточно добавить миграцию — без пересоздания всей БД.
Итог
Теперь деплой сводится к одной команде: node deploy/deploy-n8n.js --env .env.deploy. Воркфлоу создаются с правильными путями, база инициализируется корректно, всё работает.
Главный урок: не полагайся на относительные пути в Docker-контейнерах и на runtime-выражения в критических параметрах. Лучше заранее знать, где именно будет жить твоё приложение, и подставить правильный путь при развёртывании. Это скучно, но предсказуемо.
GitHub — единственная технология, где «это работает на моей машине» считается достаточной документацией. 😄
Метаданные
- Session ID:
- grouped_C--projects-bot-social-publisher_20260207_1900
- Branch:
- main
- Dev Joke
- GitHub — единственная технология, где «это работает» считается документацией.
Часть потока:
Разработка: bot-social-publisher