SSD Nodes Learn
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-07-24

Как развернуть n8n на VPS через Docker

Инструкция по установке n8n с Docker Compose, Postgres и HTTPS. Решение проблем с WEBHOOK_URL и настройкой encryption-key для стабильной работы сервиса.

Что вы создаете

n8n — это инструмент для автоматизации рабочих процессов. Это визуальный редактор, где триггер (webhook, расписание или отправка формы) запускает цепочку узлов (nodes). Эти узлы вызывают API, преобразуют данные и записывают их в другие системы. n8n стал основным связующим звеном для рабочих процессов с AI-агентами, так как он поддерживает всех провайдеров моделей и баз данных без необходимости написания собственных сервисов. Один docker run получает готовый редактор за две минуты. Данное руководство посвящено оставшимся девяноста процентам: обеспечению отказоустойчивости с помощью Postgres вместо стандартного файла SQLite, настройке доступа через HTTPS и — самой частой ошибке — настройке webhooks так, чтобы они выдавали URL, доступный из внешней сети.

Готовый стек состоит из двух контейнеров в одной сети Docker: самого n8n и базы данных Postgres, в которой хранятся рабочие процессы и учетные данные. Обратный прокси (reverse proxy) на хост-машине завершает TLS-соединение и перенаправляет трафик на n8n на localhost. Таким образом, доступ в интернет осуществляется только через этот прокси. Этот стек входит в список лучших решений для self-hosting 2026.

Предварительные требования и ограничения

Вам необходим VPS минимум с 1 GB RAM. Рекомендуется планировать 2 GB для выполнения реальных рабочих процессов. Процессы выполнения и среда выполнения Node.js потребляют много памяти. Остановка контейнера механизмом OOM killer во время работы приведет к ошибкам. Для начала работы достаточно одного vCPU.

Вам необходим домен или поддомен (например, n8n.example.com) с A-записью, указывающей на публичный IP-адрес VPS. Домен должен разрешаться до запроса сертификата. Порты 80 и 443 должны быть открыты для прокси-сервера. Порт 5678 самого n8n не должен быть доступен из интернета. Вам требуются Docker Engine и плагин Compose. Если docker compose version выдает ошибку docker: 'compose' is not a docker command, у вас установлен старый автономный бинарный файл, а плагин — это sudo apt install docker-compose-plugin.

SQLite подходит для тестов, Postgres — для критически важных задач

База данных n8n по умолчанию представляет собой файл SQLite по пути /home/node/.n8n/database.sqlite. Это подходит для ознакомления. Если не примонтировать volume, данные будут удалены при первой же пересоздаче контейнера. Переход на Postgres нужен не ради скорости. SQLite использует блокировку для одного записи (single writer lock). Это приводит к ошибке SQLITE_BUSY: database is locked при одновременном запуске нескольких workflow или при использовании queue mode. У Postgres нет таких ограничений. Он обеспечивает корректное резервное копирование с помощью pg_dump. Официальная документация n8n предполагает использование Postgres для рабочих серверов. Поздний переход потребует ручной миграции данных. Если сервер важен, сразу используйте Postgres.

DNS и firewall

Сначала настройте запись и откройте порты. Это необходимо, чтобы на этапе получения сертификата не возникло ошибки из-за невозможности разрешения имени.

dig +short n8n.example.com
curl -s ifconfig.me
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow OpenSSH
sudo ufw enable

Не открывайте порт 5678. В compose file n8n привязан к 127.0.0.1:5678, поэтому доступ к нему должен иметь только обратный прокси-сервер хоста. Открытие порта через ufw allow 5678 нарушит эту изоляцию.

Файл Compose

Создайте рабочую директорию и docker-compose.yml. Это вся архитектура: два сервиса, одна частная сеть и два именованных тома.

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

  n8n:
    image: docker.n8n.io/n8nio/n8n:2.29.10
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - N8N_HOST=n8n.example.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.example.com/
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - N8N_PROXY_HOPS=1
      - GENERIC_TIMEZONE=Europe/London
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - n8n_data:/home/node/.n8n
    networks:
      - n8n_net
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  postgres_data:
  n8n_data:

networks:
  n8n_net:

Несколько важных пояснений. DB_POSTGRESDB_HOST=postgres — это имя сервиса, которое Docker разрешает в общей сети; это не localhost, что внутри контейнера n8n означает сам n8n. Использование depends_on с condition: service_healthy предотвращает одновременный запуск n8n и Postgres; без этого n8n не сможет найти базу данных при запуске и завершит работу. Именованный том n8n_data по пути /home/node/.n8n содержит ключ шифрования и, в случае SQLite, саму базу данных — это единственный каталог, который нельзя терять. Фиксируйте образ на конкретной версии, никогда не используйте latest; причины указаны в разделе об обновлении ниже.

Файл secrets

Никогда не записывайте пароли в compose file. Поместите их в файл .env рядом с ним; Compose прочитает его автоматически. Генерируйте пароли так, чтобы они были по-настоящему случайными.

printf 'POSTGRES_PASSWORD=%s\n'  "$(openssl rand -hex 24)" >  .env
printf 'N8N_ENCRYPTION_KEY=%s\n' "$(openssl rand -hex 32)" >> .env
chmod 600 .env

N8N_ENCRYPTION_KEY — это самая важная строка в данном контексте. Это ключ, которым зашифрованы все сохраненные учетные данные. Установите его явно, а не позволяйте n8n генерировать его автоматически. Значение, которое вы сгенерировали сами, можно записать и восстановить. Как только n8n зашифрует свои первые учетные данные с помощью этого ключа, его изменение сделает все учетные данные нечитаемыми. Поэтому установите его один раз и больше не изменяйте эту строку.

Переменные окружения, определяющие работу webhooks

Четыре переменные определяют, как n8n представляется внешним сервисам. Ошибки в них — самая частая причина обращений в поддержку n8n.

  • N8N_HOST — это публичное имя хоста, n8n.example.com. Если оставить значение по умолчанию localhost при использовании прокси, редактор попытается загрузить собственный API по адресу localhost в вашем браузере, что приведет к ошибке.
  • N8N_PROTOCOL=https сообщает n8n, что трафик идет через TLS. Это позволяет устанавливать сессионную куку Secure и формировать URL-адреса https://.
  • N8N_PORT=5678 — это порт, который n8n использует внутри контейнера. Это не публичный порт; порт 443 занят прокси-сервером.
  • WEBHOOK_URL=https://n8n.example.com/ — самая критичная переменная. n8n формирует адреса webhooks для вставки в Stripe, GitHub или другие внешние сервисы на основе этих значений. Если переменная не установлена или указана неверно, n8n использует N8N_HOST:N8N_PORT и выдает вам https://n8n.example.com:5678/webhook/... или, что хуже, http://localhost:5678/webhook/.... Такие адреса выглядят правдоподобно и не вызывают ошибок при выводе, но они недоступны из интернета, поэтому запросы от внешних сервисов не доходят. Установите точный публичный базовый URL со слешем в конце. Затем убедитесь, что узел webhook отображает URL без указания порта.

N8N_PROXY_HOPS=1 указывает Express-серверу n8n доверять одному прокси-серверу. Это необходимо, чтобы ограничения частоты запросов (rate-limiting) и функции определения IP-адреса клиента видели реальный адрес, а не адрес прокси. Одна переменная, которую мы намеренно не устанавливаем — это N8N_RUNNERS_ENABLED. Исполнители задач (task runners) — процесс, в котором n8n запускает логику Code-node в отдельной песочнице — являются стандартом начиная с версии 1.69 и обязательны в ветке 2.x, на которой основан данный гайд. Старый способ включения через опцию устарел. Если вы установите её сейчас, n8n просто выведет уведомление о необходимости её удаления.

Первый запуск

docker compose up -d
docker compose ps
docker compose logs -f n8n

Успешная первая загрузка завершается строкой Editor is now accessible via:, перед которой должна быть строка n8n ready on ..., port 5678. docker compose ps должен отображать оба контейнера Up, при этом postgres должен иметь статус (healthy). Если n8n вошел в цикл Restarting, проверьте логи — причиной почти всегда является ошибка подключения к базе данных или права доступа к volume, описанные ниже.

TLS с использованием обратного прокси

n8n использует протокол HTTP на порту 5678; для терминации HTTPS необходимо внешнее решение. Существует два основных варианта.

Если вы уже используете несколько контейнеров, установите n8n за обратный прокси Traefik с автоматическим выпуском TLS-сертификатов с помощью нескольких labels — Traefik сам запросит и обновит сертификат.

Если это единственное приложение на сервере, проще использовать virtual host в nginx с сертификатом Let's Encrypt. Используйте настройку Certbot и nginx TLS для Ubuntu 24.04 для получения сертификата, затем используйте следующий server block:

server {
    listen 443 ssl;
    server_name n8n.example.com;

    ssl_certificate     /etc/letsencrypt/live/n8n.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.example.com/privkey.pem;

    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_read_timeout 3600;
        client_max_body_size 16m;
    }
}

Заголовки Upgrade и Connection "upgrade" обязательны. n8n передает обновления выполнения в реальном времени через WebSocket, и без этих двух строк страница входа загрузится, но затем зависнет с баннером «connection lost». proxy_read_timeout 3600 предотвращает разрыв длительных процессов по истечении стандартных 60 секунд nginx. Заголовок X-Forwarded-Proto $scheme дополняет N8N_PROXY_HOPS=1: он сообщает n8n, что исходный запрос был по HTTPS, хотя прокси обращается к приложению по HTTP. Это необходимо, чтобы n8n не пометил соединение как небезопасное и не отклонил свои собственные cookie.

Ваш первый рабочий процесс для проверки системы

Откройте https://n8n.example.com/, создайте учетную запись владельца (см. следующий раздел) и настройте минимальный рабочий процесс для проверки работоспособности пути: входящий webhook, HTTP-запрос и ответ.

  1. Добавьте узел Webhook. Установите метод POST и путь, например hello. Система отобразит два URL-адреса: Test URL и Production URL. Это основная причина половины жалоб на неработающие webhook. Test URL принимает только один вызов и работает только после нажатия кнопки Listen for test event; после этого срок его действия истекает. Production URL принимает вызовы всегда, когда рабочий процесс находится в статусе Active.
  2. Добавьте после него узел HTTP Request, направленный на любой публичный JSON API. Например, GET-запрос к https://api.github.com/zen вернет строку из одной строки, чего достаточно.
  3. Добавьте узел Respond to Webhook. В настройках узла Webhook установите параметр Respond в значение "Using Respond to Webhook node", чтобы вызывающая сторона получила результат работы HTTP-узла.
  4. Переключите рабочий процесс в состояние Active (в правом верхнем углу) и вызовите его: curl -X POST https://n8n.example.com/webhook/hello. Вы должны получить ту самую строку — входящий POST, вызов API и ответ. Это стандартная схема большинства реальных автоматизаций.

Вариант с расписанием заменяет узел Webhook на Schedule Trigger и вызывает эндпоинт модели. Использование Ollama, запущенной на том же VPS, — удобный способ создать ежедневный суммаризатор.

Управление пользователями вместо basic auth

В старых руководствах по n8n рекомендуется устанавливать N8N_BASIC_AUTH_ACTIVE=true. Эти переменные были удалены в n8n 1.0 и теперь не выполняют никаких функций. Современная аутентификация осуществляется через учетную запись владельца (owner account): при первом запуске редактора n8n требует создать пользователя с email и паролем. Этот этап обязателен — анонимный режим отсутствует. Создайте учетную запись сразу после первого запуска, прежде чем передавать URL-адрес другим пользователям: в период между docker compose up и отправкой первой формы экземпляр может быть захвачен любым, кто первым получит к нему доступ. Использование basic-auth через reverse-proxy является разумным дополнительным уровнем защиты, но это лишь второй фактор, а не основная аутентификация.

Резервное копирование: сначала ключ шифрования, затем база данных

Необходимо создать резервные копии двух объектов, которые невозможно заменить друг другом.

N8N_ENCRYPTION_KEY. Все учетные данные, которые вы сохраняете в n8n — API tokens, пароли к базам данных, OAuth secrets — зашифрованы в состоянии покоя с помощью этого ключа. Рабочие процессы (workflows) в Postgres будут бесполезны без этого ключа: если восстановить базу данных на новом сервере с другим ключом, n8n не сможет расшифровать ни одно учетное данные. Восстановление или сброс в этом случае невозможны. Ваш файл .env содержит этот ключ; скопируйте его вне сервера — в идеале используйте менеджер паролей — сразу после создания. Это самый важный элемент резервной копии.

База данных Postgres, необходимая для хранения рабочих процессов, истории выполнения и самих зашифрованных учетных данных:

docker compose exec -T postgres pg_dump -U n8n -d n8n \
  | gzip > n8n-db-$(date +%F).sql.gz

Настройте выполнение этой команды по расписанию и копируйте дамп за пределы сервера. Для восстановления на новом VPS: запустите стек один раз, чтобы база данных была создана, остановите n8n, загрузите дамп с помощью psql, установите тот же N8N_ENCRYPTION_KEY в переменную .env и запустите n8n. Сочетание того же ключа и дампа дает рабочую систему; новый ключ приведет к тому, что рабочие процессы не смогут использовать ни одно учетное данные.

Обновления: фиксируйте тег

В compose file намеренно используется n8nio/n8n:2.29.10 вместо latest. n8n выпускает новые минорные версии почти каждую неделю. Между ними могут меняться схема базы данных или поведение узлов. Использование latest может привести к автоматической миграции базы данных сразу после запуска контейнера. Фиксируйте версию, читайте release notes перед обновлением (там указаны критические изменения) и выполняйте обновление осознанно:

docker compose exec -T postgres pg_dump -U n8n -d n8n | gzip > pre-upgrade.sql.gz
# edit the image tag in docker-compose.yml, then:
docker compose pull n8n
docker compose up -d n8n
docker compose logs -f n8n

Это особенно важно при переходе на новые мажорные версии. Например, в версии 2.0 параметр N8N_BLOCK_ENV_ACCESS_IN_NODE был изменен на true по умолчанию. Любой Code node, который использовал process.env, потеряет к нему доступ, пока вы не вернете значение на false. В этом же релизе были введены строгие права доступа к файлу настроек. Читайте страницу breaking-changes для версии 2.0 перед переходом на новую мажорную версию. n8n запускает необходимые миграции базы данных автоматически при старте. Именно поэтому выполнение pg_dump перед обновлением обязательно. Поскольку учетные данные хранятся в зашифрованном виде с ключом в .env, а данные находятся в Postgres, контейнеры являются эфемерными. Обновляйтесь путем замены контейнеров, а для отката используйте предыдущий тег и восстановите дамп.

Failure modes, with the strings you will see

The requested webhook "POST hello" is not registered. A 404 from calling a webhook whose workflow is not Active, or from calling the test path when nobody is listening. Test paths (/webhook-test/...) answer only while you have clicked "Listen for test event"; production paths (/webhook/...) answer only when the workflow toggle is on. The sibling This webhook is not registered for GET requests. Did you mean to make a POST request? means the method is wrong — the node expects POST and you sent GET.

The webhook URL shows a :5678 or localhost. The node displays https://n8n.example.com:5678/webhook/... or http://localhost:5678/.... WEBHOOK_URL is unset or wrong, so n8n built the address from N8N_HOST:N8N_PORT instead of your public base. Set WEBHOOK_URL=https://n8n.example.com/, recreate the container with docker compose up -d, and the port disappears.

There was a problem loading init data in the browser. The editor loaded but cannot reach its own backend API. Behind a proxy this is almost always a wrong N8N_HOST or WEBHOOK_URL, a proxy missing the WebSocket Upgrade headers, or N8N_PROTOCOL not matching how you connect. Confirm the four public-facing variables and that the proxy forwards Upgrade and Connection.

password authentication failed for user "n8n" in the logs, with the container restarting. The password n8n sends does not match what the database was initialised with. The trap: Postgres reads POSTGRES_PASSWORD only when it initialises an empty data directory. Start the stack once, then change POSTGRES_PASSWORD in .env, and the existing postgres_data volume still holds the old password. Set it back to the original, or, if you have no data to keep, docker compose down and docker volume rm the postgres volume, then bring it up fresh.

EACCES: permission denied, open '/home/node/.n8n/config' on start. n8n runs as the node user (UID 1000) and cannot write its config directory. This bites people who bind-mount a host folder (./n8n_data:/home/node/.n8n) owned by root. Use the named volume shown above, or if you insist on a bind mount, sudo chown -R 1000:1000 ./n8n_data first.

Permissions 0644 for n8n settings file /home/node/.n8n/config are too wide. Changing permissions to 0600.. From the 2.x line n8n enforces 0600 on that settings file by default and fixes it itself on boot — this log line means it already corrected the mode, commonly after a bind mount or after a restore copied the file back with loose permissions. No action is needed; set N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false only if your filesystem genuinely cannot support permissions.

Mismatching encryption keys — the fuller line says the encryption key in the settings file /home/node/.n8n/config does not match the N8N_ENCRYPTION_KEY in your environment. The key in your environment differs from the one n8n wrote into its data volume on a previous run — most often because n8n generated a random key on an earlier boot when the variable was unset, and you then set a different one. Put the original key back in .env, or, only if you truly have no stored credentials worth keeping, delete the config file inside the n8n_data volume and let n8n regenerate it — accepting that existing credentials become unreadable.

A login banner about secure cookies: Your n8n server is configured to use a secure cookie, however you are either visiting this via an insecure URL, or using Safari. You set N8N_PROTOCOL=https but reached n8n over plain HTTP — usually by hitting the IP and port directly instead of the HTTPS proxy. Reach it via https://n8n.example.com/. Only if you genuinely cannot use HTTPS should you set N8N_SECURE_COOKIE=false, and never on an internet-facing box.

To put a language model inside those workflows, see building AI workflows with Claude and n8n.

FAQ

Стоит ли использовать SQLite или Postgres для n8n?

SQLite (по умолчанию) подходит для ознакомления с n8n или для персонального использования, когда выполняется только один workflow одновременно. Переходите на Postgres для любых критически важных задач: блокировка одного записителя в SQLite вызывает database is locked при высокой нагрузке, а Postgres обеспечивает корректное резервное копирование с помощью pg_dump. Миграция в дальнейшем выполняется вручную, поэтому если данные важны, сразу используйте Postgres.

Почему мои webhooks в n8n не срабатывают?

Чаще всего причина в WEBHOOK_URL. Если переменная не установлена или указана неверно, n8n формирует адреса webhook на основе N8N_HOST:N8N_PORT — часто с использованием :5678 или localhost. Такие адреса выглядят корректно, но недоступны из интернета, поэтому запросы от отправителя не доходят. Установите WEBHOOK_URL=https://n8n.example.com/ и убедитесь, что узел отображает URL без указания порта. Вторая причина — вызов webhook, для которого workflow не переведен в состояние Active, что возвращает The requested webhook ... is not registered..

Что необходимо резервно копировать в n8n?

Две вещи. Файл N8N_ENCRYPTION_KEY из вашего .env, так как все сохраненные учетные данные зашифрованы с его помощью; при его потере расшифровать их будет невозможно — скопируйте его с сервера в день создания. Также необходим pg_dump базы данных Postgres, содержащей workflows, историю и учетные данные. Для восстановления требуются оба компонента: тот же ключ и дамп.

Как настроить HTTPS для n8n?

n8n работает по протоколу HTTP на порту 5678; обратный прокси-сервер (reverse proxy) должен использоваться для терминации TLS. Привяжите n8n к 127.0.0.1:5678, чтобы доступ был только у прокси, затем используйте Traefik с автоматическими сертификатами или nginx с сертификатом Let's Encrypt. Установите N8N_PROTOCOL=https и WEBHOOK_URL=https://your-host/, и убедитесь, что прокси передает заголовки WebSocket Upgrade, иначе редактор будет зависать.

Как безопасно обновить n8n?

Используйте конкретный тег образа вместо latest, сначала сделайте pg_dump, так как n8n автоматически запускает миграции при старте, изучите примечания к релизу на предмет breaking changes, затем обновите тег и запустите docker compose pull n8n && docker compose up -d n8n. Контейнер можно удалить, поэтому для отката верните предыдущий тег и восстановите дамп, сделанный перед обновлением.