Tropic Host

Установка n8n на VPS в Docker Compose: self-hosted автоматизация для бизнеса без лимитов и санкций

39 мин чтения
Tropic

Краткий вывод: Перенос сценариев автоматизации на собственный сервер ликвидирует прогрессивный налог облачных SaaS-платформ (Make.com, Zapier), где тарификация привязана к числу атомарных шагов в итераторах, что при росте нагрузки раздувает бюджет до $1 000–$3 000 в месяц. Локальный инстанс фиксирует инфраструктурные расходы на уровне фиксированной аренды вычислительных мощностей ($15–$40/мес за 4 vCPU / 8 GB RAM), удерживает чувствительные данные (PII, CRM-лиды, токены API, финансовые проводки) внутри изолированного контура компании в рамках требований 152-ФЗ и GDPR, а также полностью нивелирует риски одномоментной блокировки аккаунтов со стороны зарубежных платежных шлюзов или вендоров.


Содержание

  1. Аппаратные требования и сайзинг: Почему бизнес и агентства переносят автоматизации на свой VPS с n8n
  2. Архитектура n8n: Single-instance vs Queue Mode с воркерами на Redis
  3. Сравнительная матрица: Self-Hosted n8n vs n8n Cloud vs Make.com vs Zapier
  4. Системные требования к KVM VPS: расчет памяти под PostgreSQL, Redis и рантайм
  5. Подготовка сервера и развертывание: готовый production docker-compose.yml
  6. Настройка вебхуков, домена и SSL: Nginx Reverse Proxy и Traefik
  7. Настройка вебхуков, домена и SSL: Nginx Reverse Proxy и Traefik
  8. Очистка истории и тюнинг БД: предотвращение разрастания диска до сотен гигабайт
  9. Резервное копирование воркфлоу и учетных данных: скрипт автоматического экспорта в S3
  10. Часто задаваемые вопросы (FAQ)

Аппаратные требования и сайзинг: Почему бизнес и агентства переносят автоматизации на свой VPS с n8n


Экономика вычислений: Фиксированный Compute против налога на операции

Ключевой архитектурный дефект SaaS-платформ автоматизации — монетизация каждого системного шага execution loop. В Make (бывший Integromat) или Zapier любая операция над данными тарифицируется отдельно: * HTTP-запрос к вебхуку CRM — 1 операция; * JSON-десериализация и фильтрация массива из 300 позиций складских остатков через Iterator — 300 операций; * Роутер на 3 ветки условий — еще 3 операции; * Отправка статуса в Telegram и запись в базу — 2 операции.

Один запуск сквозного сценария синхронизации заказа списывает со счета от 10 до 350 операций. При среднем коммерческом потоке в 500 заказов в сутки платформа сжигает 4,5–5,5 миллионов операций в месяц. На тарифных планах Zapier Company или Make Enterprise такой объем обходится бизнесу от $1 400 до $3 200 ежемесячно. Превышение месячного лимита приводит к немедленной постановке вебхуков на паузу (дроп трафика) либо к списанию за овердрафт по завышенным рейтам.

В противоположность SaaS-модели, грамотная установка n8n на vps docker compose переводит расходы из категории неконтролируемого Opex в предсказуемый фиксированный платеж за аппаратные ресурсы (Compute).

На стандартном KVM VPS с конфигурацией 4 vCPU, 8 GB RAM и NVMe-накопителем средняя себестоимость хостинга составляет от $15 до $35 в месяц. В связке с СУБД PostgreSQL 16 (с оптимизированным пулом shared_buffers = 2GB и work_mem = 64MB) n8n в режиме очередей (Queue Mode на базе Redis) обрабатывает до 120–160 execution/sec. Независимо от того, выполнит ли сервер 10 000 или 10 000 000 операций, стоимость вычислительного узла остается неизменной.

┌────────────────────────────────────────────────────────────────────────┐
│ СРАВНЕНИЕ TCO (TOTAL COST OF OWNERSHIP) ПРИ 3 000 000 ОПЕРАЦИЙ/МЕСЯЦ   │
├────────────────────────────────────────────────────────────────────────┤
│ Make.com Pro/Enterprise:   ~$1 200 – $1 800 / мес   (лимит операций)   │
│ Zapier Team/Company:       ~$2 400 – $3 100 / мес   (жесткие квоты)    │
│ VPS 4 vCPU / 8 GB NVMe:    ~$20 – $35 / мес         (unlimited ops)    │
└────────────────────────────────────────────────────────────────────────┘

Безопасность коммерческой тайны и изоляция PII

При использовании зарубежных cloud-платформ сквозной трафик компании проходит через чужую инфраструктуру. В момент исполнения сценария полезная нагрузка (payload) сохраняется во внешних логах платформы: 1. Персональные данные (PII): ФИО клиентов, номера телефонов, паспортные данные, адреса доставки и платежные реквизиты из чеков оседают в кластерах AWS us-east-1 или eu-central-1. 2. Секреты интеграций: Bearer-токены, OAuth-ключи от 1C, МойСклад, Bitrix24 и банковских эквайрингов передаются стороннему процессору. 3. Нарушение регуляторных требований: Прямое нарушение требований 152-ФЗ о первичной локализации баз данных граждан РФ на территории страны и жестких регламентов корпоративной безопасности (NDI/DPA).

Развертывание n8n в изолированном Docker-контуре гарантирует локальное хранение данных. База данных PostgreSQL и воркеры выносятся во внутреннюю сеть Docker без проброса портов во внешнюю среду:

networks:
  backend-net:
    internal: true # Полная изоляция от прямого входящего и исходящего трафика
  edge-net:
    driver: bridge

Все вебхуки принимаются обратным прокси (Nginx, Traefik или Caddy) с терминацией TLS 1.3, фильтрацией по IP-белым спискам и защитой от DDoS на уровне iptables/nftables. Внутренний трафик между n8n и CRM не покидает локального сетевого интерфейса хостинга или зашифрованного WireGuard-туннеля.


Санкционная независимость и защита от деградации каналов

Зависимость бизнес-процессов от зарубежных SaaS-сервисов несет в себе критические операционные риски: * Блокировки эквайринга: Невозможность продлить подписку корпоративной картой РФ через Stripe или Paddle. Отклонение транзакции через 7 дней льготного периода замораживает рабочие пространства, обрывая прием лидов из рекламы. * Территориальные баны: Блокировка аккаунтов по гео-IP или принудительное отключение компаний из РФ в одностороннем порядке без выгрузки накопленных сценариев. * Трансграничная задержка (p99 latency): Сетевые пакеты от российского сервера интернет-магазина до дата-центра Zapier в США и обратно собирают round-trip time (RTT) в 180–280 мс на каждый запрос. В длинных цепочках интеграций это приводит к таймаутам вебхуков со стороны платформ-отправителей (например, таймаут 5 секунд у большинства платежных шлюзов).

Инстанс n8n на собственном сервере полностью автономен. Экспорт конфигураций и рабочих процессов осуществляется штатными средствами CLI в версионируемый приватный Git-репозиторий:

# Автоматический бэкап всех активных сценариев в JSON
docker compose exec -u node n8n n8n export:workflows --all --output=/data/backup/workflows.json
docker compose exec -u node n8n n8n export:credentials --all --decrypted=false --output=/data/backup/credentials.json

В случае форс-мажора у хостинг-провайдера весь стек разворачивается из бэкапа на резервном VPS за 4–7 минут.


Сравнительная матрица: n8n на собственном VPS против SaaS-платформ

Архитектурный критерий SaaS: Make.com / Zapier Self-Hosted: n8n на VPS (Docker Compose) Инженерное преимущество n8n
Модель затрат Оплата за каждую операцию (Pay-per-step) Фиксированная аренда сервера (Pay-for-compute) Снижение расходов в 10–80 раз при высоких нагрузках
Затраты на 3 млн операций $1 200 – $3 100 / месяц $20 – $40 / месяц (VPS 4 vCPU / 8 GB RAM) Фиксация бюджета без рисков перерасхода
Локализация данных Облачные кластеры AWS/GCP (США, ЕС) Собственный сервер (любой ЦОД РФ или СНГ) Полное соответствие 152-ФЗ / GDPR, отсутствие утечек PII
Таймауты выполнения (Execution Time) 40–300 секунд (жесткий kill процесса) Без ограничений (настраивается в окружении) Выполнение тяжелых batch-импортов и ETL-скриптов
Лимиты на размер payload 5 МБ – 100 МБ на файл Лимитируется только объемом RAM/диска VPS Потоковая обработка PDF, архивов и дампов баз
Сетевая задержка (RTT / p99) 150 – 350 мс (трансграничные маршруты) 2 – 15 мс (внутри локального региона ЦОД) Мгновенный ответ вебхука (200 OK) без таймаутов
Устойчивость к санкциям Риск блокировки аккаунта, отказ карт 100% автономность, оплата хостинга в рублях Непрерывность ключевых бизнес-процессов
Кастомизация кода Ограниченный JS/Python без сторонних библиотек Доступ к npm-модулям, Python, bash, CLI утилитам Запуск произвольных бинарников, ffmpeg, pandas

Аппаратные требования и сайзинг хоста перед установкой

Для предотвращения деградации производительности движка Node.js и защиты от срабатывания механизма ядра OOM Killer виртуальная машина должна быть предварительно протестирована на оверселлинг.

Перед тем как начнется базовая установка n8n на vps docker compose, сервер валидируется по ключевым метрикам ядра Linux:

  1. CPU Steal Time (%st): Параметр оценивается через vmstat 1 или top. Значение %st не должно превышать 1.5–2% под нагрузкой. Более высокие значения означают, что гипервизор хостера перегружен «шумными соседями», что вызовет лавинообразный рост задержек в Node.js Event Loop.
  2. Дисковая подсистема (NVMe IOPS): Так как n8n при интенсивных вычислениях активно пишет журнал выполнения в PostgreSQL WAL (Write-Ahead Logging), диск обязан выдавать не менее 8 000–12 000 IOPS на случайную запись блоками по 4k. Тест выполняется через fio: bash fio --name=wal-test --ioengine=libaio --iodepth=16 --rw=randwrite --bs=4k --direct=1 --size=512M --runtime=30 --group_reporting
  3. Параметры ядра Linux (sysctl.conf): Для предотвращения зависания сетевых сокетов при пиковых нагрузках стек настраивается на уровне ОС: ```ini # Оптимизация сетевого стека под высокие объемы входящих вебхуков net.core.somaxconn = 4096 net.ipv4.tcp_max_syn_backlog = 8192 net.ipv4.tcp_fin_timeout = 15

# Предотвращение просадок при выделении виртуальной памяти СУБД vm.overcommit_memory = 1 vm.swappiness = 10 ```

Фиксация изоляции ресурсов на уровне cgroups в конфигурации docker-compose.yml (mem_limit: 4096m, cpus: 3.5) гарантирует, что даже при обработке некорректного вложенного цикла или утечке памяти в тяжелом сценарии инстанс сохранит доступность и продолжит штатную маршрутизацию трафика.

Архитектура n8n: Single-instance vs Queue Mode с воркерами на Redis

Выбор топологии рантайма n8n определяет устойчивость всей инфраструктуры автоматизации к всплескам трафика, утечкам памяти и блокировкам ввода-вывода. Движок n8n написан на TypeScript и функционирует внутри среды Node.js. Это накладывает фундаментальное архитектурное ограничение: по умолчанию вся логика — веб-сервер, парсинг входящих вебхуков, вычисление выражений, работа с базой данных и выполнение сценариев — исполняется внутри одного потока V8 Event Loop.

Когда планируется установка n8n на vps docker compose, инженер обязан заранее выбрать модель развертывания: монолитный Single-instance (Regular Mode) или распределенный Queue Mode с брокером сообщений Redis.

[ Single-Instance (Regular Mode) ]
Inbound Webhooks / UI ──► [ Nginx / Caddy ] ──► [ n8n Container (Node.js Single Process) ] ──► [ SQLite / PostgreSQL ]
                                                ├─ V8 Event Loop (UI, Webhooks, Cron)
                                                └─ Execution Engine (Синхронная блокировка)

[ Queue Mode (Decoupled Architecture) ]
Inbound Webhooks      ──► [ Traefik / Nginx ] ──► [ n8n Webhook Processors ] ──┐ (Zero execution)
UI & Management       ──► [ Traefik / Nginx ] ──► [ n8n Main Instance ]        ├─► [ Redis (BullMQ) ]
                                                                               │          │
                                                  ┌────────────────────────────┘          ▼
                                                  │                                [ Worker Pool ]
                                                  ▼                              ├─ Worker 1 (concurrency=10)
                                        [ PostgreSQL Cluster ] ◄─────────────────┼─ Worker 2 (concurrency=10)
                                                                                 └─ Worker N (scale on demand)

Анатомия узких мест Single-instance

В режиме по умолчанию (EXECUTIONS_MODE=regular) n8n поднимается единым контейнером. Архитектура процессов здесь выглядит следующим образом:

  • Webhook Receiver, Editor UI и Execution Engine делят один PID. Если сценарий выполняет ресурсоемкую синхронную операцию (например, узел Code обрабатывает массив на 50 000 объектов через сложные регулярные выражения или тяжелую сериализацию JSON), Event Loop блокируется.
  • Деградация latency p99: Время ответа на входящие HTTP-запросы мгновенно вырастает с штатных 10–15 мс до сотен миллисекунд или тайм-аута шлюза (504 Gateway Timeout на реверс-прокси).
  • Риск OOM (Out Of Memory): Node.js выделяет по умолчанию лимит кучи (heap size limit) около 1.4–2 ГБ (если не задан флаг --max-old-space-size). Обработка бинарного пейлоада в оперативной памяти или неконтролируемая утечка внутри кастомного JavaScript-скрипта приводит к срабатыванию внутреннего OOM в V8 либо к уничтожению контейнера механизмом ядра Linux (cgroup oom-killer, статус Exit 137). При падении контейнера в Single-instance отсекаются все активные вебхуки и обрываются параллельно идущие пайплайны.

Границы применимости: Single-instance на связке с внешним PostgreSQL надежно держит нагрузку до 50 000–100 000 легковесных операций в сутки (в среднем 1–3 RPS без выраженных пиков). Использование embedded SQLite на диске в production категорически не рекомендуется: под параллельной нагрузкой файловые блокировки ядра (fcntl/flock) приводят к ошибкам SQLITE_BUSY: database is locked.


Архитектура Queue Mode: изоляция отказов и горизонтальное масштабирование

Для production-систем с требованиями High Availability и трафиком от 5–10 RPS архитектура разделяется на функциональные роли через EXECUTIONS_MODE=queue. Оркестрация очередей делегируется Redis посредством библиотеки BullMQ.

В этой схеме компоненты разносятся по изолированным процессам и контейнерам:

  1. Main Instance (n8n): Обрабатывает трафик UI-редактора, компиляцию сценариев, аутентификацию, планировщик Cron (Schedule Trigger) и первичное управление стейтом. Не берет на себя выполнение задач по вебхукам.
  2. Webhook Instances (n8n webhook): Легковесные бессерверные обработчики. Принимают входящий HTTP POST/GET, валидируют заголовки, упаковывают входящий payload в задачу BullMQ внутри Redis со статусом waiting и немедленно отдают клиенту 200 OK или 202 Accepted. Их latency p99 остается стабильным (<8–12 мс) даже при пиковой нагрузке на воркеры.
  3. Worker Pool (n8n worker): Безголовые (headless) фоновые демоны. Они не открывают сетевых портов наружу, а подписываются на очереди Redis через механизм Redis Streams / Pub-Sub (BRPOPLPUSH/BLMOVE). Каждый воркер берет задачи параллельно в соответствии с параметром конкурентности (--concurrency).
  4. Redis: Хранит структуры очередей, стейты задач, pub/sub каналы для передачи промежуточного прогресса в UI через Server-Sent Events (SSE).
  5. PostgreSQL: Единый источник правды для долговременного хранения истории выполнений (execution_entity), конфигураций воркфлоу и метаданных пользователей.

Боевая конфигурация Docker Compose для Queue Mode

Ниже представлена production-конфигурация сервисов с разделением на роли, пробросом переменных окружения для Redis BullMQ и тюнингом пулов соединений:

version: "3.8"

services:
  redis:
    image: redis:7.2-alpine
    container_name: n8n-redis
    restart: unless-stopped
    command: >
      redis-server
      --appendonly yes
      --maxmemory 1024mb
      --maxmemory-policy noeviction
      --tcp-backlog 511
      --timeout 0
      --tcp-keepalive 300
    volumes:
      - redis_data:/data
    networks:
      - n8n_backend
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

  postgres:
    image: postgres:16-alpine
    container_name: n8n-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: n8n
      POSTGRES_USER: n8n_user
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - n8n_backend
    command: >
      postgres
      -c max_connections=250
      -c shared_buffers=1GB
      -c effective_cache_size=3GB
      -c work_mem=16MB
      -c maintenance_work_mem=256MB

  n8n-main:
    image: n8nio/n8n:latest
    container_name: n8n-main
    restart: unless-stopped
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n_user
      - DB_POSTGRESDB_PASSWORD=${DB_PASSWORD}
      - DB_POSTGRESDB_POOL_SIZE=50
      - EXECUTIONS_MODE=queue
      - QUEUE_BULL_REDIS_HOST=redis
      - QUEUE_BULL_REDIS_PORT=6379
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_DEFAULT_BINARY_DATA_MODE=filesystem
      - WEBHOOK_URL=https://n8n.example.com/
    volumes:
      - n8n_shared_data:/home/node/.n8n
    networks:
      - n8n_backend
    depends_on:
      redis:
        condition: service_healthy

  n8n-webhook:
    image: n8nio/n8n:latest
    restart: unless-stopped
    command: webhook
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n_user
      - DB_POSTGRESDB_PASSWORD=${DB_PASSWORD}
      - EXECUTIONS_MODE=queue
      - QUEUE_BULL_REDIS_HOST=redis
      - QUEUE_BULL_REDIS_PORT=6379
      - N8N_DEFAULT_BINARY_DATA_MODE=filesystem
      - WEBHOOK_URL=https://n8n.example.com/
    volumes:
      - n8n_shared_data:/home/node/.n8n
    networks:
      - n8n_backend
    depends_on:
      - n8n-main

  n8n-worker:
    image: n8nio/n8n:latest
    restart: unless-stopped
    command: worker --concurrency=10
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n_user
      - DB_POSTGRESDB_PASSWORD=${DB_PASSWORD}
      - DB_POSTGRESDB_POOL_SIZE=30
      - EXECUTIONS_MODE=queue
      - QUEUE_BULL_REDIS_HOST=redis
      - QUEUE_BULL_REDIS_PORT=6379
      - N8N_DEFAULT_BINARY_DATA_MODE=filesystem
    volumes:
      - n8n_shared_data:/home/node/.n8n
    networks:
      - n8n_backend
    depends_on:
      - n8n-main

networks:
  n8n_backend:
    driver: bridge

volumes:
  redis_data:
  db_data:
  n8n_shared_data:

Масштабирование воркеров под нагрузку осуществляется стандартной командой Docker Compose без необходимости перезапуска UI или вебхуков:

docker compose up -d --scale n8n-worker=4 --scale n8n-webhook=2

Системный тюнинг ядра Linux и Redis под Queue Mode

Использование BullMQ на базе Redis под высокой нагрузкой требует обязательной корректировки параметров подсистемы виртуальной памяти ядра (vm) и сетевого стека хоста. Без этого фоновые процессы сохранения базы на диск (BGSAVE) завершаются фатальными сбоями при нехватке памяти.

  1. Параметр перевыделения памяти (vm.overcommit_memory = 1):
    При выполнении BGSAVE Redis вызывает системный вызов fork(). Ядро Linux дублирует таблицу страниц родительского процесса через механизм Copy-on-Write (CoW). Если overcommit_memory выставлен в дефолтное значение 0, ядро блокирует форк при высоком потреблении ОЗУ, считая, что памяти для аллокации копии процесса недостаточно.
  2. Очередь соединений TCP сокетов (net.core.somaxconn):
    Значение по умолчанию (обычно 128) приводит к скрытому сбросу SYN-пакетов при резком наплыве входящих HTTP-вебхуков (SYN drops).

Примените параметры на хосте VPS:

# Применение параметров в рантайме
sudo sysctl -w vm.overcommit_memory=1
sudo sysctl -w net.core.somaxconn=1024
sudo sysctl -w fs.file-max=2097152

# Персистентная фиксация в /etc/sysctl.d/99-n8n-tuning.conf
cat << 'EOF' | sudo tee /etc/sysctl.d/99-n8n-tuning.conf
vm.overcommit_memory = 1
net.core.somaxconn = 1024
net.ipv4.tcp_max_syn_backlog = 2048
fs.file-max = 2097152
EOF

sudo sysctl --system

В конфигурации самого Redis для очередей BullMQ критично указать директиву maxmemory-policy noeviction. Если в условиях дефицита RAM Redis начнет удалять случайные ключи (volatile-lru или allkeys-lru), метаданные BullMQ повредятся, приведя к зависанию джоб в статусе active и потере транзакций.


Профиль нагрузки и принятие решения: Single vs Queue

  • Оставайтесь на Single-instance, если:
  • Инфраструктура ограничена виртуальной машиной 1–2 vCPU и 2–4 ГБ RAM (минимальный бюджет).
  • Объем транзакций не превышает 100 000 в сутки, а трафик распределен равномерно (отсутствуют спайки в десятки запросов за секунду).
  • Сценарии не содержат тяжелой обработки бинарных файлов (видео, PDF, архивы) внутри JavaScript узлов.
  • Падение сервиса на 30–60 секунд во время автоматического перезапуска контейнера не наносит критического ущерба бизнес-процессам.
  • Переходите на Queue Mode, если:
  • Трафик вебхуков имеет выраженный взрывной характер (bursty traffic): пачки по 50–200 запросов в секунду от платежных шлюзов, CRM или систем мониторинга.
  • Требуется изолировать UI-редактор от падений: инженер настраивает воркфлоу, не опасаясь, что параллельный воркер сожрет 100% CPU и заморозит браузерную сессию.
  • Задачи делятся на быстрые (уведомления) и долгие (парсинг выгрузок на 10 минут): воркеры с параметром --concurrency разгребают длинные очереди без деградации времени доставки легковесных событий.
  • VPS располагает ресурсами от 4 vCPU и 8 ГБ RAM, что позволяет выделить подсистемы под независимые лимиты cgroups (cpu.cfs_quota_us и memory.max).

Сравнительная матрица: Self-Hosted n8n vs n8n Cloud vs Make.com vs Zapier

Выбор инструмента оркестрации процессов определяет не только ежемесячный OPEX инфраструктуры, но и жесткие архитектурные границы пропускной способности (throughput), задержку сетевых вызовов (p99 latency) и уровень изолированности корпоративных данных.

В то время как SaaS-платформы (Zapier, Make.com, n8n Cloud) навязывают модель монетизации за каждый атомарный шаг сценария и выставляют искусственные лимиты на объем оперативной памяти и размер бинарных пейлоадов, развертывание собственного экземпляра снимает эти барьеры. Вектор масштабирования смещается исключительно в плоскость выделенных аппаратных ресурсов: количества vCPU, доступного дискового IOPS на массивах NVMe и пропускной способности сетевого стека ядра Linux.


Детальная архитектурно-техническая матрица

В таблице ниже сопоставлены ключевые эксплуатационные характеристики четырех платформ при работе под промышленными нагрузками:

Технический параметр Self-Hosted n8n (VPS Docker Compose) n8n Cloud (План Pro / Enterprise) Make.com (План Pro / Enterprise) Zapier (План Professional / Team)
Модель тарификации (TCO) Фиксированная стоимость VPS ($10–$40/мес) за неограниченное число шагов и вызовов Фиксированная подписка ($60–$600+/мес) с лимитом от 10k до 50k запусков Оплата за операции: $10–$300+/мес (каждый узел/роутер списывает 1 операцию) Оплата за «Tasks»: от $30 до $1 000+/мес (шаги фильтрации и триггеры тарифицируются)
Стоимость 1 000 000 операций/мес ~$20–$50 (аренда 4–8 vCPU / 16 GB RAM на Hetzner/Netcup) Индивидуальный Enterprise-контракт ($1 000+) ~$600–$900 (пакеты дополнительных операций) >$3 500 (критическая неэффективность на объемах)
Лимит параллельных потоков (Concurrency) Не ограничен со стороны ПО. Масштабируется горизонтально через Redis Queue 5–20 параллельных потоков (жесткий троттлинг на базовых планах) 3–40 одновременных сценариев (остальные встают в задержку) 1–5 потоков (очередь с интервалом опроса 1–15 минут)
Максимальное время выполнения (Timeout) Конфигурируется без ограничений (EXECUTIONS_TIMEOUT=-1) 300 секунд (жесткий лимит инстанса) 40–300 секунд (принудительный drop по таймауту) 30–120 секунд (аварийное завершение)
Кастомный рантайм кода (Python / Node.js) Полная свобода: Node.js (V8) + Python 3.x через системные бинарники и sub-processes Только изолированный JavaScript (ограниченная песочница VM2) Нет нативного Python; только внутренние формулы и базовый JS Sandboxed Python / JS с лимитом памяти 128 MB и таймаутом 10 сек
Подключение внешних библиотек (NPM / Pip) Доступны любые пакеты: numpy, pandas, axios, crypto-js через ENV-переменные Доступны только стандартные встроенные модули n8n Использование внешних сторонних SDK запрещено Изолированный список разрешенных библиотек (requests, datetime)
Обработка бинарных данных и хранилище Zero-Memory Streaming: локальный NVMe через N8N_DEFAULT_BINARY_DATA_MODE=filesystem Внутренний лимит S3-хранилища (500 MB – 5 GB на инстанс) Ограничение размера файла: 5 MB (Free) до 100 MB (Enterprise) Ограничение пейлоада: 30 MB (drop при обработке тяжелых CSV/PDF)
Доступ к локальным БД и приватным контурам Прямой: Docker Bridge/Overlay, локальные сокеты, приватные VPC-адреса Только через Public IP, NAT Gateway или обратный SSH-туннель Требуется настройка Make On-Premise Agent или публичный white-list IP Только через публичные эндпоинты с белыми IP или Webhook Relay
Сетевая задержка (Latency p99 к локальной БД) < 1.2 мс (Docker bridge loopback) 80–250 мс (TLS-handshake + транзит через публичный интернет) 100–350 мс (задержка стороннего SaaS-роутинга) 120–400 мс (множественные REST-прослойки)

Экономика масштабирования и предел пропускной способности (TCO & Throughput)

Главный инженерный недостаток Zapier и Make.com — линейный рост стоимости инфраструктуры при увеличении входящего трафика. Если сценарий парсит массив из 5 000 строк и отправляет каждую запись в CRM отдельным узлом, Make спишет 5 000 операций за один запуск, а Zapier потратит 5 000 тасков. На объемах от 500 000 событий в месяц SaaS-решения становятся экономически нецелесообразными.

Грамотная установка n8n на vps docker compose переводит модель расходов из переменной в строго фиксированную. Виртуальный сервер с 4 выделенными ядрами KVM и 8 ГБ RAM без труда утилизирует до 2,5–3 миллионов выполнений простых вебхуков в месяц. При этом критически важно отслеживать метрику CPU Steal Time (%st в выводе top или mpstat -P ALL 1). Если хостер допускает оверселлинг физических ядер процессора (%st > 3%), задержка диспетчеризации Node.js Event Loop резко возрастает, что приводит к деградации обработки асинхронных вызовов.

# Проверка паразитной нагрузки на CPU (Steal Time) и задержки ввода-вывода (iowait)
vmstat 1 5
# Анализ дисковой задержки NVMe при пиковой записи журнала executions
iostat -xz 1 5

Архитектура очередей: Webhook Ingress против троттлинга SaaS

SaaS-системы реагируют на резкие всплески сетевого трафика жестким дропом соединений и кодами 429 Too Many Requests. У n8n Cloud порог параллелизма зафиксирован на уровне тарифного плана — при его превышении новые вызовы встают в блокирующий стек либо сбрасываются.

В селф-хост архитектуре эта проблема решается переключением режима оркестратора из стандартного EXECUTIONS_MODE=regular в режим очередей EXECUTIONS_MODE=queue с выносом состояний в Redis.

flowchart LR
    A["HTTP Webhook Ingress (Burst 10k req/s)"] --> B["Traefik / Nginx (Edge Proxy)"]
    B --> C["n8n Main (Webhook Receiver)"]
    C -->|Zero Payload Loss| D[("Redis 7.x Cluster (BullMQ)")]
    D --> E["n8n Worker 01 (Task Runner)"]
    D --> F["n8n Worker 02 (Task Runner)"]
    E --> G[("PostgreSQL 16 Engine")]
    F --> G

Для стабильной утилизации всплесков сетевых пакетов на уровне ядра хост-машины тюнятся параметры сетевого стека в /etc/sysctl.d/99-network-throughput.conf:

# Увеличение длины очереди входящих пакетов для сокетов
net.core.somaxconn = 8192
net.ipv4.tcp_max_syn_backlog = 8192

# Выделение памяти под буферы TCP-соединений (min default max)
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# Разрешение выделения виртуальной памяти без немедленного сбоя (критично для Redis fork)
vm.overcommit_memory = 1

Управление оперативной памятью: NVMe Streaming против V8 OOM Killer

При обработке бинарных объектов (дампы баз данных, сжатые архивы, файлы выгрузок объемом 500+ МБ) Make и Zapier завершают сценарий с ошибкой переполнения буфера, так как их рантайм кодирует бинарники в строки Base64 прямо в оперативной памяти.

В инсталляции n8n на собственном сервере эта уязвимость ликвидируется переводом движка на потоковую запись на диск: N8N_DEFAULT_BINARY_DATA_MODE=filesystem. В этом режиме входящий поток байтов сбрасывается в смонтированный том NVMe минуя кучу V8 JavaScript. Дополнительно предотвращается аварийное завершение процесса OOM Killer через лимиты cgroups и флаг памяти V8.

Фрагмент рабочего docker-compose.yml с изоляцией ресурсов и доступом к системному рантайму:

services:
  n8n-worker:
    image: n8nio/n8n:latest
    command: worker --concurrency=10
    restart: always
    environment:
      - EXECUTIONS_MODE=queue
      - QUEUE_BULL_REDIS_HOST=redis
      - QUEUE_BULL_REDIS_PORT=6379
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n_user
      - DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/db_password
      - N8N_DEFAULT_BINARY_DATA_MODE=filesystem
      - N8N_BINARY_DATA_STORAGE_PATH=/home/node/.n8n/binaryData
      # Расширение кучи V8 и разблокировка внешних библиотек
      - NODE_OPTIONS=--max-old-space-size=4096
      - NODE_FUNCTION_ALLOW_EXTERNAL=axios,lodash,crypto-js,pg,redis
      - NODE_FUNCTION_ALLOW_BUILTIN=fs,path,child_process,crypto
    volumes:
      - n8n_storage:/home/node/.n8n
      - /opt/scripts/python:/opt/scripts/python:ro
    deploy:
      resources:
        limits:
          cpus: '4.00'
          memory: 4096M
        reservations:
          cpus: '1.00'
          memory: 1024M
    networks:
      - internal_backend

networks:
  internal_backend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.24.0.0/16

volumes:
  n8n_storage:
    driver: local

Сетевая безопасность и работа с локальными СУБД

Интеграция облачных сервисов (Make, Zapier) с внутренними источниками данных компании (PostgreSQL, ClickHouse, Redis, RabbitMQ) требует либо открытия порта 5432/6379 наружу на 0.0.0.0/0 с добавлением подсетей провайдера в белые списки файрвола, либо поднятия туннелей (Cloudflare Tunnels, WireGuard). Это расширяет поверхность атаки и добавляет сетевой оверхед: задержка каждого запроса (p99) колеблется в районе 80–250 мс из-за удаленности дата-центров SaaS.

Развертывание n8n в рамках единого Docker Compose или частной подсети VPC позволяет узлам обращаться к реляционным базам данных через внутренние DNS-имена Docker (postgres:5432) со средней задержкой < 1 мс. Конфиденциальные данные, токены авторизации и дампы таблиц не покидают периметр безопасности сервера, исключая риски компрометации на стороне сторонних облачных операторов.

Системные требования к KVM VPS: расчет памяти под PostgreSQL, Redis и рантайм

При развертывании автоматизаций корпоративного уровня попытка запустить стек на минималистичном сервере за $3 в месяц гарантированно приводит к отказам под нагрузкой. Качественная установка n8n на vps docker compose требует строгого понимания профиля потребления ресурсов каждым компонентом системы: Node.js/V8, СУБД PostgreSQL, брокером задач Redis и ядром Linux.


1. Аппаратный baseline: 2 vCPU, 4 GB RAM и 30 GB NVMe

Архитектурный минимум для продуктивного инстанса — 2 vCPU, 4 GB RAM и 30 GB NVMe с полной аппаратной виртуализацией KVM.

Контейнеризация на базе OpenVZ или LXC недопустима: общая разделяемая память с хостом и урезанный доступ к системным вызовам ядра (clone(), управление пространствами имен cgroups v2, манипуляции с swap) приводят к непредсказуемым падениям Node.js при пиках потребления.

Ключевой параметр на уровне процессора — процент процессорного времени, украденного гипервизором (CPU Steal Time, %st). Если хостер допускает жесткий оверселлинг ядер, показатель %st превышает 3–5%, что вызывает задержки планировщика событий Node.js (Event Loop Lag) и приводит к сбросу сетевых тайм-аутов HTTP-запросов.

Проверить текущее состояние виртуализации и кражу тактов процессора на работающем сервере следует командой:

vmstat 1 5
# Обратите внимание на последнюю колонку 'st' (steal time)
# Значение выше 3% свидетельствует о непригодности хоста для очередей n8n

2. Крах встроенного SQLite: анатомия блокировки базы данных

По умолчанию n8n поставляется с файловой базой данных SQLite. Это допустимо исключительно для локальной разработки или тестовых сценариев с линейным запуском сценариев раз в час.

В production-среде SQLite становится главным узким местом из-за механизма блокировок:

  1. Модель Single-Writer: Несмотря на поддержку режима WAL (Write-Ahead Logging), SQLite допускает наличие ровно одного активного пишущего процесса в конкретный момент времени на уровне файла базы данных.
  2. Ошибки SQLITE_BUSY: database is locked: Когда на вход поступает пачка параллельных вебхуков (например, 20–30 входящих HTTP-запросов в секунду от платежного шлюза или CRM), n8n пытается параллельно зафиксировать статус выполнения воркфлоу (execution_entity). Время ожидания освобождения файлового дескриптора (busy_timeout) истекает, вызывая фатальный сбой транзакции.
  3. Раздувание очереди в RAM: Не сумев сбросить состояние на диск, среда исполнения V8 накапливает контекст незавершенных сценариев в оперативной памяти. Происходит лавинообразный рост RSS (Resident Set Size), после чего срабатывает OOM Killer ядра Linux и принудительно завершает процесс Node.js по сигналу SIGKILL.

Использование внешней СУБД PostgreSQL 16+ с пулом соединений — безальтернативное требование для production.


3. Профиль дисковой подсистемы: NVMe, fsync() и задержки p99

n8n генерирует преимущественно транзакционную нагрузку с интенсивной случайной записью короткими блоками (4 KB – 16 KB).

Каждый запуск воркфлоу выполняет цепочку операций: * Запись метаданных входящего вебхука; * Фиксация состояния узлов при промежуточном сохранении; * Сброс журнала транзакций в PostgreSQL (WAL flush); * Синхронизация дискового кэша через системный вызов fsync().

На стандартных HDD или медленных сетевых SAS/SATA SSD задержка вызова fsync() (p99 latency) возрастает с нормальных <1.5 мс до сотен миллисекунд. В результате воркеры n8n блокируются в состоянии uninterruptible sleep (D-state), ожидая завершения дискового ввода-вывода (I/O wait %wa в top).

Для замера реальной задержки записи на дисковой подсистеме VPS выполните тест утилитой fio:

fio --name=kvm_fsync_test --filename=/var/lib/docker/volumes/test_bench.tmp \
    --size=512M --rw=randwrite --bs=4k --ioengine=sync --direct=1 \
    --fsync=1 --runtime=20 --time_based --group_reporting

Если по результатам теста показатель задержки clat (p99) превышает 5.00 мс, дисковая подсистема не обеспечит стабильную работу очередей n8n под нагрузкой.


4. Матрица распределения оперативной памяти и сайзинга

Для стабильной работы стека на базе 4 GB RAM критически важно жестко разграничить аллокацию памяти между контейнерами, оставив запас ядру под буферы ввода-вывода и системные демоны.

Компонент / Сервис Минимальный лимит (Хост 4 GB) Оптимальный лимит (Хост 8 GB) Механизм ограничения Назначение и риски утечки
n8n Main / Worker 1536 MB 3072 MB mem_limit + --max-old-space-size Выделяется под V8 heap. Без флага --max-old-space-size Node.js не запускает GC вовремя и падает по OOM.
PostgreSQL 16 768 MB 2048 MB mem_limit + shared_buffers shared_buffers = 256MB/512MB, остальное — под work_mem для сортировок и буфер соединений.
Redis 7 (BullMQ) 256 MB 512 MB mem_limit + maxmemory Хранение состояний очередей. Требует maxmemory-policy noeviction, иначе BullMQ потеряет джобы.
Traefik / Nginx 128 MB 256 MB mem_limit SSL-терминация, кэширование статики, буферизация клиентских запросов.
Host OS / Page Cache 1300 MB 2000 MB Резерв ядра ОС Page cache для файловых операций СУБД, systemd, sshd, Docker daemon.

5. Практическая конфигурация ресурсов в Docker Compose и ядре

Для предотвращения падения хоста при неконтролируемом росте нагрузки необходимо зафиксировать лимиты в манифесте docker-compose.yml и скорректировать параметры sysctl на хосте.

Настройка системных параметров ядра (/etc/sysctl.d/99-n8n.conf)

Redis использует механизм fork() для сохранения снапшотов RDB на диск. При нехватке свободной виртуальной памяти bgsave завершается аварийно, если выключен overcommit. Снижение агрессивности своппинга сохранит отзывчивость базы данных:

# Разрешить аллокацию памяти сверх физической для безопасного fork() в Redis
vm.overcommit_memory = 1

# Минимизировать сброс страниц Node.js и Postgres в swap
vm.swappiness = 10

# Увеличить размер очереди входящих TCP-соединений
net.core.somaxconn = 1024

Примените изменения:

sysctl --system

Конфигурация лимитов cgroups в docker-compose.yml

Для среды Node.js критично передать лимит памяти внутрь виртуальной машины V8 через переменную окружения NODE_OPTIONS, согласовав его с жестким ограничением cgroups v2 (limits.memory):

version: "3.8"

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    restart: always
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n_user
      - DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/db_password
      - EXECUTIONS_MODE=regular
      - NODE_OPTIONS=--max-old-space-size=1280
    deploy:
      resources:
        limits:
          cpus: "1.50"
          memory: 1536M
        reservations:
          cpus: "0.50"
          memory: 512M
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  postgres:
    image: postgres:16-alpine
    restart: always
    command:
      - "postgres"
      - "-c"
      - "shared_buffers=256MB"
      - "-c"
      - "work_mem=16MB"
      - "-c"
      - "maintenance_work_mem=64MB"
      - "-c"
      - "effective_cache_size=768MB"
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 768M
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n_user -d n8n"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    restart: always
    command: ["redis-server", "--maxmemory", "200mb", "--maxmemory-policy", "noeviction"]
    deploy:
      resources:
        limits:
          cpus: "0.50"
          memory: 256M
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

volumes:
  postgres_data:

Контроль за соблюдением лимитов в реальном времени осуществляется штатной утилитой:

docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}"

Если утилизация памяти контейнером n8n достигает 85–90% от установленного лимита 1536M, сборщик мусора V8 начинает потреблять до 100% выделенного CPU. В таких условиях необходимо масштабировать сервер до 8 GB RAM и переводить инстанс в режим распределенных воркеров (EXECUTIONS_MODE=queue).

Подготовка сервера и развертывание: готовый production docker-compose.yml

Грамотная установка n8n на VPS через docker compose требует изоляции сервисов на уровне ядра Linux, жесткого разграничения прав доступа (POSIX permissions) и тюнинга сетевого стека. Запуск контейнеров от пользователя root с хранением данных в неразмеченных каталогах хоста приводит к повреждению базы данных при рестартах демона Docker, конфликтам прав владения файлами (UID/GID mismatch) и рискам побега из контейнера при эксплуатации уязвимостей в Node.js рантайме.

1. Тюнинг ядра хоста и подготовка файловой структуры

Перед развертыванием контейнеров необходимо скорректировать параметры подсистемы виртуальной памяти (vm) и сетевые буферы через sysctl. Это предотвратит деградацию p99 latency при пиковых нагрузках на вебхуки и исключит внезапное аварийное завершение процессов брокера сообщений.

Для Redis критически важен оверкоммит памяти: при создании RDB-снапшотов через системный вызов fork() нехватка виртуальных страниц приведет к отказу аллокации. Внесите следующие директивы в /etc/sysctl.d/99-n8n.conf:

# Разрешение оверкоммита памяти для Redis background save (fork-safe allocation)
vm.overcommit_memory = 1

# Увеличение длины очереди сокетов для предотвращения сброса TCP SYN пакетов при спайках вебхуков
net.core.somaxconn = 1024

# Расширение диапазона эфемерных портов для исходящих HTTP-нод n8n
net.ipv4.ip_local_port_range = 10240 65535

# Увеличение лимита открытых дескрипторов файлов
fs.file-max = 2097152

Примените конфигурацию без перезагрузки ноды:

sudo sysctl --system

Официальный Docker-образ n8n собран на базе Alpine/Debian и исполняет процесс внутри контейнера от непривилегированного пользователя node с зафиксированным UID 1000 и GID 1000. Если примонтировать каталог хоста с правами root:root (0:0), n8n завершится с ошибкой EACCES: permission denied при попытке инициализации внутренней базы SQLite или записи ключей шифрования. В то же время процесс postgres:16-alpine использует внутри собственный UID 999 (или 70 в зависимости от апстрима), а redis:7-alpine — UID 999.

Создайте выделенную директорию /opt/n8n, изолированные подкаталоги монтирования и назначьте корректные права:

# Создание рабочей директории проекта
sudo mkdir -p /opt/n8n/{data,postgres_data,redis_data}

# Создание сервисного пользователя n8n на хосте без шелла и домашней директории
sudo groupadd -g 1000 n8n 2>/dev/null || true
sudo useradd -u 1000 -g 1000 -M -s /usr/sbin/nologin n8n 2>/dev/null || true

# Выставление прав владения для томов монтирования:
# 1000:1000 для воркспейса n8n
sudo chown -R 1000:1000 /opt/n8n/data
sudo chmod 750 /opt/n8n/data

# 999:999 для PostgreSQL (стандартный uid демона в образе)
sudo chown -R 999:999 /opt/n8n/postgres_data
sudo chmod 700 /opt/n8n/postgres_data

# 999:999 для Redis
sudo chown -R 999:999 /opt/n8n/redis_data
sudo chmod 700 /opt/n8n/redis_data

2. Конфигурация переменных окружения: безопасный .env

Хранение учетных данных внутри тела манифеста docker-compose.yml недопустимо. Создайте файл /opt/n8n/.env с правами доступа 600, исключающими чтение посторонними пользователями системы (chmod 600 /opt/n8n/.env).

Для генерации надежных криптографических ключей и паролей используйте системный генератор псевдослучайных чисел:

openssl rand -hex 32

Заполните /opt/n8n/.env production-параметрами:

# Сетевой периметр и домен
DOMAIN_NAME=n8n.example.com
SUBDOMAIN=n8n
N8N_HOST=n8n.example.com
N8N_PORT=5678
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.example.com/

# Таймзона контейнеров
GENERIC_TIMEZONE=UTC
TZ=UTC

# Безопасность и телеметрия
N8N_ENCRYPTION_KEY=c3ab87e21a09d3e8e19c0b56f84d123456789abcdef0123456789abcdef01234
N8N_DIAGNOSTICS_ENABLED=false
N8N_VERSION_NOTIFICATIONS_ENABLED=false
N8N_DISABLE_PRODUCTION_MAIN_PROCESS=false

# Прунинг и оптимизация базы данных (удержание p99 latency диска)
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=168
EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000
EXECUTIONS_DATA_SAVE_ON_ERROR=all
EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=false

# Параметры пула PostgreSQL
POSTGRES_USER=n8n_master
POSTGRES_PASSWORD=SECURE_PG_PASSWORD_REPLACE_ME_64_CHARS
POSTGRES_DB=n8n_production
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n_production
DB_POSTGRESDB_USER=n8n_master
DB_POSTGRESDB_PASSWORD=SECURE_PG_PASSWORD_REPLACE_ME_64_CHARS
DB_POSTGRESDB_POOL_SIZE=50

# Параметры очереди Redis
REDIS_PASSWORD=SECURE_REDIS_PASSWORD_REPLACE_ME_64_CHARS
QUEUE_BULL_REDIS_HOST=redis
QUEUE_BULL_REDIS_PORT=6379
QUEUE_BULL_REDIS_PASSWORD=SECURE_REDIS_PASSWORD_REPLACE_ME_64_CHARS
QUEUE_BULL_REDIS_DB=0
  • WEBHOOK_URL: обязан передаваться с явным протоколом и конечным слешем. Ошибки в этой строке вызывают генерацию невалидных callback-URL в сторонних API (Telegram, Stripe, GitHub).
  • N8N_DIAGNOSTICS_ENABLED=false: отключает сбор телеметрии, исключая сторонний исходящий трафик и фоновые треды Node.js.
  • EXECUTIONS_DATA_PRUNE: ротация данных исполнений. Без ограничения EXECUTIONS_DATA_MAX_AGE=168 (7 суток) и EXECUTIONS_DATA_SAVE_ON_SUCCESS=none таблица execution_entity за месяц разрастается до сотен гигабайт, приводя к деградации IOPS и блокировкам autovacuum в PostgreSQL.

3. Production манифест docker-compose.yml

Манифест конфигурирует изолированный стек с детерминированным порядком запуска через healthcheck, распределением по изолированным сетям (Bridge) и лимитами cgroups v2 (limits), что гарантирует защиту хоста от OOM-killer при утечках памяти в сторонних NPM-модулях n8n.

Создайте /opt/n8n/docker-compose.yml:

services:
  postgres:
    image: postgres:16-alpine
    container_name: n8n_postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
      PGDATA: /var/lib/postgresql/data/pgdata
    command: >
      postgres
      -c shared_buffers=256MB
      -c work_mem=16MB
      -c maintenance_work_mem=64MB
      -c effective_cache_size=768MB
      -c min_wal_size=1GB
      -c max_wal_size=4GB
      -c checkpoint_completion_target=0.9
      -c wal_buffers=16MB
      -c default_statistics_target=100
      -c random_page_cost=1.1
      -c max_connections=100
    volumes:
      - /opt/n8n/postgres_data:/var/lib/postgresql/data
    networks:
      - n8n_backend
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 5s
      timeout: 5s
      retries: 5
      start_period: 10s
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 1536M
        reservations:
          memory: 512M

  redis:
    image: redis:7-alpine
    container_name: n8n_redis
    restart: unless-stopped
    command: >
      redis-server
      --requirepass ${REDIS_PASSWORD}
      --maxmemory 512mb
      --maxmemory-policy noeviction
      --save 60 1
      --loglevel notice
      --tcp-backlog 511
      --timeout 0
      --tcp-keepalive 300
    volumes:
      - /opt/n8n/redis_data:/data
    networks:
      - n8n_backend
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 5s
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 768M
        reservations:
          memory: 256M

  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    container_name: n8n_app
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - N8N_HOST=${N8N_HOST}
      - N8N_PORT=${N8N_PORT}
      - N8N_PROTOCOL=${N8N_PROTOCOL}
      - WEBHOOK_URL=${WEBHOOK_URL}
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - TZ=${TZ}
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - N8N_DIAGNOSTICS_ENABLED=${N8N_DIAGNOSTICS_ENABLED}
      - N8N_VERSION_NOTIFICATIONS_ENABLED=${N8N_VERSION_NOTIFICATIONS_ENABLED}
      - N8N_DISABLE_PRODUCTION_MAIN_PROCESS=${N8N_DISABLE_PRODUCTION_MAIN_PROCESS}
      - EXECUTIONS_DATA_PRUNE=${EXECUTIONS_DATA_PRUNE}
      - EXECUTIONS_DATA_MAX_AGE=${EXECUTIONS_DATA_MAX_AGE}
      - EXECUTIONS_DATA_PRUNE_MAX_COUNT=${EXECUTIONS_DATA_PRUNE_MAX_COUNT}
      - EXECUTIONS_DATA_SAVE_ON_ERROR=${EXECUTIONS_DATA_SAVE_ON_ERROR}
      - EXECUTIONS_DATA_SAVE_ON_SUCCESS=${EXECUTIONS_DATA_SAVE_ON_SUCCESS}
      - EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=${EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS}
      - DB_TYPE=${DB_TYPE}
      - DB_POSTGRESDB_HOST=${DB_POSTGRESDB_HOST}
      - DB_POSTGRESDB_PORT=${DB_POSTGRESDB_PORT}
      - DB_POSTGRESDB_DATABASE=${DB_POSTGRESDB_DATABASE}
      - DB_POSTGRESDB_USER=${DB_POSTGRESDB_USER}
      - DB_POSTGRESDB_PASSWORD=${DB_POSTGRESDB_PASSWORD}
      - DB_POSTGRESDB_POOL_SIZE=${DB_POSTGRESDB_POOL_SIZE}
      - EXECUTIONS_MODE=regular
      - QUEUE_BULL_REDIS_HOST=${QUEUE_BULL_REDIS_HOST}
      - QUEUE_BULL_REDIS_PORT=${QUEUE_BULL_REDIS_PORT}
      - QUEUE_BULL_REDIS_PASSWORD=${QUEUE_BULL_REDIS_PASSWORD}
      - QUEUE_BULL_REDIS_DB=${QUEUE_BULL_REDIS_DB}
    volumes:
      - /opt/n8n/data:/home/node/.n8n
    networks:
      - n8n_backend
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2048M
        reservations:
          memory: 1024M

networks:
  n8n_backend:
    name: n8n_backend_net
    driver: bridge
    internal: false

4. Архитектурный разбор параметров манифеста

  1. Защита сетевого периметра: Порт приложения привязан к локальному интерфейсу обратной петли (127.0.0.1:5678:5678). Контейнеры postgres и redis не имеют директив ports, находясь исключительно во внутренней оверлейной/мостовой сети n8n_backend. Доступ извне к СУБД и кэшу через публичный IP VPS аппаратно заблокирован. Трафик к n8n должен поступать строго через обратный прокси (Nginx, Traefik или Caddy) с терминацией TLS.
  2. Детерминированность запуска (depends_on.condition): Применение условия service_healthy вместо стандартного condition: service_started предотвращает состояние гонки (race condition). Node.js процесс n8n не запускается до тех пор, пока pg_isready не вернет код 0, исключая падение n8n по таймауту миграции схемы БД TypeORM.
  3. Лимиты cgroups: Для контейнера n8n_app установлен жесткий потолок памяти 2048M. Если ресурсоемкий workflow попытается обработать бинарный файл (например, нераспакованный архив на 5 ГБ в оперативной памяти), cgroup OOM-killer уничтожит только изолированный процесс контейнера, не затрагивая ядро хоста и процесс СУБД.
  4. Оптимизация параметров PostgreSQL: Значение random_page_cost=1.1 подобрано под NVMe/SSD накопители современных VPS, ориентируя планировщик запросов на индексное сканирование вместо последовательного чтения (seq scan).

5. Инициализация и runtime-валидация стека

Запустите контейнеры в фоновом режиме:

cd /opt/n8n
docker compose up -d

Проверьте статус инициализации сервисов и валидность прохождения healthcheck:

docker compose ps

Все три контейнера должны находиться в состоянии Up (healthy). Если n8n_app находится в статусе restarting, изучите системный лог:

docker compose logs -f --tail=100 n8n

Финальная проверка производительности дисковой подсистемы хоста при активной записи журналов транзакций PostgreSQL:

# Мониторинг задержек ввода-вывода (IOPS, %util, await)
iostat -xz 1 5

# Мониторинг кражи процессорных тактов гипервизором (%st)
vmstat 1 5

Если колонка %st (Steal Time) в выводе vmstat стабильно превышает 3-5%, хостинг-провайдер агрессивно оверселлит vCPU, что вызовет спорадические таймауты сетевых запросов внутри воркфлоу n8n независимо от настроек Docker. Среднее время ожидания дисковых операций await в iostat для production-контура не должно превышать 5.0 ms.

Настройка вебхуков, домена и SSL: Nginx Reverse Proxy и Traefik

Когда выполняется установка n8n на vps docker compose, сам контейнер приложения по умолчанию слушает внутренний порт 5678/tcp в изолированном сетевом пространстве Docker (bridge). Прямая публикация этого сокета наружу (0.0.0.0:5678:5678) в боевом окружении недопустима: она открывает сырой HTTP-трафик без шифрования, блокирует работу современных браузерных API и ломает маршрутизацию внешних вебхуков.

Для промышленной эксплуатации требуется ingress-слой: Nginx или Traefik. На этом уровне решаются три критические задачи: TLS-терминация, корректная передача метаданных клиента через HTTP-заголовки и поддержка постоянных дуплексных соединений WebSocket.


Механика «вечного спиннера» и CORS: анатомия сбоя

Типичная проблема развертывания n8n за реверс-прокси — бесконечная анимация загрузки (spinner) в браузере при попытке входа в UI или открытии рабочего процесса. Это не сбой базы данных и не нехватка RAM, а результат разрыва контекста между Node.js бэкендом и браузерным клиентом.

Сбой вызывается двумя факторами:

  1. Разрыв WebSocket-хэндшейка (RFC 6455):
    Интерфейс n8n (начиная с перехода на шину событий push) использует постоянный транспорт для синхронизации состояния графа и статуса нод (/rest/push). Если прокси-сервер отправляет запрос upstream-контейнеру по протоколу HTTP/1.0 или не транслирует заголовки согласования протокола, upstream возвращает HTTP 200 OK или HTTP 400 Bad Request вместо обязательного HTTP 101 Switching Protocols. Браузерный сокет падает в таймаут, фронтенд не получает подтверждения готовности шины и блокирует рендеринг холста.
  2. Конфликт X-Forwarded-Proto и флагов Cookie:
    Когда трафик шифруется между клиентом и Nginx, но проксируется в контейнер по незащищенному HTTP, n8n обязан знать исходную схему запроса. Если заголовок X-Forwarded-Proto: https отсутствует или срезан:
  3. Node.js считает текущее соединение открытым (http://) и выставляет сессионную cookie n8n-auth без флага Secure либо с несовместимым SameSite=Lax.
  4. Браузер блокирует отправку таких cookie из-за несоответствия схем origin (https:// снаружи и http:// внутри).
  5. Механизм CSRF-валидации n8n падает с кодом 403 Forbidden, зацикливая аутентификационный роут на бесконечный редирект.

Чтобы исключить расхождение URL, переменные окружения контейнера в docker-compose.yml должны жестко фиксировать внешнюю точку входа:

environment:
  - N8N_HOST=n8n.example.com
  - N8N_PORT=5678
  - N8N_PROTOCOL=https
  - WEBHOOK_URL=https://n8n.example.com/
  - N8N_EDITOR_BASE_URL=https://n8n.example.com/
  - N8N_PUSH_BACKEND=websocket

Вариант 1: Nginx Reverse Proxy и автоматический Certbot

Классический стек на базе Nginx обеспечивает минимальный оверхед по памяти (около 15–20 МБ RSS на воркер) и минимальную задержку (p99 latency < 2 мс при обработке входящих вебхуков).

1. Тюнинг ядра хоста под высокий входящий поток вебхуков

При пиковых нагрузках (например, пакетные вызовы вебхуков от внешних CRM или платежных шлюзов) сокеты в состоянии TIME_WAIT быстро исчерпывают ephemeral-порты. Внесите изменения в /etc/sysctl.d/99-network-tuning.conf:

# Увеличение очереди соединений сокета
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# Быстрая утилизация сокетов TIME_WAIT для исходящих TCP-сессий
net.ipv4.tcp_tw_reuse = 1

# Расширение диапазона локальных портов
net.ipv4.ip_local_port_range = 10240 65535

# Защита от OOM при переполнении сетевых буферов
net.ipv4.tcp_max_tw_buckets = 1440000

Примените параметры без перезагрузки:

sysctl --system

2. Боевая конфигурация виртуального хоста Nginx

Создайте конфигурационный файл /etc/nginx/sites-available/n8n.conf. Конфигурация включает буферизацию для тяжелых JSON/Multipart полезных нагрузок вебхуков (параметр client_max_body_size) и корректную мапу апгрейда соединений:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen [::]:80;
    server_name n8n.example.com;

    # ACME challenge для Let's Encrypt
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name n8n.example.com;

    # SSL сертификаты (пути Certbot)
    ssl_certificate /etc/letsencrypt/live/n8n.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.example.com/privkey.pem;

    # Оптимизация TLS-сессий
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # HSTS
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;

    # Буферы для обработки тяжелых полезных нагрузок в вебхуках (файлы, дампы баз)
    client_max_body_size 64M;
    client_body_buffer_size 128k;
    proxy_buffers 8 64k;
    proxy_buffer_size 128k;
    proxy_busy_buffers_size 192k;

    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;

        # Проброс заголовков для WebSocket
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Проброс клиентских метаданных (предотвращение сбоев Auth и CORS)
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        # Отключение буферизации для EventSource / SSE
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }
}

3. Первичный выпуск и автопродление сертификата

Для получения бесплатного TLS-сертификата выполните выпуск через официальный Certbot в режиме webroot:

mkdir -p /var/www/certbot
certbot certonly --webroot -w /var/www/certbot \
  -d n8n.example.com \
  --email [email protected] \
  --agree-tos \
  --no-eff-email \
  --rsa-key-size 4096

Проверьте таймер systemd, отвечающий за автопродление (выполняется дважды в сутки):

systemctl list-timers | grep certbot
certbot renew --dry-run

Вариант 2: Traefik v3 с нативным Docker Provider

Если инфраструктура требует полной декларативности в одном файле без установки внешнего Nginx на хост, используется Traefik v3. Он опрашивает Docker API через unix-сокет /var/run/docker.sock, автоматически перехватывает поднятие контейнера n8n, выпускает сертификат Let's Encrypt через ACME TLS-ALPN-01 или HTTP-01 challenge и строит внутренний маршрут.

Полный манифест docker-compose.yml:

services:
  traefik:
    image: traefik:v3.1
    container_name: traefik
    restart: always
    command:
      - "--api.insecure=false"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entryPoints.web.address=:80"
      - "--entryPoints.websecure.address=:443"
      # Редирект с HTTP на HTTPS на уровне EntryPoint
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redire
## Настройка вебхуков, домена и SSL: Nginx Reverse Proxy и Traefik

Когда выполняется установка n8n на vps docker compose, контейнер сервиса по умолчанию слушает сокет `0.0.0.0:5678` внутри изолированного сетевого моста (`bridge network`). Выставлять этот порт напрямую в открытый интернет — грубая ошибка архитектуры. Отсутствие TLS-терминации, невозможность безопасного проброса заголовков аутентификации и отсутствие гибкой балансировки делают невозможным промышленный прием внешних вебхуков.

Входной шлюз (ingress-уровень) решает три инженерные задачи: шифрование трафика (TLS 1.3), трансляцию метаданных сессии через прокси-заголовки и удержание двунаправленных сокетов для real-time протоколов.

---

### Механика сбоев: бесконечный спиннер, разрыв веб-сокетов и CORS

При развертывании n8n за реверс-прокси инженеры чаще всего сталкиваются с зацикленной анимацией загрузки (infinite spinner) на странице входа или мгновенным сбросом сессии при переходе в Canvas редактора. Это следствие расхождения сетевого контекста между ядром Node.js и браузером.

#### 1. Разрыв протокола Push и деградация WebSocket
Интерфейс n8n синхронизирует состояние графов выполнения нод в реальном времени через маршрут `/rest/push`. По умолчанию движок пытается выполнить апгрейд HTTP-соединения до дуплексного протокола WebSocket (`RFC 6455`). 
Если проксирующий сервер не умеет переключать протоколы или срезает заголовки согласования, на запрос `GET /rest/push` бэкенд возвращает статус `400 Bad Request` вместо обязательного `101 Switching Protocols`. Фронтенд безуспешно повторяет попытки хэндшейка, удерживая лоадер интерфейса в активном состоянии.

#### 2. Рассинхронизация `X-Forwarded-Proto` и защитные атрибуты Cookie
Если внешнее соединение от клиента к прокси шифруется (`HTTPS`), а обмен между прокси и n8n идет по чистому TCP (`HTTP`), приложению необходимо явно передать исходную схему запроса. При отсутствии заголовка `X-Forwarded-Proto: https`:
* Node.js считает текущую сессию незащищенной и отдает cookie авторизации без флага `Secure`.
* Современный браузер отклоняет установку cookie авторизации при несовпадении origin (`https://` в адресной строке против переданного контекста `http://`).
* Браузерный агент блокирует запросы к API с ошибками CORS (Cross-Origin Resource Sharing) или вываливает ошибку валидации CSRF-токена с кодом `403 Forbidden`.

Чтобы предотвратить подобные конфликты, в манифесте развертывания переменные окружения n8n должны жестко фиксировать публичную точку монтирования:

```env
N8N_HOST=n8n.internal-domain.io
N8N_PORT=5678
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.internal-domain.io/
N8N_EDITOR_BASE_URL=https://n8n.internal-domain.io/
N8N_PUSH_BACKEND=websocket

Архитектура Nginx Reverse Proxy и автоматизация Certbot

Nginx демонстрирует предсказуемое потребление системных ресурсов (15–30 МБ RSS-памяти на рабочий процесс) и гарантирует задержку p99 < 1.5 мс при приеме входящих вебхуков.

Тюнинг сокетов хоста на уровне ядра Linux

При обработке очередей входящих вебхуков (сотни событий в секунду от внешних шлюзов) хост может исчерпать доступные сокеты в статусе TIME_WAIT. Для исключения сетевого дропа пакетов настройте параметры виртуальной памяти и сетевого стека в /etc/sysctl.d/98-n8n-network.conf:

# Увеличение очереди обработки входящих соединений
net.core.somaxconn = 32768
net.ipv4.tcp_max_syn_backlog = 16384

# Повторное использование TCP-сокетов в состоянии TIME_WAIT
net.ipv4.tcp_tw_reuse = 1

# Расширение пула динамических портов
net.ipv4.ip_local_port_range = 10240 65535

# Буферизация TCP-пакетов
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

Активируйте правила: sysctl --system.

Конфигурация виртуального хоста Nginx

Создайте файл /etc/nginx/conf.d/n8n.conf. Конфигурация включает обработку тяжелых полезных нагрузок в вебхуках (JSON, файлы) и двусторонний стриминг:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen [::]:80;
    server_name n8n.internal-domain.io;

    # Валидация домена для Let's Encrypt HTTP-01 challenge
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name n8n.internal-domain.io;

    # Сертификаты Let's Encrypt
    ssl_certificate /etc/letsencrypt/live/n8n.internal-domain.io/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.internal-domain.io/privkey.pem;

    # Криптографический профиль
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:20m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # Лимиты входящих вебхуков (файлы и вложения)
    client_max_body_size 100M;
    client_body_buffer_size 512k;

    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;

        # Проброс заголовков для перехода на WebSocket
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Проброс контекста клиента
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        # Отключение буферизации для потоковых ответов n8n
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Выпуск первичного сертификата через консольный Certbot:

mkdir -p /var/www/certbot
certbot certonly --webroot -w /var/www/certbot \
  -d n8n.internal-domain.io \
  --email [email protected] \
  --agree-tos \
  --no-eff-email

Архитектура Traefik v3: Cloud-Native маршрутизация через лейблы

В изолированных контейнерных средах без сторонних демонов на хосте роль ingress выполняет Traefik v3. Сервис слушает события Docker Daemon через сокет /var/run/docker.sock и автоматически конфигурирует маршруты и TLS-сертификаты на основе метаданных контейнеров.

Ниже представлен production-манифест docker-compose.yml, объединяющий Traefik и n8n:

services:
  ingress-traefik:
    image: traefik:v3.1
    container_name: traefik_gateway
    restart: always
    command:
      - "--api.insecure=false"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      # Автоматический редирект HTTP -> HTTPS
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      # Автовыпуск сертификатов через ACME HTTP-01
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencrypt.acme.email=sysadmin@internal-domain.io"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik_acme:/letsencrypt
    networks:
      - edge_network
    deploy:
      resources:
        limits:
          cpus: '0.50'
          memory: 256M

  n8n-core:
    image: docker.n8n.io/n8nio/n8n:latest
    container_name: n8n_app
    restart: always
    environment:
      - N8N_HOST=n8n.internal-domain.io
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.internal-domain.io/
      - N8N_EDITOR_BASE_URL=https://n8n.internal-domain.io/
      - N8N_PUSH_BACKEND=websocket
    volumes:
      - n8n_storage:/home/node/.n8n
    networks:
      - edge_network
    labels:
      - "traefik.enable=true"
      # Назначение точки входа и маршрутизация по имени хоста
      - "traefik.http.routers.n8n.rule=Host(`n8n.internal-domain.io`)"
      - "traefik.http.routers.n8n.entrypoints=websecure"
      - "traefik.http.routers.n8n.tls=true"
      - "traefik.http.routers.n8n.tls.certresolver=letsencrypt"
      # Определение внутреннего порта сервиса
      - "traefik.http.services.n8n.loadbalancer.server.port=5678"
      # Защита от разрыва WebSocket и настройка заголовков
      - "traefik.http.middlewares.n8n-headers.headers.customrequestheaders.X-Forwarded-Proto=https"
      - "traefik.http.middlewares.n8n-headers.headers.sslredirect=true"
      - "traefik.http.routers.n8n.middlewares=n8n-headers"
    deploy:
      resources:
        limits:
          cpus: '2.00'
          memory: 2048M

networks:
  edge_network:
    driver: bridge

volumes:
  traefik_acme:
  n8n_storage:

Диагностика сетевого стека и проверка прохождения трафика

После запуска контейнеров убедитесь, что апгрейд соединений до WebSocket функционирует без блокировок. Проверьте поведение шлюза с помощью curl:

curl -I -N \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Host: n8n.internal-domain.io" \
  -H "Origin: https://n8n.internal-domain.io" \
  https://n8n.internal-domain.io/rest/push

Корректный ответ шлюза обязан содержать HTTP-статус переключения протокола:

HTTP/2 101
upgrade: websocket
connection: Upgrade

Если вместо кода 101 возвращается HTTP 400 или HTTP 502 Bad Gateway, проверьте права доступа на unix-сокет Docker (/var/run/docker.sock) и системные метрики вводов-выводов (IOPS дисковой подсистемы и %st CPU Steal Time через команду vmstat 1). Высокий процент Steal Time на виртуальных машинах хостеров вызывает таймауты при синхронизации TLS-хэндшейков, что приводит к ложным сбросам соединений в очередях вебхуков.

Очистка истории и тюнинг БД: предотвращение разрастания диска до сотен гигабайт

Когда выполняется базовая установка n8n на vps docker compose по стандартным руководствам, в стек закладывается скрытая деградация хранилища. По умолчанию n8n функционирует в режиме максимальной трассировки: каждый шаг каждого сценария сериализует весь входящий и исходящий JSON-пейлоад в базу данных (таблицы execution_entity и execution_data).

Если рабочий процесс обрабатывает парсинг каталогов, вебхуки с бинарными объектами или списки в 10 000 элементов каждые 60 секунд, объем одной записи может достигать десятков мегабайт. В высоконагруженных инстансах (от 50 000 выполнений в сутки) база данных PostgreSQL разрастается на 10–30 ГБ в день. Это приводит к взрывному росту дискового I/O, деградации latency p99 до сотен миллисекунд, исчерпанию лимитов IOPS облачного диска и внезапной остановке демона СУБД с ошибкой PANIC: could not write to file ... No space left on device (код ENOSPC).

Настройка агрессивной автоочистки в Docker Compose

Решение проблемы разделяется на два этапа: ограничение жизненного цикла данных на уровне приложения и управление освобождением страниц на уровне ядра PostgreSQL.

Первый шаг — перевод n8n в режим жесткой ротации метаданных через переменные окружения. В продакшене категорически запрещено хранить дампы успешных выполнений, если они не требуются для аудита.

Внесите в блок окружения контейнера n8n файла docker-compose.yml следующие параметры:

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    restart: unless-stopped
    environment:
      # Активация демона фоновой очистки
      - EXECUTIONS_DATA_PRUNE=true
      # Время жизни данных выполнения в часах (168 часов = 7 дней)
      - EXECUTIONS_DATA_MAX_AGE=168
      # Жесткий лимит общего количества записей в таблице
      - EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000
      # Запуск сборщика мусора каждый час (CRON-формат)
      - EXECUTIONS_DATA_PRUNE_SCHEDULE=0 * * * *
      # Полный сброс только при авариях; успешные шаги очищаются от тяжелых пейлоадов
      - EXECUTIONS_DATA_SAVE_ON_ERROR=all
      - EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
      - EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=false

Параметр EXECUTIONS_DATA_SAVE_ON_SUCCESS=none полностью отключает сохранение контекста нод для штатно завершившихся workflow, оставляя лишь статус завершения, таймстемп и ID выполнения. Это снижает объем входящей записи (write amplification) на накопитель на 85–95% и предотвращает преждевременный износ ресурса TBW у серверных SSD.

Механика MVCC и феномен мертвого пространства (Table Bloat)

Критическая ошибка многих системных администраторов — уверенность в том, что активация EXECUTIONS_DATA_PRUNE=true уменьшит размер физических файлов базы данных на хосте (/var/lib/postgresql/data/base/...).

PostgreSQL реализует многоверсионность (MVCC, Multi-Version Concurrency Control). При выполнении n8n команды DELETE FROM execution_entity WHERE ... строки физически не стираются с накопителя. Движок лишь проставляет в заголовке кортежа флаг xmax, помечая запись как «мертвую» (dead tuple). Дисковые страницы размером 8 КБ остаются занятыми. Если новые операции INSERT не успевают повторно переиспользовать эти слоты через Free Space Map (FSM), файлы таблиц и связанных B-Tree индексов непрерывно пухнут, а последовательные сканирования (Seq Scan) начинают вызывать стопроцентную утилизацию дискового контроллера в %util (по выводу iostat -xz 1).

По умолчанию стандартный процесс autovacuum настроен слишком консервативно для таблицы с такой динамикой ротации: он триггерится только при изменении 20% объема таблицы (autovacuum_vacuum_scale_factor = 0.2). При объеме базы в 100 ГБ очистка начнется лишь тогда, когда в ней накопится 20 ГБ мертвых строк.

Тюнинг параметров Autovacuum под профиль нагрузки n8n

Чтобы автовакуум непрерывно подчищал мертвые кортежи, не допуская дефрагментации страниц и блокировок, необходимо скорректировать параметры планировщика PostgreSQL. Создайте файл конфигурации postgresql.conf (или переопределите директивы в команде запуска контейнера postgres в docker-compose.yml):

# Тюнинг фонового вакуума для high-churn таблиц
autovacuum = on
autovacuum_max_workers = 4
autovacuum_naptime = 15s

# Агрессивный порог срабатывания (5% изменений вместо 20%)
autovacuum_vacuum_scale_factor = 0.05
autovacuum_vacuum_threshold = 1000

# Увеличение стоимости до троттлинга дискового I/O
autovacuum_vacuum_cost_limit = 2000
autovacuum_vacuum_cost_delay = 2ms

# Память для сбора идентификаторов мертвых строк (TID)
maintenance_work_mem = 512MB

Если нет возможности перезапустить весь кластер с новым конфигурационным файлом, примените агрессивные правила точечно к самой нагруженной таблице n8n через SQL-консоль:

docker compose exec -T postgres psql -U n8n -d n8n -c "
ALTER TABLE execution_entity SET (
    autovacuum_vacuum_scale_factor = 0.02,
    autovacuum_vacuum_threshold = 500,
    autovacuum_vacuum_cost_delay = 0
);
"

Для мониторинга объема мертвого пространства выполните диагностический запрос:

SELECT 
    relname AS table_name,
    n_live_tup AS live_tuples,
    n_dead_tup AS dead_tuples,
    round(n_dead_tup * 100.0 / nullif(n_live_tup + n_dead_tup, 0), 2) AS dead_ratio,
    last_vacuum,
    last_autovacuum
FROM pg_stat_user_tables
WHERE relname IN ('execution_entity', 'execution_data');

Если процент dead_ratio стабильно держится выше 15–20%, дисковая подсистема работает с существенным оверхедом.

Сжатие раздутой БД: почему VACUUM FULL опасен и как использовать pg_repack

Если база n8n уже успела разрастись до 150 ГБ при реальном объеме полезных данных в 10 ГБ, стандартный VACUUM вернет страницы внутрь FSM, но не отдаст место операционной системе (размер каталога в Linux не уменьшится).

Команда VACUUM FULL execution_entity; физически переписывает таблицу в новые файлы, возвращая дисковое пространство файловой системе. Однако у нее есть два критических недостатка: 1. Она берет эксклюзивную блокировку AccessExclusiveLock, полностью замораживая чтение и запись в n8n на часы. Все активные webhook-триггеры начнут отдавать 502 Bad Gateway. 2. Ей требуется свободное место на диске, равное текущему размеру сжимаемой таблицы плюс накладные расходы на индексы. Если на диске осталось менее 10–15% емкости, запуск VACUUM FULL гарантированно приведет к сбою файловой системы и краху базы.

В продакшене для сжатия таблиц на лету без остановки сервиса применяется утилита pg_repack. Она создает теневую копию таблицы, вешает триггер на сбор дельты изменений, синхронизирует данные и производит мгновенный swap таблиц на уровне системного каталога с кратковременной блокировкой в доли секунды.

Если расширение pg_repack не было предустановлено в Docker-образ PostgreSQL, безопасная процедура регламентной ручной очистки выполняется в период минимального трафика по следующему алгоритму:

# 1. Принудительный сброс кешей и запуск стандартного анализа с вербозным логом
docker compose exec -T postgres psql -U n8n -d n8n -c "VACUUM (VERBOSE, ANALYZE) execution_entity;"

# 2. Если на диске есть запас свободного места >= размеру БД, останавливаем n8n во избежание дедлоков
docker compose stop n8n

# 3. Полная компактизация таблицы и перестроение ее индексов
docker compose exec -T postgres psql -U n8n -d n8n -c "VACUUM FULL VERBOSE execution_entity;"
docker compose exec -T postgres psql -U n8n -d n8n -c "REINDEX TABLE execution_entity;"

# 4. Запуск n8n
docker compose start n8n

Системный уровень: контроль dirty pages ядра Linux

Для предотвращения пиковых задержек ядра при массовом сбросе страниц из оперативной памяти на диск (page flushing), добавьте в /etc/sysctl.conf на хостовой машине следующие параметры:

# Снижение порога сброса «грязных» страниц в фоновом режиме (процент от RAM)
vm.dirty_background_ratio = 5
# Максимальный порог, при котором процесс блокируется до завершения синхронизации
vm.dirty_ratio = 10

Применение настроек без перезагрузки:

sysctl -p

Такая конфигурация гарантирует, что дисковый контроллер VPS будет равномерно сбрасывать данные малыми порциями, исключая всплески iowait, фризы Docker-демона и дропы сетевых пакетов в моменты ночной очистки базы n8n.

Резервное копирование воркфлоу и учетных данных: скрипт автоматического экспорта в S3

Когда завершена базовая установка n8n на vps docker compose, развертывание считается незавершенным до тех пор, пока не настроен детерминированный контур Disaster Recovery. Потеря базы данных или приватного ключа шифрования превращает продакшн-узел автоматизации в набор бесполезных контейнеров.

Организация резервного копирования n8n требует разделения на два параллельных процесса: 1. Создание бинарного или транзакционного дампа СУБД (PostgreSQL), где хранятся таблицы execution_entity, workflow_entity и credentials_entity. 2. Гранулярный экспорт описаний воркфлоу в формате JSON через встроенный CLI-интерфейс n8n для быстрого аудита, версионирования в Git или выборочного отката без наката полного дампа базы.


Криптографический якорь: анатомия N8N_ENCRYPTION_KEY

Любые токены API, приватные SSH-ключи, OAuth2 refresh-токены и пароли от баз данных сохраняются n8n в таблице credentials_entity исключительно в зашифрованном виде. Для шифрования payload используется алгоритм AES-256-GCM (в устаревших версиях — AES-256-CBC), где симметричным ключом выступает значение переменной окружения N8N_ENCRYPTION_KEY.

credentials_entity (PostgreSQL)
  ├── id: UUID
  ├── name: "Production Stripe API"
  ├── type: "stripeApi"
  └── data: "U2FsdGVkX1+v8...==" <── AES-256-GCM (зашифровано через N8N_ENCRYPTION_KEY)

Если утерять N8N_ENCRYPTION_KEY, восстановить базу данных невозможно: n8n запустится, но при исполнении первого же узла, требующего авторизации, выбросит исключение Decryption failed или bad decrypt.

Главная архитектурная ошибка — помещать значение N8N_ENCRYPTION_KEY внутрь того же незашифрованного архива с дампом базы, который отправляется в стороннее хранилище. Это нарушает принцип изоляции секретов (Zero Trust Storage). Ключ должен храниться отдельно: во внешнем менеджере секретов (HashiCorp Vault, AWS Secrets Manager) либо шифроваться асимметричным GPG-ключом перед транспортировкой.


Стратегия снижения I/O нагрузки и изоляция ресурсов

На нагруженных инстансах n8n (с сотнями вебхуков в минуту) неконтролируемый запуск pg_dump и архиватора zstd может вызвать всплеск дисковой очереди и деградацию времени отклика сервиса (рост latency p99 до неприемлемых значений).

Чтобы процесс резервного копирования не привел к срабатыванию OOM Killer ядра Linux (kernel: Out of memory: Kill process (node)), скрипт бэкапа обязан выполняться с жесткими системными лимитами: * Приоритет планировщика ввода-вывода (I/O): утилита ionice -c 2 -n 7 (best-effort с наименьшим приоритетом) либо ionice -c 3 (idle). Это гарантирует, что чтение базы с диска не заблокирует запись входящих payload-транзакций n8n. * Приоритет CPU: nice -n 19 предотвращает перехват процессорных квантов у основного процесса Node.js и воркеров PostgreSQL. * Ограничение dirty pages в ядре: если сервер работает на бюджетном NVMe/SATA с ограниченным IOPS, интенсивный сброс буферов сжатия может заморозить подсистему ввода-вывода. Значения sysctl vm.dirty_background_ratio=5 и sysctl vm.dirty_ratio=10 предотвращают накопление гигантских грязных страниц в оперативной памяти перед их записью на диск.


Bash-скрипт автоматизации: n8n-backup-s3.sh

Скрипт выполняет транзакционный дамп базы данных PostgreSQL в формате custom (-Fc), экспортирует все воркфлоу через вызов n8n CLI внутри контейнера, пакует артефакты с использованием многопоточного компрессора zstd, шифрует архив симметричным паролем и отправляет его в S3-совместимое объектное хранилище (AWS S3, MinIO, Wasabi, Cloudflare R2).

Создайте исполняемый файл /opt/n8n/scripts/backup.sh:

#!/usr/bin/env bash
# ==============================================================================
# n8n Disaster Recovery Backup to S3
# Requirements: aws-cli v2, zstd, docker compose (v2)
# ==============================================================================
set -euo pipefail

# --- КОНФИГУРАЦИЯ СРЕДЫ ---
PROJECT_DIR="/opt/n8n"
BACKUP_TEMP_DIR="/tmp/n8n_backup_$$"
S3_BUCKET="s3://my-infra-backups-bucket/n8n"
S3_ENDPOINT_URL="" # Оставьте пустым для AWS, либо укажите https://<account_id>.r2.cloudflarestorage.com
RETENTION_DAYS=14
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
BACKUP_NAME="n8n_backup_${TIMESTAMP}"
LOCK_FILE="/var/lock/n8n_backup.lock"

# Экспорт переменных из production .env
if [[ -f "${PROJECT_DIR}/.env" ]]; then
    set -a
    # shellcheck disable=SC1091
    source "${PROJECT_DIR}/.env"
    set +a
else
    echo "[ERROR] .env файл не найден в ${PROJECT_DIR}" >&2
    exit 1
fi

# Проверка обязательных переменных
: "${POSTGRES_USER:?Не задан POSTGRES_USER в .env}"
: "${POSTGRES_DB:?Не задан POSTGRES_DB в .env}"
: "${N8N_ENCRYPTION_KEY:?Не задан N8N_ENCRYPTION_KEY в .env}"
: "${BACKUP_GPG_PASSPHRASE:?Не задан пароль шифрования архива}"

# Блокировка от параллельного запуска через дескриптор файла
exec 200>"${LOCK_FILE}"
flock -n 200 || { echo "[ERROR] Предыдущий процесс бэкапа еще не завершен." >&2; exit 1; }

# Очистка при аварийном завершении
cleanup() {
    local exit_code=$?
    rm -rf "${BACKUP_TEMP_DIR}"
    rm -f "${LOCK_FILE}"
    if [[ ${exit_code} -ne 0 ]]; then
        echo "[FATAL] Бэкап завершился с ошибкой (код: ${exit_code})" >&2
    fi
    exit ${exit_code}
}
trap cleanup EXIT INT TERM

echo "[INFO] [$(date)] Инициализация резервного копирования..."
mkdir -p "${BACKUP_TEMP_DIR}/${BACKUP_NAME}"
chmod 700 "${BACKUP_TEMP_DIR}"

# 1. ТРАНЗАКЦИОННЫЙ ДАМП POSTGRESQL
# Формат -Fc (custom) позволяет применять pg_restore параллельно и выборочно
echo "[INFO] Создание дампа PostgreSQL (${POSTGRES_DB})..."
nice -n 19 ionice -c 2 -n 7 \
docker compose -f "${PROJECT_DIR}/docker-compose.yml" exec -T postgres \
    pg_dump -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" \
    -Fc --no-owner --no-privileges \
    > "${BACKUP_TEMP_DIR}/${BACKUP_NAME}/postgres_dump.dump"

# 2. ЭКСПОРТ ВОРКФЛОУ ЧЕРЕЗ N8N CLI
# Запуск от пользователя node внутри контейнера исключает проблемы с правами
echo "[INFO] Экспорт JSON-описаний воркфлоу через n8n CLI..."
nice -n 19 ionice -c 2 -n 7 \
docker compose -f "${PROJECT_DIR}/docker-compose.yml" exec -T --user node n8n \
    n8n export:workflow --all --output=/tmp/workflows_exported.json

# Копируем полученный JSON на хост
docker compose -f "${PROJECT_DIR}/docker-compose.yml" cp \
    n8n:/tmp/workflows_exported.json \
    "${BACKUP_TEMP_DIR}/${BACKUP_NAME}/workflows_all.json"

# Удаляем временный файл внутри контейнера
docker compose -f "${PROJECT_DIR}/docker-compose.yml" exec -T --user node n8n \
    rm -f /tmp/workflows_exported.json

# 3. ФИКСАЦИЯ МЕТАДАННЫХ ОКРУЖЕНИЯ
cat <<EOF > "${BACKUP_TEMP_DIR}/${BACKUP_NAME}/backup_metadata.json"
{
  "timestamp": "${TIMESTAMP}",
  "postgres_db": "${POSTGRES_DB}",
  "schema_version": "docker-compose-v2",
  "n8n_version": "$(docker compose -f "${PROJECT_DIR}/docker-compose.yml" exec -T --user node n8n n8n --version | tr -d '\r')"
}
EOF

# 4. СЖАТИЕ И АСИММЕТРИЧНОЕ/СИММЕТРИЧНОЕ ШИФРОВАНИЕ
echo "[INFO] Архивирование (tar + zstd) и шифрование через OpenSSL..."
ARCHIVE_PATH="${BACKUP_TEMP_DIR}/${BACKUP_NAME}.tar.zst.enc"

nice -n 19 ionice -c 2 -n 7 \
tar -C "${BACKUP_TEMP_DIR}" -cf - "${BACKUP_NAME}" \
    | zstd -3 -T2 \
    | openssl enc -aes-256-cbc -salt -pbkdf2 -iter 100000 \
      -pass env:BACKUP_GPG_PASSPHRASE \
      -out "${ARCHIVE_PATH}"

# 5. ОТПРАВКА В ОБЪЕКТНОЕ ХРАНИЛИЩЕ S3
echo "[INFO] Передача архива в S3..."
S3_CMD_EXTRA=()
if [[ -n "${S3_ENDPOINT_URL}" ]]; then
    S3_CMD_EXTRA+=(--endpoint-url "${S3_ENDPOINT_URL}")
fi

aws s3 cp "${ARCHIVE_PATH}" "${S3_BUCKET}/${BACKUP_NAME}.tar.zst.enc" \
    --storage-class STANDARD_IA \
    "${S3_CMD_EXTRA[@]}"

# 6. РОТАЦИЯ УСТАРЕВШИХ КОПИЙ В S3
echo "[INFO] Удаление бэкапов старше ${RETENTION_DAYS} дней..."
OLDER_THAN_DATE=$(date -d "${RETENTION_DAYS} days ago" +"%Y-%m-%d" 2>/dev/null || date -v-"${RETENTION_DAYS}"d +"%Y-%m-%d")

aws s3 ls "${S3_BUCKET}/" "${S3_CMD_EXTRA[@]}" | while read -r line; do
    FILE_DATE=$(echo "$line" | awk '{print $1}')
    FILE_NAME=$(echo "$line" | awk '{print $4}')

    if [[ -n "${FILE_NAME}" && "${FILE_NAME}" =~ ^n8n_backup_.*\.enc$ ]]; then
        if [[ "${FILE_DATE}" < "${OLDER_THAN_DATE}" ]]; then
            echo "[INFO] Удаление устаревшего объекта: ${FILE_NAME}"
            aws s3 rm "${S3_BUCKET}/${FILE_NAME}" "${S3_CMD_EXTRA[@]}"
        fi
    fi
done

echo "[SUCCESS] Резервное копирование завершено: ${BACKUP_NAME}.tar.zst.enc"

Сделайте файл исполняемым и закройте права доступа:

chmod 700 /opt/n8n/scripts/backup.sh
chown root:root /opt/n8n/scripts/backup.sh

Настройка расписания: Systemd Timer против Cron

Традиционный cron не изолирует процессы в отдельные cgroups и агрессивно сбрасывает логи в почтовую очередь. Рекомендуется использовать Systemd Timers: они предоставляют жесткий лимит ресурсов, встроенный сборщик логов через journald и автоматический мониторинг завершения.

Создайте сервисный юнит /etc/systemd/system/n8n-backup.service:

[Unit]
Description=Automated n8n and PostgreSQL Backup to S3
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
User=root
WorkingDirectory=/opt/n8n
ExecStart=/opt/n8n/scripts/backup.sh
StandardOutput=journal
StandardError=journal

# Изоляция ресурсов ядра (cgroups v2)
Slice=backup.slice
MemoryMax=1G
CPUQuota=50%
IOWeight=100

Создайте таймер /etc/systemd/system/n8n-backup.timer:

[Unit]
Description=Run n8n backup daily at 03:00 UTC

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=600

[Install]
WantedBy=timers.target

Активируйте и запустите таймер:

systemctl daemon-reload
systemctl enable --now n8n-backup.timer

Проверить расписание и статус выполнения можно штатными командами:

systemctl list-timers n8n-backup.timer
journalctl -u n8n-backup.service -e

Верификация восстановления (Disaster Recovery Drill)

Резервная копия, не прошедшая процедуру тестового развертывания, не является гарантией выживания сервиса. Для проверки целостности файла выполняется процедура расшифровки и восстановления:

# 1. Расшифровка и распаковка на staging-сервере
openssl enc -d -aes-256-cbc -pbkdf2 -iter 100000 \
    -pass pass:"ВАШ_СЕКРЕТНЫЙ_ПАРОЛЬ" \
    -in n8n_backup_20261002_030000.tar.zst.enc \
    | zstd -d \
    | tar -xf -

# 2. Накат дампа базы данных в чистый PostgreSQL контейнер
docker compose exec -T postgres dropdb -U n8n_user --if-exists n8n_db
docker compose exec -T postgres createdb -U n8n_user n8n_db
docker compose exec -T postgres pg_restore -U n8n_user -d n8n_db \
    --clean --if-exists --no-owner < postgres_dump.dump

# 3. Выборочный импорт воркфлоу (если требуется восстановить только логику)
docker compose cp workflows_all.json n8n:/tmp/workflows_restore.json
docker compose exec -T --user node n8n n8n import:workflow --input=/tmp/workflows_restore.json

Если база данных была восстановлена из дампа, перед запуском контейнера n8n обязательно сверяется переменная N8N_ENCRYPTION_KEY в файле .env: она должна посимвольно совпадать со значением на исходном сервере. Только при выполнении этого условия движок расшифрует узлы авторизации и возобновит выполнение расписаний без инцидентов.

Часто задаваемые вопросы (FAQ)

Сложно ли обновлять n8n в Docker Compose?

Обновление занимает 30 секунд: достаточно выполнить docker compose pull и docker compose up -d. Встроенные миграции PostgreSQL автоматически обновят схему базы данных.

Можно ли выполнять тяжелые Python-скрипты внутри n8n?

Да, n8n поддерживает ноды Code (JavaScript и Python). При необходимости можно подключить кастомные Python-библиотеки через монтирование окружения в контейнер.

Что делать, если база данных переполнила диск сервера?

Включите режим EXECUTIONS_DATA_PRUNE в переменных окружения и выполните команду VACUUM FULL в PostgreSQL для высвобождения занятого дискового пространства.

Безопасно ли хранить API-ключи от CRM и банков в self-hosted n8n?

Все учетные записи шифруются 256-битным ключом (N8N_ENCRYPTION_KEY). Так как сервер принадлежит вам, ключи никогда не попадут к третьим лицам.