Миграция n8n с SQLite на PostgreSQL: Практическое руководство
IVOLЗапускал два инстанса n8n (на портах 5678 и 5679) с SQLite, решил перейти на PostgreSQL для масштабирования и стабильности. Делюсь опытом полной миграции включая Data Tables.
TL;DR
- ✅ Credentials и Workflows - экспорт через UI
- ✅ Data Tables - ручная миграция через CSV (n8n не умеет их экспортировать)
- ⚠️ Ключевая ловушка: Foreign Key зависимости и Case-Sensitive имена таблиц в PostgreSQL
- ⏱️ Время: ~2 часа на два инстанса
Что нужно сделать чтобы переехать на PostgreSQL
1. Подготовка PostgreSQL
Создать отдельные контейнеры для каждого инстанса n8n:
# docker-compose.yml
services:
postgres_n8n_prod:
image: postgres:16
environment:
POSTGRES_USER: n8nuser
POSTGRES_PASSWORD: secure_password
POSTGRES_DB: n8ndb
volumes:
- postgres_prod_data:/var/lib/postgresql/data
ports:
- "5432:5432"
volumes:
postgres_prod_data:
2. Экспорт Credentials и Workflows
Через n8n UI:
- Settings → Import/Export
- Download workflows + credentials (JSON)
- Сохранить
encryption keyиз старого docker-compose.yml
Важно: Не удаляй старые SQLite БД до завершения миграции!
3. Запуск n8n с PostgreSQL
# docker-compose.yml для n8n
services:
n8n_prod:
image: n8nio/n8n
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres_n8n_prod
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: n8ndb
DB_POSTGRESDB_USER: n8nuser
DB_POSTGRESDB_PASSWORD: secure_password
N8N_ENCRYPTION_KEY: "твой_старый_ключ" # КРИТИЧНО!
4. Импорт Credentials и Workflows
- Пройти setup wizard (создать пользователя)
- Settings → Import/Export → Upload JSON
Результат: Workflows и credentials работают, но Data Tables пустые.
Миграция Data Tables (сложная часть)
Проблема
n8n создает для каждой Data Table физическую таблицу вида data_table_user_{tableId}, но UI не экспортирует их данные. Нужна ручная миграция.
Решение: CSV экспорт/импорт
Шаг 1: Узнать ID таблиц
sqlite3 /path/to/database.sqlite ".tables" | grep data_table_user # Вывод: data_table_user_aB3Cd4EfGh5IjKl6M, data_table_user_nO7Pq8RsTu9VwXy0Z, ...
Шаг 2: Экспорт структуры и данных из SQLite
# Экспорт метаданных sqlite3 database.sqlite -header -csv "SELECT * FROM data_table" > /tmp/data_table.csv sqlite3 database.sqlite -header -csv "SELECT * FROM data_table_column" > /tmp/data_table_column.csv # Экспорт данных каждой таблицы sqlite3 database.sqlite -header -csv "SELECT * FROM data_table_user_aB3Cd4EfGh5IjKl6M" > /tmp/dt_aB3Cd4EfGh5IjKl6M.csv # Повторить для всех таблиц из Шага 1
Шаг 3: Узнать новый projectId в PostgreSQL
docker exec -it postgres_n8n_prod psql -U n8nuser -d n8ndb -c "SELECT id FROM project;" # Вывод: xY9Za1Bc2De3Fg4Hi (новый ID, созданный при setup)
Шаг 4: Заменить старый projectId в CSV
# Найти старый projectId head -n 2 /tmp/data_table.csv # id,name,projectId,createdAt,updatedAt # aB3Cd4EfGh5IjKl6M,UserData,jK5Lm6Nn7Oo8Pp9Qq,"2024-10-18 02:29:49.383",... # Заменить на новый sed -i 's/jK5Lm6Nn7Oo8Pp9Qq/xY9Za1Bc2De3Fg4Hi/g' /tmp/data_table.csv sed -i 's/jK5Lm6Nn7Oo8Pp9Qq/xY9Za1Bc2De3Fg4Hi/g' /tmp/data_table_column.csv
Шаг 5: Импорт метаданных в PostgreSQL
cat /tmp/data_table.csv | docker exec -i postgres_n8n_prod psql -U n8nuser -d n8ndb -c "\COPY data_table FROM STDIN WITH (FORMAT CSV, HEADER)" cat /tmp/data_table_column.csv | docker exec -i postgres_n8n_prod psql -U n8nuser -d n8ndb -c "\COPY data_table_column FROM STDIN WITH (FORMAT CSV, HEADER)"
Шаг 6: Создать физические таблицы
# Узнать структуру из SQLite
sqlite3 database.sqlite ".schema data_table_user_aB3Cd4EfGh5IjKl6M"
# CREATE TABLE ... ("id" integer PRIMARY KEY, "user_id" TEXT, ...)
# Адаптировать для PostgreSQL
docker exec -i postgres_n8n_prod psql -U n8nuser -d n8ndb << 'EOF'
CREATE TABLE IF NOT EXISTS "data_table_user_aB3Cd4EfGh5IjKl6M" (
"id" SERIAL PRIMARY KEY,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"user_id" TEXT,
"session_id" TEXT
);
EOF
Важно: Имена таблиц в PostgreSQL Case-Sensitive! Используй двойные кавычки.
Шаг 7: Импорт данных
cat /tmp/dt_aB3Cd4EfGh5IjKl6M.csv | docker exec -i postgres_n8n_prod psql -U n8nuser -d n8ndb -c "\COPY \"data_table_user_aB3Cd4EfGh5IjKl6M\" FROM STDIN WITH (FORMAT CSV, HEADER)"
Шаг 8: Проверка
docker exec -it postgres_n8n_prod psql -U n8nuser -d n8ndb -c "SELECT COUNT(*) FROM \"data_table_user_aB3Cd4EfGh5IjKl6M\";"
Типичные ошибки и решения
1. Foreign Key constraint на projectId
Ошибка: Key (projectId)=(jK5Lm6Nn7Oo8Pp9Qq) is not present in table "project"
Решение: Заменить старый projectId на новый (см. Шаг 3-4)
2. Case-Sensitive имена таблиц
Ошибка: relation "data_table_user_ab3cd4efgh5ijkl6m" does not exist
Решение: Использовать двойные кавычки в SQL: "data_table_user_aB3Cd4EfGh5IjKl6M"
3. Пустые Data Tables не создаются
Проблема: n8n ждет существования таблиц, даже если они пустые
Решение: Создать таблицы вручную (Шаг 6) без импорта данных
Чеклист миграции
- Создать PostgreSQL контейнер
- Экспортировать workflows + credentials через UI
- Сохранить encryption key
- Запустить n8n с PostgreSQL
- Импортировать workflows + credentials
- Узнать список Data Tables (
\dtв SQLite) - Экспортировать metadata (data_table, data_table_column)
- Заменить projectId в CSV
- Импортировать metadata
- Создать физические таблицы
- Импортировать данные таблиц
- Проверить работу через n8n UI
- Удалить временные CSV
- Бэкап старой SQLite БД (на случай отката)
Итог
Плюсы миграции:
- Стабильность под нагрузкой
- Масштабируемость (репликация, шардинг)
- Лучшая производительность для больших Data Tables
Минусы:
- Ручная миграция Data Tables (UI не поддерживает)
- Больше инфраструктуры (отдельный контейнер для БД)
Время миграции: 2 часа на два инстанса (с 50+ credentials, 20+ workflows, 8 Data Tables)
Автоматизация (для следующего раза)
Написал bash-скрипт для автоматической миграции:
#!/bin/bash # migrate_n8n_sqlite_to_postgres.sh # TODO: Опубликую на GitHub после рефакторинга
P.S. Если у тебя меньше 5 Data Tables и данные не критичны - быстрее пересоздать их вручную через UI, чем мигрировать.
#n8n #PostgreSQL #DevOps #Automation #Migration #BuildInPublic