Обновление инфраструктуры — это рутина. Но когда нужно перевезти проект со старого стека, пропустив несколько мажорных версий софта, начинается самое интересное. Недавно я проводил жесткую миграцию n8n с легаси-связки сразу на актуальные версии: Traefik 3.3 и Postgres 16.
В этой статье поделюсь инструкцией по переносу, в которой учтены неочевидные баги несовместимости Traefik 3 с Docker API, политика безопасности свежего Postgres и конфликты ключей шифрования.
Шаг 1. Бэкап базы и спасение ключей
Первое правило миграции n8n — не забыть про ключ шифрования. Без него на новом сервере база поднимется, но все сохраненные учетные данные (Credentials) превратятся в тыкву.
-
Снимаем дамп базы в формате
customдля утилитыpg_restore:
docker exec -t postgres_db pg_dump -U root -d n8n -Fc > /tmp/n8n_backup.dumpdocker cp postgres_db:/tmp/n8n_backup.dump ./n8n_backup.dump
-
Вытаскиваем старый ключ шифрования. Это действие ОБЯЗАТЕЛЬНО, иначе отвалятся все доступы. Найти его можно в старом конфиге:
cat ~/.n8n/config | grep encryptionKey
-
Альтернативный вариант — найти переменную
N8N_ENCRYPTION_KEYв старом файлеdocker-compose.yml. -
После этого переносим полученный файл
n8n_backup.dumpна новый сервер.
Шаг 2. Развертывание и грабли Traefik 3.3 (Конфликт Docker API)
Создаем рабочую директорию и пишем docker-compose.yml.
Здесь кроется главная засада, с которой пришлось жестко повозиться. При попытке поднять стек Traefik v3.3 упорно отказывался видеть контейнеры и сыпал ошибками 404.
Оказалось, что роутер по дефолту обращался к Docker API как очень старый клиент (версии 1.24), в то время как современный сервер (демон Docker) требовал клиента версии не ниже 1.41. Возникал конфликт версий, из-за которого Traefik просто не мог получить список запущенных сервисов.
Лечится это добавлением одной строчки прямо в секцию environment для контейнера Traefik:
environment: - DOCKER_API_VERSION=1.41
Это заставило роутер забыть про дефолтный старый API и начать общаться с демоном Docker на актуальном языке. Как только Traefik переключился на версию 1.41, он тут же увидел запущенный контейнер n8n, прочитал его метки (labels), и ошибка 404 исчезла.
Ну и базовая сетевая гигиена: для надежности мы сажаем все контейнеры (Traefik, n8n и Postgres) в одну общую изолированную сеть n8n_net, чтобы они гарантированно видели друг друга без сбоев маршрутизации.
Итоговый конфиг выглядит следующим образом:
version: '3.8'volumes: db_storage: n8n_storage: traefik_certs:networks: n8n_net: name: n8n_netservices: postgres_db: image: postgres:16 container_name: postgres_db restart: always environment: - POSTGRES_USER=root - POSTGRES_PASSWORD=твой_пароль_от_базы - POSTGRES_DB=n8n volumes: - db_storage:/var/lib/postgresql/data networks: - n8n_net traefik: image: traefik:v3.3 container_name: traefik restart: unless-stopped command: - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - "--certificatesresolvers.myresolver.acme.tlschallenge=true" - "--certificatesresolvers.myresolver.acme.email=твоя_почта@mail.com" - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" environment: # Заставляем Traefik говорить с демоном на актуальном языке API - DOCKER_API_VERSION=1.41 ports: - "80:80" - "443:443" volumes: - traefik_certs:/letsencrypt - /var/run/docker.sock:/var/run/docker.sock:ro networks: - n8n_net n8n: image: docker.n8n.io/n8nio/n8n:latest container_name: n8n restart: always environment: - DB_TYPE=postgresdb - DB_POSTGRESDB_DATABASE=n8n - DB_POSTGRESDB_HOST=postgres_db - DB_POSTGRESDB_PORT=5432 - DB_POSTGRESDB_USER=root - DB_POSTGRESDB_PASSWORD=твой_пароль_от_базы # Строго старый ключ шифрования, иначе доступы выдадут "Needs first setup" - N8N_ENCRYPTION_KEY=твой_старый_ключ - N8N_HOST=твой_домен.com - N8N_PORT=5678 - N8N_PROTOCOL=https - NODE_ENV=production - WEBHOOK_URL=https://твой_[домен.com/](https://домен.com/) labels: - "traefik.enable=true" - "traefik.http.routers.n8n.rule=Host(`твой_домен.com`)" - "traefik.http.routers.n8n.entrypoints=websecure" - "traefik.http.routers.n8n.tls.certresolver=myresolver" volumes: - n8n_storage:/home/node/.n8n networks: - n8n_net depends_on: - postgres_db
Запускаем стек командой docker compose up -d.
Шаг 3. Восстановление базы и фикс прав (Postgres 16)
-
Закидываем дамп в контейнер базы и разворачиваем его:
docker cp n8n_backup.dump postgres_db:/tmp/docker exec -i postgres_db pg_restore -U root -d n8n -1 /tmp/n8n_backup.dump
-
Здесь возникает следующий критичный момент: Postgres 16 по умолчанию сильно урезает права на схему
public. -
Из-за этого ограничения n8n после запуска уйдет в цикличный ребут с ошибкой
permission denied for schema public. -
Проблема лечится выдачей полных прав владельцу базы:
docker exec -it postgres_db psql -U root -d n8n -c "GRANT ALL ON SCHEMA public TO root;"
Шаг 4. Фикс Credentials (ошибка «Needs first setup»)
Даже если вы правильно указали переменную N8N_ENCRYPTION_KEY в файле compose, при первом старте n8n генерирует новый локальный файл config внутри примонтированного volume.
В результате возникает конфликт: база поднимается, но сохраненные Credentials отказываются расшифровываться. Чтобы это исправить, необходимо удалить этот свежесозданный локальный конфиг. Тогда n8n принудительно возьмет ключ из переменных среды (environment). Удаляем и рестартанем контейнер:
docker exec -it n8n rm /home/node/.n8n/configdocker compose restart n8n
Шаг 5. Бонус: Фикс фоновых парсеров (SQLite Lock)
Этот пункт актуален для тех, у кого на сервере крутятся фоновые Python-скрипты (например, парсеры на Telethon) в виде systemd-служб.
После жесткого переезда они могут уйти в бесконечный рестарт из-за блокировки файла сессии. В логах будет висеть ошибка sqlite3.OperationalError: database is locked.
Решается это принудительным завершением зомби-процессов и удалением временных журналов SQLite. Сама .session при этом не пострадает, поэтому заново авторизовываться по номеру телефона в Telegram не придется:
apt install psmisc -yfuser -k -v /root/parser_folder/session_name.sessionrm -f /root/parser_folder/session_name.session-journalrm -f /root/parser_folder/session_name.session-walsystemctl restart имя_службы.service
Итоги
На этом миграция завершена. Переход на мажорные версии БД и реверс-прокси всегда сопровождается подводными камнями, но если следовать этому алгоритму, все интеграции внутри n8n поднимаются без необходимости перевыпускать токены и перенастраивать вебхуки.
P.S. Я профессионально занимаюсь автоматизацией бизнеса и проектированием сложных архитектур на n8n. Если ваш проект уперся в технический потолок или вы ищете специалиста для решения нетривиальных задач — стучитесь ко мне в Telegram. Открыт к предметному диалогу и интересным проектам.
ссылка на оригинал статьи https://habr.com/ru/articles/1025040/