n8n и SQLite: как миграция на production сломала пути в БД

Как мы научили n8n доставлять настройки на сервер и не сломать БД
Всё началось с простой задачи в проекте ai-agents-admin-agent: нужно было развернуть рабочие потоки n8n на production-сервере. Звучит просто, но детали оказались коварными.
В чём была беда
После первого деплоя обнаружилось, что все SQLite-ноды в воркфлоу ищут БД по пути C:\projects\ai-agents\admin-agent\database\admin_agent.db — локальному Windows-пути из машины разработчика. На сервере Linux такого пути вообще нет. Результат: ошибка no such table: users при каждом запуске воркфлоу.
Плюс был ещё один сюрприз: пакет n8n-nodes-sqlite3 загружал прекомпилированный бинарник, несовместимый с версией Node.js на сервере. Пришлось отключить эти кэшированные бинарники и пересобрать better-sqlite3 с нуля.
Три варианта решения
Первое, что приходит в голову: просто заменить пути перед деплоем. Но какие пути использовать?
Вариант 1 — относительный путь (./data/admin_agent.db). Звучит мобильно, но это ловушка: относительный путь разрешается от рабочей директории процесса n8n. Где он запущен? Из Docker-контейнера, из systemd, из скрипта? Результат непредсказуем.
Вариант 2 — абсолютный путь на каждом окружении. Надёжнее, но нужна инициализация БД на сервере: скопировать schema.sql, запустить миграции.
Вариант 3 — использовать переменные окружения через n8n expressions ($env.DATABASE_PATH). Казалось идеально: путь разрешается в рантайме, без замены при деплое. Но в v2.4.5 n8n выяснилось, что это не работает: task runner-процесс изолирован, и переменные среды не проходят сквозь слои. Путь всё равно разрешался в Windows-версию.
Что в итоге сработало
Комбинированный подход:
-
В локальном
docker-compose.ymlдобавили переменнуюDATABASE_PATH=/data/admin_agent.db— для удобства локальной разработки. -
В
deploy.config.jsнастроили pathReplacements — при деплое скрипт проходит по всем 8 воркфлоу и заменяет выражение$env.DATABASE_PATHна абсолютный путь/var/lib/n8n/data/admin_agent.db. -
В деплой-скрипт добавили шаг инициализации:
deploy/lib/ssh.jsкопирует на сервер миграции (schema.sql,seed_questions.sql) и выполняет их через n8n API перед активацией воркфлоу.
Неожиданно выяснилось, что n8n кэширует старые версии воркфлоу: даже после обновления файла выполнение использовало старую ветку. Пришлось полностью пересоздавать воркфлоу через API, а не просто обновлять JSON.
Интересный факт о n8n
n8n хранит две версии каждого воркфлоу: stored (в БД) и active (загруженная в памяти). Когда вы обновляете workflow через API или UI, обновляется только stored-версия, а active может остаться со старыми параметрами. Это гарантирует, что текущие выполнения не прерываются, но может привести к ситуации, когда код и поведение не синхронизированы. Решение: перезапустить n8n или явно деактивировать и активировать воркфлоу.
Что получилось
Теперь деплой одной командой: node deploy/deploy-n8n.js --env .env.deploy. Воркфлоу создаются с правильными путями, БД инициализируется, всё работает. Плюс добавили миграции (ALTER TABLE users ADD COLUMN phone TEXT) — так что в будущем обновления БД-схемы будут безболезненными.
Главный урок: не полагайся на relative paths в Docker-контейнерах и на expressions в критических параметрах. Лучше заранее знать, где именно будет жить твоё приложение, и подставить правильный путь при деплое.
😄 Eight bytes walk into a bar. The bartender asks, “Can I get you anything?” “Yeah,” reply the bytes. “Make us a double.”
Метаданные
- Session ID:
- grouped_ai-agents-admin-agent_20260207_1858
- Dev Joke
- Eight bytes walk into a bar. The bartender asks, "Can I get you anything?" "Yeah," reply the bytes. "Make us a double."
Часть потока:
Разработка: ai-agents-admin-agent