n8n на своём сервере: установка и настройка

Установка n8n на свой сервер через Docker Compose: PostgreSQL, домен с HTTPS, ключ шифрования, вебхуки, queue mode, бэкапы и обновления без потери данных.

Ниже — схема для одного VPS: n8n, PostgreSQL и task runners в Docker Compose, перед ними Caddy или nginx. Всё сверено с документацией и кодом n8n и актуально на сентябрь 2026 года: stable — 2.41.4, beta — 2.42.1, n8n 3.0 запланирован на октябрь.

Что понадобится

Шаг 1. Решите, подходит ли вам self-hosted n8n

Что вы получаете

n8n — конструктор автоматизаций: сценарий (workflow) собирается из узлов и запускается вебхуком, расписанием или событием сервиса, есть узел Code на JavaScript и Python, AI-агенты и MCP. Своя установка не ограничивает число сценариев и запусков, а credentials остаются на вашем диске. Обновления, бэкапы и безопасность — тоже на вас.

Лицензия

n8n выходит под Sustainable Use License — это fair-code, а не open source. Бесплатно можно автоматизировать свою компанию и личные проекты, делать сценарии для клиентов на своём инстансе и брать деньги за разработку, если клиенты сами сценарии не правят. Без договора с n8n нельзя сдавать его как сервис, где клиенты строят сценарии, в том числе через ваш интерфейс, API или AI-агента, и продавать n8n под своим брендом.

Чего нет в Community Edition

Queue mode с воркерами в бесплатной редакции есть. Нет SSO через SAML и LDAP, проектов с ролями, окружений и Git, внешних хранилищ секретов, стриминга логов, хранения файлов в S3, multi-main и принудительной 2FA для всех. Главное для команды: сценарии и credentials видят только владелец инстанса и их автор, общего доступа нет. Бесплатная регистрация по email добавляет папки, отладку прошлых запусков и custom execution data.

Шаг 2. Подготовьте сервер

Снаружи нужны только порты 22, 80 и 443, подробнее — в «Файрвол ufw: порты».

sudo ufw allow 22/tcp
sudo ufw allow 80,443/tcp
sudo ufw enable
Важно: порты из секции ports: Docker открывает правилами NAT в обход ufw. Поэтому ниже n8n слушает только 127.0.0.1:5678, а PostgreSQL и брокер task runners не публикуются вовсе.

Шаг 3. Создайте .env с секретами

sudo mkdir -p /opt/n8n && cd /opt/n8n
sudo tee .env > /dev/null <<EOF
N8N_VERSION=2.41.4
N8N_DOMAIN=n8n.example.com
GENERIC_TIMEZONE=Europe/Moscow
POSTGRES_PASSWORD=$(openssl rand -hex 24)
N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)
N8N_RUNNERS_AUTH_TOKEN=$(openssl rand -hex 32)
EOF
sudo chmod 600 .env

N8N_ENCRYPTION_KEY шифрует все credentials в базе. Если его не задать, n8n сгенерирует ключ сам и сохранит в /home/node/.n8n/config внутри тома: потеряли том — credentials в уцелевшей базе уже не расшифровать. Сразу сохраните копию .env вне сервера.

Важно: для существующей установки ключ не генерируйте, а возьмите из файла config: с другим ключом n8n не стартует и пишет Mismatching encryption keys.

Шаг 4. Опишите сервисы в compose.yml

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

x-n8n-env: &n8n-env
  DB_TYPE: postgresdb
  DB_POSTGRESDB_HOST: postgres
  DB_POSTGRESDB_DATABASE: n8n
  DB_POSTGRESDB_USER: n8n
  DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
  N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
  N8N_HOST: ${N8N_DOMAIN}
  N8N_PROTOCOL: https
  N8N_EDITOR_BASE_URL: https://${N8N_DOMAIN}/
  N8N_WEBHOOK_URL: https://${N8N_DOMAIN}/
  N8N_PROXY_HOPS: 1
  GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
  TZ: ${GENERIC_TIMEZONE}
  N8N_RUNNERS_MODE: external
  N8N_RUNNERS_AUTH_TOKEN: ${N8N_RUNNERS_AUTH_TOKEN}
  N8N_RUNNERS_BROKER_LISTEN_ADDRESS: 0.0.0.0
  EXECUTIONS_DATA_MAX_AGE: 168
  EXECUTIONS_DATA_PRUNE_MAX_COUNT: 20000

services:
  postgres:
    image: postgres:18
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: n8n
      PGDATA: /var/lib/postgresql/data
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
      interval: 5s
      timeout: 5s
      retries: 10

  n8n:
    image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment: *n8n-env
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy

  runners:
    image: n8nio/runners:${N8N_VERSION}
    restart: unless-stopped
    environment:
      N8N_RUNNERS_AUTH_TOKEN: ${N8N_RUNNERS_AUTH_TOKEN}
      N8N_RUNNERS_TASK_BROKER_URI: http://n8n:5679
    depends_on:
      - n8n

volumes:
  db_data:
  n8n_data:

Что здесь важно:

  • Версия зафиксирована. n8nio/n8n и n8nio/runners обязаны быть одной версии, а с тегом latest любой docker compose pull принесёт новую версию с миграциями базы.
  • Каталог данных PostgreSQL задан явно. В postgres:18 он переехал, и без строки PGDATA база окажется вне тома.
  • Адреса. N8N_WEBHOOK_URL — база для вебхуков, без него редактор покажет http://localhost:5678/webhook/...; N8N_EDITOR_BASE_URL нужен письмам и OAuth. Прежнее имя WEBHOOK_URL с версии 2.35 устарело.
  • Прокси. N8N_PROXY_HOPS: 1 велит брать IP клиента из X-Forwarded-For. Если перед nginx есть ещё Cloudflare с проксированием, ставьте 2.
  • Часовой пояс. Без GENERIC_TIMEZONE расписания идут по America/New_York.
  • Task runners. С 2.0 код из узла Code выполняется отдельно от n8n. По умолчанию это дочерний процесс с правами n8n (устаревший internal mode), а контейнер runners отделяет код от базы и ключа и нужен для Python.

Запустите стек и проверьте готовность:

cd /opt/n8n
sudo docker compose up -d
sudo docker compose ps
curl -s http://127.0.0.1:5678/healthz/readiness

/healthz/readiness вернёт {"status":"ok"}, когда база подключена и миграции прошли. Логи — sudo docker compose logs -f n8n, а для внешнего мониторинга подойдёт https://n8n.example.com/healthz, например в Uptime Kuma.

Шаг 5. Подключите домен и HTTPS

Вариант А. Caddy

Caddy сам выпустит сертификат, проксирует WebSocket и передаст Host и X-Forwarded-*. Добавьте в /etc/caddy/Caddyfile:

n8n.example.com {
    reverse_proxy 127.0.0.1:5678
}

Примените: sudo systemctl reload caddy. Подробнее — в «Caddy: автоматический HTTPS».

Вариант Б. Nginx и certbot

По умолчанию nginx не пропускает WebSocket, передаёт в Host адрес бэкенда, ограничивает тело запроса 1 МБ и ждёт ответа 60 секунд — n8n это мешает. Создайте /etc/nginx/sites-available/n8n.example.com:

server {
    listen 80;
    server_name n8n.example.com;

    client_max_body_size 64m;

    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        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_buffering off;
        proxy_read_timeout 3600s;
    }
}

Включите сайт и получите сертификат, подробнее — в «Бесплатный SSL с Let's Encrypt (certbot)»:

sudo ln -s /etc/nginx/sites-available/n8n.example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d n8n.example.com

Что делают строки конфига:

  • WebSocket. Заголовки Upgrade и Connection пропускают /rest/push, по которому редактор получает результаты запусков.
  • Проверка Origin. Ей нужны Host и X-Forwarded-Host: увидев 127.0.0.1:5678, n8n отклонит WebSocket редактора.
  • Размер запроса. client_max_body_size снимает лимит в 1 МБ, а сам n8n принимает до 16 МиБ (N8N_PAYLOAD_SIZE_MAX), файлы в multipart — до 200 МиБ.
  • Таймаут. proxy_read_timeout не даёт оборвать долгий вебхук и WebSocket, который n8n пингует раз в 60 секунд.
  • Буферизация. С proxy_buffering off потоковые ответы AI-агентов доходят до клиента сразу.
Совет: Cloudflare в режиме проксирования обрывает запрос после 100 секунд тишины с ошибкой 524. Долгие сценарии запускайте с ответом сразу (Respond: Immediately), а результат отправляйте отдельным запросом.

Шаг 6. Первый вход: владелец, 2FA, порты

Откройте https://n8n.example.com: n8n предложит создать владельца (owner), и кто первым заполнит форму, тот и получит инстанс. Регистрируйтесь сразу после запуска, затем включите 2FA в Settings → Personal → Enable 2FA и сохраните коды восстановления.

Если коды потеряны или забыт пароль:

sudo docker compose exec n8n n8n mfa:disable [email protected]
sudo docker compose exec n8n n8n user-management:reset

Первая снимает 2FA с пользователя. Вторая удаляет остальных пользователей, передаёт их сценарии и credentials владельцу и сбрасывает его учётную запись до экрана создания — выход для владельца без SMTP (N8N_EMAIL_MODE=smtp и N8N_SMTP_*). Остальным владелец выдаёт ссылку сброса пароля в Settings → Users.

Проверьте порты:

sudo ss -tlnp | grep -E '5678|5679|5432'

В выводе должна быть одна строка с 127.0.0.1:5678. Общий список мер — в «Чеклисте базовой защиты VPS».

Шаг 7. Настройте вебхуки

У каждого узла Webhook два адреса:

  • Тестовый /webhook-test/<path> слушает 120 секунд после Listen for test event и принимает один вызов, данные видны на холсте.
  • Рабочий /webhook/<path> работает после Publish. С версии 2.0 правки остаются черновиком, а в работе — опубликованная версия, так что после изменений сценарий публикуют заново. Запуски видны во вкладке Executions.

Кроме /webhook/, наружу должны смотреть /webhook-waiting/ (сценарии, ждущие ответа, например одобрения в Slack), /form/ и /form-waiting/. Публичные вебхуки закрывайте аутентификацией узла: Basic, Header или JWT. Фильтр IP-адресов в узле работает только с N8N_PROXY_HOPS.

Шаг 8. Учтите, как работает узел Code

  • Python доступен только через контейнер runners. Это нативный Python 3 вместо Pyodide, поэтому _input и доступ к полям через точку из старых сценариев не работают.
  • Переменные окружения закрыты: $env в выражениях и Code недоступен (N8N_BLOCK_ENV_ACCESS_IN_NODE=true), секреты храните в credentials.
  • Сторонние модули ставятся в свой образ FROM n8nio/runners:2.41.4 через pnpm add или uv pip install и разрешаются в /etc/n8n-task-runners.json ключами NODE_FUNCTION_ALLOW_EXTERNAL и N8N_RUNNERS_EXTERNAL_ALLOW.
  • Таймаут кода — 300 секунд (N8N_RUNNERS_TASK_TIMEOUT), в 3.0 станет 60.
  • Execute Command и Local File Trigger выключены через NODES_EXCLUDE. Вернув их, вы дадите shell в контейнере всем, кто правит сценарии.
  • Файловые узлы видят только /home/node/.n8n-files, а этот каталог вне тома. Смонтируйте свой, например ./files:/files, и задайте N8N_RESTRICT_FILE_ACCESS_TO=/files.

Шаг 9. Включите queue mode, если одного процесса мало

В обычном режиме один процесс Node.js отдаёт интерфейс, принимает вебхуки и выполняет сценарии: тяжёлый сценарий тормозит всё остальное, а лишние ядра простаивают. Сначала попробуйте N8N_CONCURRENCY_PRODUCTION_LIMIT — запуски сверх лимита встанут в очередь. Если мало, включайте queue mode: главный процесс кладёт задания в Redis, воркеры их выполняют. OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS шлёт туда же запуски из редактора.

Добавьте в x-n8n-env:

  EXECUTIONS_MODE: queue
  QUEUE_BULL_REDIS_HOST: redis
  QUEUE_HEALTH_CHECK_ACTIVE: "true"
  OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS: "true"

В services добавьте Redis и пару из воркера и его runners, в volumes — redis_data:, в depends_on сервиса n8n — redis с condition: service_healthy:

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 10

  worker-1:
    image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
    restart: unless-stopped
    command: worker --concurrency=10
    environment: *n8n-env
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      - n8n

  runners-1:
    image: n8nio/runners:${N8N_VERSION}
    restart: unless-stopped
    environment:
      N8N_RUNNERS_AUTH_TOKEN: ${N8N_RUNNERS_AUTH_TOKEN}
      N8N_RUNNERS_TASK_BROKER_URI: http://worker-1:5679
    depends_on:
      - worker-1

Что учесть:

  • Ключ шифрования воркер берёт из того же x-n8n-env: без ключа он не стартует, с другим не расшифрует credentials.
  • Runners у каждого воркера свои, ведь брокер задач живёт в воркере. Поэтому --scale не подходит: добавляйте пары worker-2 и runners-2.
  • Параллелизм. --concurrency — одновременные запуски на воркер, по умолчанию 10. Меньше 5 не советуют: много таких воркеров исчерпают пул соединений к базе.
  • Файлы из сценариев хранятся в PostgreSQL, до 512 МиБ на файл: filesystem в queue mode не поддерживается, а S3 есть только в платных планах.
  • Redis не публикуйте и не включайте в нём вытеснение ключей вроде allkeys-lru, иначе пропадут задания. Подробнее — в «Redis: установка и настройка».

Шаг 10. Настройте обслуживание

Очистка истории выполнений

По умолчанию n8n хранит запуски 14 дней и не больше 10 000, в compose выше — 7 дней и 20 000. Удаляются они вместе с бинарными данными, аннотированные и незавершённые не трогаются. Писать меньше помогут EXECUTIONS_DATA_SAVE_ON_SUCCESS=none, чтобы хранить только ошибки, и EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=false, а для отдельного сценария — его настройки.

PostgreSQL место удалённых строк системе не возвращает. Проверьте, что выросло:

sudo docker compose exec postgres psql -U n8n -d n8n -c "SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) FROM pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 5;"

Обычно это execution_data. Сжать таблицу может только VACUUM FULL: он блокирует её и требует свободного места размером с неё, поэтому остановите n8n (sudo docker compose stop n8n), а после запустите снова (start):

sudo docker compose exec postgres psql -U n8n -d n8n -c "VACUUM (FULL, ANALYZE) execution_data;"

Ещё растут бинарные данные в томе n8n и логи Docker — задайте max-size и max-file в /etc/docker/daemon.json, см. «Закончилось место на диске».

Бэкап

Нужны три вещи: дамп базы, ключ из .env и том n8n_data с файлом config, бинарными данными и community nodes. Без ключа дамп бесполезен.

sudo mkdir -p /var/backups/n8n && cd /opt/n8n
sudo docker compose exec -T postgres pg_dump -U n8n -d n8n -Fc | sudo tee /var/backups/n8n/db-$(date +%F).dump > /dev/null
sudo docker run --rm -v n8n_n8n_data:/data:ro -v /var/backups/n8n:/backup alpine tar czf /backup/n8n-data-$(date +%F).tgz -C /data .
sudo cp .env /var/backups/n8n/env-$(date +%F)

Имя тома — n8n_n8n_data, по каталогу проекта; проверьте через sudo docker volume ls. Расписание и копии вне сервера — в статьях «Бэкапы баз данных», «Автобэкапы по расписанию (cron)» и «Инкрементальные бэкапы с restic». Хотя бы раз восстановите бэкап на чистом сервере. CLI-экспорт n8n export:workflow --backup его не заменяет: в нём нет пользователей, истории и переменных.

Обновления

Минорные версии выходят почти еженедельно, исправления безопасности — раз в две недели.

  1. Прочитайте release notes до целевой версии, а перед мажорной откройте Settings → Migration Report.
  2. Снимите бэкап.
  3. Смените N8N_VERSION в .env и выполните sudo docker compose pull && sudo docker compose up -d.
  4. Не останавливайте контейнер, пока в логе видно Migrations in progress, please do NOT stop the process.

Миграции необратимы: откат — это старая версия плюс дамп. Main, воркеры и runners держите на одной версии. Расписания по умолчанию живут в памяти, и запуск, пришедшийся на рестарт, пропадёт; с версии 2.36 это решает durable scheduler (N8N_SCHEDULER_ENABLED=true и N8N_USE_WORKFLOW_PUBLICATION_SERVICE=true).

Что изменит n8n 3.0:

  • только Docker, установка через npm перестанет работать;
  • ~/.n8n/binaryData станет ~/.n8n/storage — поправьте тома, смонтированные прямо на binaryData;
  • непроверенные community nodes не загрузятся без N8N_UNVERIFIED_PACKAGES_ENABLED=true;
  • таймаут Code сократится до 60 секунд;
  • ротация ключей шифрования включится по умолчанию, и откат на 2.x останется только из дампа;
  • уйдут старые узлы Function, Cron, Read Binary File и другие — их покажет Migration Report.

Шаг 11. Закройте основные риски

  • Обновляйтесь вовремя. За 2026 год n8n выпустил 196 бюллетеней безопасности, 19 из них критические. Ni8mare (CVE-2026-21858, CVSS 10) позволяла без входа читать файлы сервера через формы и захватить инстанс, исправлена в 1.121.0. CVE-2025-68613 — выполнение кода любым вошедшим пользователем — с марта 2026 года в каталоге CISA KEV, то есть эксплуатируется.
  • Community nodes работают с правами n8n и видят все credentials. В январе 2026 года Endor Labs нашла в npm пакеты n8n-nodes-* под видом интеграций (один изображал Google Ads), которые крали OAuth-токены. Ставьте только проверенные (verified) узлы или выключите установку: N8N_COMMUNITY_PACKAGES_ENABLED=false.
  • Редактор надёжнее открыть только из VPN или с доверенных IP через allow и deny в nginx, оставив публичными пути из Шага 7.
  • Лишнее выключите: публичный API — N8N_PUBLIC_API_DISABLED=true. Если сценарии пишут другие люди, включите N8N_SSRF_PROTECTION_ENABLED=true (с 2.12), и узлы перестанут ходить на приватные адреса и соседние контейнеры.
  • Аудит: sudo docker compose exec n8n n8n audit найдёт неиспользуемые credentials и рискованные узлы.

Сколько ресурсов нужно

В обычном режиме n8n — один процесс Node.js на одном ядре. Официальный Helm-чарт закладывает на главный процесс и воркер от 512 МБ до 1 ГБ памяти, на runners — от 256 до 512 МБ. Память растёт с объёмом данных в выполнении, а ручной запуск держит их копию ещё и для редактора.

  • 1 vCPU и 2 ГБ — личный инстанс: десятки сценариев на расписаниях и вебхуках, редкий Code.
  • 2 vCPU и 4 ГБ — рабочий инстанс: AI-агенты, Code, выборки на тысячи строк, файлы. Столько же официальная инструкция требует для стека с песочницей n8n Assistant.
  • 4 vCPU и 8 ГБ и больше — queue mode с двумя-тремя воркерами.

Диск — NVMe от 20 ГБ: каждый запуск пишет в execution_data.

Трафик n8n почти не тратит. Вебхуки, ответы API, docker pull, apt и npm — входящий трафик, а в пакет ForestsNet Cloud засчитывается только исходящий: запросы n8n к API и ответы на вебхуки, обычно килобайты. Заметным он становится, если сценарии отправляют наружу файлы.

Диагностика

  • Плашка Connection lost. WebSocket /rest/push не проходит: нет Upgrade и Connection, подменён Host (в логе — Origin header does NOT match the expected origin) или короткий таймаут, см. Шаг 5.
  • Адрес вебхука с localhost. Не задан N8N_WEBHOOK_URL: добавьте его и выполните sudo docker compose up -d.
  • Ошибка X-Forwarded-For. ValidationError: The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false — не задан N8N_PROXY_HOPS.
  • Страница о secure cookie. Your n8n server is configured to use a secure cookie, however you are either visiting this via an insecure URL, or using Safari. — вход по http://. Настройте HTTPS, а до того заходите через SSH-туннель на localhost:5678 не из Safari; N8N_SECURE_COOKIE=false — только для проверки.
  • Credentials не расшифровываются. Credentials could not be decrypted — том пересоздан без N8N_ENCRYPTION_KEY или ключ заменён. Верните старый ключ из бэкапа, иначе credentials вводятся заново.
  • Нет прав на config. EACCES: permission denied, open '/home/node/.n8n/config' — каталог хоста принадлежит root, а n8n работает под uid 1000: sudo chown -R 1000:1000 <каталог>.
  • Вебхук отвечает 404. The requested webhook "POST <path>" is not registered. — сценарий не опубликован, истёк тестовый режим или импорт через CLI снял публикацию; This webhook is not registered for GET requests — не тот метод.
  • Не хватает памяти. n8n may have run out of memory while running this execution или рестарты контейнера. Код выхода в sudo docker compose ps -a: 137 — OOM killer, 134 — лимит кучи V8. Дробите данные через Loop Over Items и sub-workflows, добавьте RAM или поднимите NODE_OPTIONS=--max-old-space-size=2048.
  • Code не выполняется. Task request timed out или Python runner unavailable — runners не подключились: разные версии образов или токены, нет N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0. Смотрите sudo docker compose logs runners.
  • PostgreSQL не стартует после смены тега. database files are incompatible with server — мажорную версию меняют через pg_dumpall и восстановление в новый том, данные целы.

Источники