Квантовый скачок n8n: миграция с Traefik 1.x и Postgres 11 на актуальный стек (Traefik 3.3 + PG 16) без потери данных

от автора

Обновление инфраструктуры — это рутина. Но когда нужно перевезти проект со старого стека, пропустив несколько мажорных версий софта, начинается самое интересное. Недавно я проводил жесткую миграцию 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/