Статья · ИНСТРУКЦИИ · 3 авг. 2026 · 12 мин чтения

Установка n8n на Ubuntu 24.04: Docker Compose и SSL

Всеволод
Технический писатель · IT, облачные сервисы
12 мин чтения

n8n — low-code платформа для автоматизации бизнес-процессов: вебхуки, интеграции с сотнями сервисов, обработка данных и ИИ-агенты собираются мышкой в визуальном редакторе. Развернуть n8n self-hosted под Ubuntu 24.04 LTS — значит держать все данные и учётные записи у себя, без лимитов на исполнения и без зависимости от внешнего облака. Ниже — воспроизводимый сценарий: установка n8n через Docker Compose, боевой стек с PostgreSQL, обратный прокси Nginx с корректными вебхуками и бесплатный SSL от Let's Encrypt. На выходе — рабочая инфраструктура по вашему домену через HTTPS, готовая к продакшну: параллельные вебхуки, длинные workflow и восстановление после сбоя.

Зачем разворачивать n8n на своём сервере и что для этого нужно

n8n распространяется в двух формах: облачный n8n Cloud по подписке и self-hosted-версия с открытым кодом. Для российского бизнеса выбор чаще предопределён: платить за зарубежную подписку n8n Cloud из РФ непросто, а данные workflow физически лежат за пределами страны — и с 1 июля 2025 года хранение персональных данных россиян на зарубежных серверах прямо ограничено законом. Собственный инстанс снимает эти вопросы.

n8n Cloud или собственный сервер: контроль над данными и отсутствие лимитов

Главный аргумент за self-hosted — данные. Через n8n проходят токены доступа к CRM, ключи платёжных систем, персональные данные клиентов из форм и таблиц. Когда инстанс работает на вашем VPS, эти сведения не покидают контролируемый контур — это напрямую отвечает требованиям 152-ФЗ к обработке персональных данных на территории РФ.

Второй аргумент — экономика. В тарифах n8n Cloud количество исполнений ограничено пакетом, и активная автоматизация быстро упирается в потолок. На своём сервере лимит один — ресурсы железа: если workflow вызывает вебхук тысячу раз в час, вы платите за сервер, а не за каждый запуск. Взамен обновления, бэкапы и мониторинг ложатся на вас.

Сколько ресурсов нужно под нагрузку: конфигурации VPS (RAM/CPU/диск)

Потребление ресурсов n8n зависит от характера использования. Сам процесс Node.js в простое потребляет немного, но каждый параллельный запуск — это память, а тяжёлые ноды (большие JSON, файлы, ИИ-запросы) дают пики. На 1 GB RAM инстанс с PostgreSQL стабильно сталкивается с OOM уже на паре одновременных вебхуков — это нижняя граница, ниже которой ставить не стоит.

Ориентир по конфигурациям VPS/VDS:

Профиль нагрузки

vCPU

RAM

SSD

Комментарий

Тест, до ~15 простых workflow, редкие запуски

2

2 ГБ

30 ГБ

Минимум для n8n + PostgreSQL в Docker

Рабочий инстанс, десятки workflow, регулярные вебхуки

2–4

4 ГБ

60 ГБ

Комфортный дефолт для большинства команд

Интенсивная автоматизация, ИИ-ноды, пики параллелизма

4+

8 ГБ

80–120 ГБ

Запас под задачи с большими данными

Queue mode с Redis и отдельными воркерами

4–8

8–16 ГБ

120 ГБ+

Горизонтальное масштабирование

Диск считайте с запасом: база PostgreSQL растёт за счёт истории исполнений, и без автоочистки таблица раздувается незаметно. SSD здесь оправдан — история запусков читается и пишется постоянно.

Подойдёт любой VPS с чистой Ubuntu 24.04 и root-доступом. Например, эту задачу закрывает аренда сервера с Ubuntu от Cloud4U: VDS с преднастроенной Ubuntu поднимается за несколько минут, в тариф уже входят SSD-хранилище, защита от DDoS и автоматические резервные копии на стороне провайдера — удобный второй рубеж поверх собственных бэкапов n8n. Инструкция при этом вендоронезависима: те же команды отработают на любом сервере с Ubuntu 24.04.

Подготовка сервера Ubuntu 24.04: домен, Docker и базовая защита

Прежде чем разворачивать контейнеры, приведём чистый сервер в порядок: привяжем домен, заведём рабочего пользователя вместо root, поставим Docker и закроем лишние порты.

Домен, A-запись и непривилегированный пользователь

n8n за прокси и с SSL проще всего развернуть на полноценном домене или поддомене. Let's Encrypt с недавних пор умеет выпускать сертификаты и на IP-адрес, но это отдельный краткосрочный сценарий; для боевого инстанса берите домен. Заведите A-запись, указывающую на публичный IP сервера. Обычно под автоматизацию выделяют поддомен вида n8n.example.ru:

Тип: A
Имя: n8n
Значение: 203.0.113.10   (IP вашего сервера)
TTL: 300

DNS обновляется не мгновенно — от нескольких минут до пары часов. Проверить, что запись разошлась:

dig +short n8n.example.ru

Пока команда не вернёт IP сервера — с выпуском сертификата спешить нет смысла.

Дальше безопасность хоста. Работать под root на постоянной основе не стоит: заведём отдельного пользователя с правами sudo и добавим его в группу docker.

adduser deploy
usermod -aG sudo deploy
usermod -aG docker deploy

После этого переподключитесь по SSH уже под deploy. Хорошая практика — отключить вход root по паролю и перейти на ключи, но это отдельная тема; дальше все команды идут от непривилегированного пользователя.

Установка Docker и Docker Compose, настройка firewall ufw

Ставить Docker из стандартного репозитория Ubuntu не надо — там версия отстаёт. Подключаем официальный репозиторий Docker с проверкой GPG-ключа:

sudo apt update && sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Современный Docker Compose ставится как плагин и вызывается через docker compose (с пробелом), а не устаревшей командой docker-compose. Проверка:

docker --version
docker compose version

Теперь firewall. Держать наружу лишние порты незачем — оставляем SSH и веб:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Порт 5678, на котором слушает n8n, наружу мы не открываем сознательно — доступ к нему пойдёт только через локальный прокси.

Боевой стек: docker-compose.yml с PostgreSQL и переменными окружения

Дефолтный n8n хранит данные в SQLite — одном файле. Для продакшна не годится: SQLite блокирует базу на запись целиком, и при параллельных вебхуках возникают блокировки базы данных (database locks) — пока один workflow пишет результат, остальные ждут и под нагрузкой завершаются с ошибками. PostgreSQL снимает это за счёт построчных блокировок. Поэтому боевой стек — два контейнера: n8n и Postgres рядом.

Создайте рабочий каталог и в нём — docker-compose.yml:

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

  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    restart: unless-stopped
    ports:
      - '127.0.0.1:5678:5678'
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
      - DB_POSTGRESDB_USER=${POSTGRES_USER}
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
      - N8N_HOST=${N8N_HOST}
      - N8N_PROTOCOL=https
      - N8N_PORT=5678
      - WEBHOOK_URL=https://${N8N_HOST}/
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - N8N_SECURE_COOKIE=true
      - N8N_RUNNERS_ENABLED=true
      - GENERIC_TIMEZONE=Europe/Moscow
      - NODE_ENV=production
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  postgres_data:
  n8n_data:

Нюансы, которые отличают этот файл от того, что часто копируют:

  • Никакого version: в начале — в Compose v2 ключ устарел и только засоряет вывод предупреждениями.
  • Образ docker.n8n.io/n8nio/n8n — рекомендованный сейчас реестр, а не старый n8nio/n8n с Docker Hub.
  • Порт привязан к 127.0.0.1 — снаружи достучаться до n8n напрямую нельзя. Пропустите 127.0.0.1 — и порт 5678 окажется открыт всему интернету в обход прокси.
  • healthcheck у Postgres плюс depends_on ... condition: service_healthy — n8n стартует только после того, как база готова принимать соединения.

Файл .env, N8N_HOST, WEBHOOK_URL и другие переменные окружения

Секреты в compose-файл не пишут — их выносят в .env рядом, Docker Compose подхватывает его автоматически.

# .env
POSTGRES_USER=n8n
POSTGRES_PASSWORD=смените_на_длинный_случайный_пароль
POSTGRES_DB=n8n

N8N_HOST=n8n.example.ru
N8N_ENCRYPTION_KEY=сюда_сгенерированный_ключ

Ключевые переменные окружения:

  • N8N_HOST — ваш домен. По нему n8n формирует ссылки и понимает, под каким именем живёт.
  • WEBHOOK_URL — самая частая причина неработающих вебхуков. Это базовый адрес, который n8n подставляет во все URL вебхуков; если он не совпадает с реальным HTTPS-доменом, внешние сервисы обращаются не по тому адресу. Должно быть строго https://ваш-домен/.
  • N8N_PROTOCOL=https и N8N_SECURE_COOKIE=true — говорят n8n, что он работает под HTTPS.
  • GENERIC_TIMEZONE — таймзона для нод расписания (Cron/Schedule). Без неё запуски по времени поедут относительно вашего часового пояса.

Права на .env стоит сузить: chmod 600 .env — в файле пароли и ключ шифрования.

N8N_ENCRYPTION_KEY: почему потеря ключа означает потерю всех учётных данных

Момент, который пропускают почти все инструкции, а он критически важен. Все учётные данные (credentials) в n8n — токены API, пароли, ключи, OAuth-доступы — хранятся в базе в зашифрованном виде, и шифруются они значением N8N_ENCRYPTION_KEY. Если ключ задан явно в .env, вы контролируете его; если не задан — n8n при первом старте генерирует его сам и кладёт в ~/.n8n/config внутри тома.

Отсюда следствие: дамп базы без ключа шифрования бесполезен. Восстановите PostgreSQL на новом сервере, но забудьте перенести ключ — и все credentials расшифровать не удастся, их придётся заводить заново. Поэтому ключ задают явно и хранят отдельно от сервера — в менеджере секретов.

Сгенерировать стойкое значение:

openssl rand -hex 32

Полученную строку впишите в N8N_ENCRYPTION_KEY в .env до первого запуска. Менять ключ на работающем инстансе нельзя — ранее сохранённые credentials перестанут расшифровываться.

Запускаем стек:

docker compose up -d
docker compose logs -f n8n

В логах должно появиться, что n8n слушает порт 5678. Наружу он пока не смотрит — этим займётся прокси.

Обратный прокси и бесплатный SSL: Nginx с WebSocket и Let's Encrypt

n8n умеет отдавать HTTPS сам, но на продакшне перед ним ставят обратный прокси (reverse proxy): он терминирует SSL, добавляет заголовки безопасности и разгружает приложение. Из вариантов — Nginx, Traefik, Caddy — возьмём Nginx: он прозрачен, стоит почти везде и даёт полный контроль.

Конфиг Nginx с поддержкой WebSocket и таймаутами для длинных workflow

Ставим Nginx на хост (не в контейнер — так проще с certbot):

sudo apt install -y nginx

Ключевой момент, на котором часто возникают проблемы: редактор n8n работает поверх WebSocket. Push-соединение держит редактор «живым» — показывает статус исполнения в реальном времени, обновляет ноды. Если прокси не пробрасывает заголовки Upgrade, интерфейс подвисает, кнопка выполнения крутится вечно, а соединение обрывается по таймауту.

Создайте /etc/nginx/sites-available/n8n:

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

    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 "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;

        # Таймауты под длинные workflow: исполнение может идти минутами
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;

        proxy_buffering off;
    }
}

Почему proxy_read_timeout 3600s, а не дефолтные 60 секунд: workflow с ожиданием ответа внешнего API, паузой или обработкой большого объёма данных легко исполняется дольше минуты. С коротким таймаутом Nginx обрывает соединение на середине, и в интерфейсе вы видите обрыв, хотя на бэкенде процесс ещё шёл. proxy_buffering off нужен, чтобы серверные события доходили до редактора сразу.

Активируем конфиг и проверяем синтаксис:

sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Выпуск и автопродление сертификата Let's Encrypt через certbot

Теперь бесплатный SSL от Let's Encrypt. Ставим certbot с плагином для Nginx:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d n8n.example.ru

Certbot проверит владение доменом по HTTP-01, выпустит сертификат и сам перепишет конфиг Nginx: добавит блок на 443 порту, пропишет пути к сертификату и настроит редирект с HTTP на HTTPS. После этого перепроверьте X-Forwarded-Proto $scheme — именно он сообщает n8n, что клиент пришёл по HTTPS.

Подводные камни certbot рядом с Docker:

  • Порт 80 должен быть свободен для проверки. Мы отдали его Nginx на хосте, а не контейнеру — конфликта нет.
  • Автопродление уже настроено. Пакет certbot ставит системный таймер, который дважды в день проверяет сертификаты и обновляет те, которым осталось меньше 30 дней. Проверить, что механизм жив:
sudo systemctl status certbot.timer
sudo certbot renew --dry-run

Если --dry-run отработал без ошибок — реальное автопродление тоже пройдёт. При обновлении Nginx перечитывает конфиг сам, ронять контейнеры n8n не требуется.

Откройте https://n8n.example.ru — должна появиться форма создания аккаунта администратора. Заведите владельца (owner) — это первый пользователь. Учтите, что штатное восстановление пароля работает через письмо, а SMTP по умолчанию не настроен, — поэтому логин и пароль владельца сохраните сразу.

Эксплуатация: обновление, резервное копирование и типовые ошибки

Инстанс открылся по HTTPS — половина дела. Вторая половина в том, чтобы он пережил обновления и сбои.

Резервное копирование и восстановление: том PostgreSQL и ключ шифрования

Бэкап n8n — это три вещи, и пропуск любой ломает восстановление:

  1. Том PostgreSQL — сами workflow, история исполнений, зашифрованные credentials.
  2. Каталог ~/.n8n (том n8n_data) — конфиг инстанса и служебные файлы.
  3. N8N_ENCRYPTION_KEY — ключ, без которого содержимое базы не расшифровать. Он у вас в .env, но убедитесь, что копия .env тоже уходит в бэкап и хранится в защищённом месте.

Дамп базы снимается штатным pg_dump изнутри контейнера:

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

Эту команду логично оформить в виде скрипта и запустить по cron — ежедневно ночью, с ротацией копий за 7–14 дней и выгрузкой в отдельное хранилище (не на тот же сервер).

Восстановление на новом сервере: разворачиваете тот же docker-compose.yml, переносите .env с тем же N8N_ENCRYPTION_KEY, поднимаете Postgres и заливаете дамп:

gunzip < n8n_db_2026-07-15.sql.gz | \
  docker compose exec -T postgres psql -U n8n -d n8n

Если ключ совпадает — все credentials восстанавливаются без повторного ввода. Если ключ забыли — база восстановится, но токены и пароли внутри останутся нечитаемыми. Это и есть цена потерянного ключа шифрования.

Обновление n8n и разбор типовых ошибок (webhook not registered, редирект-петля, 502)

Обновление n8n на Docker Compose — две команды: загружаем свежий образ и пересоздаём контейнер.

docker compose pull
docker compose up -d

Данные при этом на месте — они в томах, а не в контейнере. Разумный порядок: сначала снять бэкап базы, потом обновляться. Перед апдейтом полезно заглянуть в changelog на предмет ломающих изменений.

Типовые ошибки и их причины:

  • Webhook not registered / вебхук не срабатывает. В девяти случаях из десяти виноват неверный WEBHOOK_URL — он должен точно равняться https://ваш-домен/. Вторая причина: production-URL требует включённого (active) workflow, тестовый URL живёт, только пока открыт редактор.
  • Бесконечный редирект / не удаётся войти. Типичная ситуация при работе за прокси: N8N_SECURE_COOKIE=true требует HTTPS, а прокси не передаёт n8n, что соединение защищённое. Браузер получает secure-cookie по HTTP, отбрасывает её, n8n снова перенаправляет на страницу входа — и так по кругу. Решается проброшенным заголовком X-Forwarded-Proto https и корректным N8N_PROTOCOL=https.
  • 502 Bad Gateway. Nginx не смог достучаться до апстрима. Причины по частоте: контейнер n8n не поднялся (смотрим docker compose ps и docker compose logs n8n), не совпадает порт в proxy_pass и в проброске контейнера, либо база не готова и n8n перезапускается по кругу. Начинайте диагностику с логов контейнера.

Главные выводы

Разница между «n8n запустился» и «n8n работает в продакшне» держится на нескольких решениях. PostgreSQL вместо SQLite снимает блокировки базы при параллельных вебхуках; привязка порта к 127.0.0.1 и весь трафик через Nginx закрывают приложение от прямого доступа снаружи; корректный проброс WebSocket и увеличенные таймауты не дают редактору зависать на длинных workflow; сертификат Let's Encrypt с проверенным автопродлением обеспечивает HTTPS.

Отдельно держите в голове две вещи, потеря которых обходится дороже всего: ключ шифрования N8N_ENCRYPTION_KEY и регулярные бэкапы тома PostgreSQL. Дамп базы без ключа не расшифровать, а свежий ключ на восстановленной базе не поможет — эти две вещи бэкапятся и хранятся только вместе. Настройте автоматический дамп с выгрузкой на отдельное хранилище, положите ключ в менеджер секретов — и тогда даже полная потеря сервера означает не катастрофу, а переустановку по инструкции выше с восстановлением из копии.

Вверх!