n8n.cloud считает активные workflow и запуски и не достаёт до сервисов в вашей частной сети. Свой n8n на VPS снимает оба ограничения за цену тарифа в пару долларов. Разбираем docker compose, том с данными, три переменные окружения, которые молча ломают вебхуки, если задать их неправильно, вход через встроенную учётную запись владельца, периметр из nginx и ufw и то, что нужно бэкапить, чтобы не потерять workflow вместе с сервером.
n8n.cloud удобен ровно до тех пор, пока не упираетесь в тарифный лимит активных workflow или запусков в месяц, либо пока не понадобилось подключить сценарий напрямую к базе данных, которая не торчит в интернет. Развернуть n8n на VPS - способ снять оба ограничения за цену тарифа в пару долларов: workflow и запусков ровно столько, сколько выдержит сервер, а к внутренним сервисам можно ходить напрямую. Ниже - развёртывание через docker compose, том для данных, три переменные окружения, которые молча ломают вебхуки при первой же ошибке, встроенная учётная запись владельца вместо давно убранного basic auth, периметр из nginx и ufw и то, что реально нужно бэкапить.
Коротко. n8n ставится одним compose-файлом из образа
n8nio/n8n, слушает порт 5678 и хранит всё - workflow, учётные данные подключений, историю запусков и ключ шифрования - в SQLite-файле внутри каталога/home/node/.n8n, который обязательно выносится томом на хост. Ресурсов инстансу нужно немного: по документации n8n простаивающий процесс занимает около 100 МБ, а рабочий диапазон для несложных сценариев - 320 МБ - 2 ГБ, это не LLM-стек. За обратным прокси адрес, который n8n собирает сам изN8N_HOST/N8N_PROTOCOL/N8N_PORT, не совпадает с реальным публичным URL - вебхуки регистрируются на внешних сервисах именно по нему, и если он неверный, интеграции молча ломаются. Правильный адрес задаётся отдельно черезN8N_WEBHOOK_URL. Логин через переменные окружения (N8N_BASIC_AUTH_ACTIVE) в текущих версиях не работает - авторизация идёт через встроенную учётную запись владельца. Порт наружу не публикуйте:127.0.0.1:5678:5678в compose, nginx с доменом и HTTPS сверху, ufw на 80 и 443.
n8n - инструмент визуальной автоматизации: вы соединяете узлы («ноды») в схему - пришёл вебхук, дёрнули внешний API, записали результат в таблицу - без своего бэкенда под каждый такой сценарий. n8n.cloud - управляемая версия этого же продукта, и для разовой задачи она разумный выбор: не нужно поднимать сервер, обновления происходят сами. Но у управляемого тарифа есть два системных ограничения, которые упираются в стену рано или поздно.
Первое - лимиты плана: число активных workflow, запусков в месяц, участников команды. Второе, более неприятное для разработчика или агентства - облачный n8n физически не может достучаться до сервиса, который не выставлен в открытый интернет. Внутренняя PostgreSQL на VPS клиента, API во внутренней сети, локальный Redis - всё это либо приходится временно открывать наружу ради интеграции, либо тащить через промежуточный туннель. На собственном VPS n8n и целевой сервис могут жить в одной приватной сети без единого открытого порта.
Своя причина не менее практичная: лицензия. n8n распространяется по Sustainable Use License - разрешает бесплатно ставить, менять и использовать продукт для своих внутренних задач, запрещает перепродавать его как услугу другим. Именно поэтому self-hosted n8n на VPS - легальный и бесплатный вариант для своей команды или клиентских проектов, а не серая зона.
Что важно | n8n.cloud | n8n на своём VPS |
|---|---|---|
Лимит активных workflow и запусков | По тарифному плану | Ограничен только ресурсами сервера |
Доступ к внутренним сервисам без открытого порта | Нет, нужен публичный адрес | Да, если сервисы в одной сети или на одном хосте |
Обновление версии и патчи безопасности | Делает n8n | Делаете вы: |
Бэкап данных | Отвечает провайдер | Отвечаете вы, ниже - как |
Ежемесячная стоимость | От платного тарифа за место в команде | Цена тарифа VPS, независимо от числа workflow |
n8n - не языковая модель и не векторная база: он не держит гигабайты весов в памяти, а выполняет workflow последовательно, шаг за шагом. По документации проекта простаивающий инстанс занимает около 100 МБ памяти, а типичный диапазон для несложных сценариев - от 320 МБ до 2 ГБ, в зависимости от числа воркфлоу, объёма данных, которые проходят через ноды, и от того, сколько запусков идёт параллельно. Это ориентир, а не гарантия: обработка большого файла внутри одной ноды - например, парсинг многостраничного PDF или загрузка тяжёлого CSV в память целиком - может дать разовый скачок памяти сильно выше среднего.
Сценарий | Ориентир по памяти на весь сервер |
|---|---|
Личные автоматизации, несколько workflow, редкие срабатывания | 1-2 ГБ |
Небольшая команда, регулярные вебхуки и интеграции с API | 2-4 ГБ |
Много параллельных запусков, тяжёлая обработка данных в нодах, вынесенная PostgreSQL | 4-8 ГБ и выше |
Диск считайте так же скромно: сам образ и его слои - меньше гигабайта, дальше растёт файл SQLite вместе с историей запусков. n8n по умолчанию хранит выполненные запуски бессрочно, и на активном инстансе эта история годами способна разрастись до заметного объёма - если диск маленький, настройте автоочистку старых executions в настройках инстанса, это отдельная переменная окружения и в объём этой статьи не входит.
Ставим Docker из репозитория самого Docker, а не из репозитория Ubuntu: пакет docker.io из дистрибутива отстаёт по версиям, и docker compose в нём отдельная история. Команды ниже - официальный способ, они одинаковы для Ubuntu 22.04, 24.04 и 26.04.
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] 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-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
$(. /etc/os-release && echo "$VERSION_CODENAME") подставляет кодовое имя вашей версии Ubuntu, строку не нужно править руками.docker-compose-plugin - это docker compose в два слова. Старый docker-compose через дефис - отдельный устаревший инструмент.Проверка: docker compose version должен вывести строку вида Docker Compose version v2.x.x. Если вместо этого docker: 'compose' is not a docker command - плагин не установился, повторите последнюю команду.
Теперь каталог для данных. n8n хранит workflow, учётные данные для подключений и историю запусков в файле SQLite внутри каталога /home/node/.n8n - это фиксированный путь внутри образа, менять его нет смысла. Каталог обязательно выносится томом на хост, иначе любой docker compose down сотрёт все workflow вместе с контейнером.
sudo mkdir -p /opt/n8n/data
cd /opt/n8n
sudo chown -R 1000:1000 /opt/n8n/data
data - сюда примонтируется /home/node/.n8n из контейнера, вся персистентность именно здесь.chown 1000:1000 - процесс внутри образа работает от пользователя node, а не от root, и без прав на запись контейнер не сможет создать файл базы данных при первом старте.Кладём /opt/n8n/docker-compose.yml:
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
- N8N_HOST=n8n.example.com
- N8N_PROTOCOL=https
- N8N_PORT=5678
- N8N_WEBHOOK_URL=https://n8n.example.com/
- N8N_PROXY_HOPS=1
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- N8N_ENCRYPTION_KEY=замените-на-случайную-строку-от-32-символов
- GENERIC_TIMEZONE=Europe/Amsterdam
- TZ=Europe/Amsterdam
volumes:
- ./data:/home/node/.n8n
127.0.0.1:5678:5678 - порт публикуется только на loopback, снаружи его не существует. Наружу будет смотреть nginx, разбор - в разделе про периметр.N8N_HOST, N8N_PROTOCOL, N8N_PORT - из них n8n сам собирает адрес, по которому он себя видит. За обратным прокси этот адрес не совпадает с реальным публичным: внутри контейнер слушает 5678 по http, снаружи должен быть домен по https на 443.N8N_WEBHOOK_URL - публичный адрес, который n8n показывает в интерфейсе редактора и подставляет во все внешние интеграции (например, при регистрации вебхука в Telegram или Stripe). Это актуальное имя переменной: старая WEBHOOK_URL считается устаревшей начиная с версии 2.35.0. Не задать её или задать с ошибкой в домене - вебхук создастся, но внешний сервис будет стучаться по неверному адресу, и это не выдаст явной ошибки в момент создания workflow.N8N_PROXY_HOPS=1 - говорит n8n, что перед ним стоит один обратный прокси, и заголовку X-Forwarded-Proto от него можно доверять. Без этой переменной n8n может решить, что запрос пришёл по обычному http, даже если снаружи всё было по https - и тогда упрётся в защиту куки, о которой ниже.N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true - требует, чтобы файл с настройками и ключом шифрования внутри тома был читаем только владельцем. Это из примера в официальной документации, назначение - не оставлять секреты с правами на чтение для всех.N8N_ENCRYPTION_KEY - без этой переменной n8n сам сгенерирует ключ и сохранит его в тот же том при первом запуске. Указать свой ключ явно стоит ради бэкапа: если вы когда-нибудь захотите восстановить только дамп базы данных на новом сервере без переноса всего тома целиком, без этого же самого ключа зашифрованные учётные данные подключений останутся нечитаемыми. Генерируется командой openssl rand -hex 32.GENERIC_TIMEZONE и TZ - часовой пояс, по которому будут срабатывать расписания (cron-триггеры) внутри workflow. Не указать - триггеры сработают по UTC, что для расписаний вроде «каждый день в 9 утра» обычно не то, что вы имели в виду.Поднимаем:
cd /opt/n8n
sudo docker compose up -d
sudo docker compose ps
В выводе ps статус должен быть running, а в колонке портов - 127.0.0.1:5678->5678/tcp. Если статус restarting - смотрите sudo docker compose logs -n 50 n8n: на этом этапе почти всегда виноваты права на том с данными.
Если вы уже разворачивали сервисы вроде WordPress или старых версий других open-source проектов, привычка - искать переменные вида *_BASIC_AUTH_USER и задавать логин с паролем прямо в compose-файле. У n8n такие переменные (N8N_BASIC_AUTH_ACTIVE, N8N_BASIC_AUTH_USER, N8N_BASIC_AUTH_PASSWORD) существовали, но начиная с версии 1.0 базовая авторизация из продукта убрана, и эти переменные больше ни на что не влияют - оставить их в файле не вредно, но и не действует.
Вместо этого при первом открытии интерфейса n8n сам показывает форму создания учётной записи владельца - почта и пароль. Это не опциональный шаг, который можно пропустить: без него в интерфейс не попасть вообще. Дальше из-под этой учётной записи в разделе управления пользователями можно пригласить участников с более узкими ролями - разница по сути та же, что в статье про AnythingLLM: одна общая точка входа против полноценных ролей для команды.
Добираемся до интерфейса для первичной настройки, не открывая ничего наружу, - через SSH-туннель:
ssh -L 5678:127.0.0.1:5678 root@ВАШ_IP
Пока сессия открыта, http://127.0.0.1:5678 в вашем браузере - это порт на сервере. Создайте учётную запись владельца сейчас, до того как появится домен и публичный доступ, а не после.
Как и с любым другим сервисом в Docker, здесь работает то же правило периметра, которое подробно разобрано в статье про AnythingLLM на VPS: Docker при публикации порта пишет собственные правила в iptables раньше тех, куда пишет ufw, поэтому запрет вроде ufw deny 5678 здесь ничего не даёт, а ufw status при этом выглядит совершенно спокойно. Строка 127.0.0.1:5678:5678 в compose-файле выше уже решает эту проблему - порт просто не существует снаружи, и весь внешний трафик обязан идти через nginx.
Серверный блок - обычный обратный прокси с заголовками, которые нужны n8n для вебхуков и живых обновлений в редакторе:
server {
listen 80;
server_name n8n.example.com;
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
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 Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
X-Forwarded-Proto $scheme - именно этот заголовок n8n читает благодаря N8N_PROXY_HOPS=1, чтобы понять, что снаружи был https, даже если сам он отвечает по обычному http.Upgrade и Connection - редактор n8n держит соединение для живых обновлений интерфейса, без этих заголовков часть UI будет работать рывками.client_max_body_size 50M - лимит на тело запроса к вебхукам. Умолчание nginx - 1 МБ, и любой вебхук с файлом или крупным JSON-payload’ом упрётся в 413. 50 МБ - разумный старт, поднимайте под свою нагрузку.Дальше по стандартной схеме: sudo nginx -t, и только если проверка прошла - sudo systemctl reload nginx. Сертификат TLS ставится отдельным шагом после того, как HTTP уже отвечает - обычно через certbot:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d n8n.example.com
После установки сертификата N8N_PROTOCOL=https и N8N_WEBHOOK_URL с https:// в compose-файле должны совпадать с тем, что реально отдаёт сервер - иначе браузер начнёт жаловаться на «незащищённую куку» при входе по паролю, потому что n8n ждёт https, а получает http, или наоборот. Полный разбор конфига, минимального набора правил ufw и того, почему reload, а не restart, - в статье про nginx и ufw перед вашим приложением.
Полноценный бэкап n8n - это каталог /home/node/.n8n целиком (или дамп внешней базы, если вы подключили PostgreSQL вместо SQLite), а не только сами workflow. Внутри, кроме файлов с логикой автоматизаций, лежат зашифрованные учётные данные для подключений, история запусков и ключ шифрования - тот самый, который вы либо задали переменной N8N_ENCRYPTION_KEY, либо n8n сгенерировал сам при первом старте.
Есть отдельный способ - CLI-экспорт, он удобен, чтобы перенести конкретные workflow между инстансами, но не заменяет полный бэкап:
sudo docker compose exec n8n n8n export:workflow --backup --output=/home/node/.n8n/backup/workflows/
sudo docker compose exec n8n n8n export:credentials --backup --output=/home/node/.n8n/backup/credentials/
--backup - сокращение для «экспортировать всё, красиво отформатировать, разложить по отдельным файлам»./home/node/.n8n - если вывести файлы в любой другой путь внутри контейнера, они пропадут при следующем пересоздании контейнера, потому что тот путь никуда не смонтирован.Для реального плана резервного копирования сервера - тома целиком плюс шифрование, хранение вне сервера, автоматический запуск по расписанию - используйте общий метод из статьи как бэкапить свой сервер на HIP: в случае n8n источником для restic или дампа становится каталог /opt/n8n/data целиком, а не отдельные экспортированные файлы.
sudo docker compose logs -n 50 n8n. Ошибки доступа к файлам почти всегда означают неверные права на том - повторите chown -R 1000:1000 /opt/n8n/data.N8N_WEBHOOK_URL, а что-то вроде адреса контейнера, значит переменная не подхватилась при последнем перезапуске. Проверьте, что она реально попала в контейнер: docker compose exec n8n env | grep WEBHOOK.N8N_PROTOCOL=https; в nginx есть строка proxy_set_header X-Forwarded-Proto $scheme; в compose есть N8N_PROXY_HOPS=1.docker compose config путь ./data:/home/node/.n8n указывает туда, где реально лежат файлы, а не в пустой каталог, случайно созданный заново.n8n.cloud - управляемый тариф с лимитами на активные workflow, число запусков и участников, и он не может подключиться к сервису в вашей частной сети, только к тому, что доступно из интернета. На своём VPS оба ограничения снимаются: workflow ровно столько, сколько выдержит сервер, а к внутренней базе или API можно ходить напрямую. Взамен обновление образа, бэкап и мониторинг диска и памяти становятся вашей задачей.
По документации n8n простаивающий инстанс занимает около 100 МБ, а типичный диапазон для несложных сценариев - 320 МБ - 2 ГБ. Для личных и небольших командных автоматизаций достаточно тарифа на 1-2 ГБ. Тяжёлая обработка данных внутри нод, много параллельных запусков или вынесенная PostgreSQL требуют памяти заметно больше.
N8N_PROTOCOL, N8N_HOST и N8N_PORT собирают адрес, по которому n8n видит сам себя, но за обратным прокси он не совпадает с реальным публичным URL. Внешний адрес для вебхуков задаётся отдельно через N8N_WEBHOOK_URL (актуальное имя переменной, WEBHOOK_URL устарела с версии 2.35.0) - туда пишется полный публичный адрес с https. Рядом обязательно ставится N8N_PROXY_HOPS=1, иначе n8n неправильно определяет протокол запроса по заголовкам от прокси.
Переменные N8N_BASIC_AUTH_ACTIVE, N8N_BASIC_AUTH_USER и N8N_BASIC_AUTH_PASSWORD существовали в старых версиях, но начиная с n8n 1.0 базовая авторизация убрана из продукта, и эти переменные ничего не делают. При первом заходе в интерфейс n8n сам просит создать учётную запись владельца с почтой и паролем, а дальше в неё добавляются пользователи с ролями через встроенное управление пользователями.
Всё - workflow, зашифрованные учётные данные подключений, история запусков и ключ шифрования - лежит в SQLite-файле внутри каталога /home/node/.n8n, который обязательно выносится томом на хост. Полноценный бэкап - этот каталог целиком (или внешняя база, если вы используете PostgreSQL), потому что CLI-экспорт workflow и credentials не включает пользователей, историю запусков и сам ключ шифрования, без которого восстановленные учётные данные подключений нечитаемы.
Нет, для старта не нужен. По умолчанию n8n использует встроенную SQLite прямо в своём томе с данными, и для одного пользователя или небольшой команды этого достаточно. Отдельная PostgreSQL нужна, когда параллельно работают несколько воркеров в режиме очереди или число одновременных запусков становится большим и SQLite упирается в запись.
/home/node/.n8n, вынесенный томом на хост. Не вынесли - потеряли всё при первом же docker compose down.n8n - ещё один сервис в Docker на том же VPS, и вокруг него работают те же правила периметра и эксплуатации, что и для остальной self-hosted инфраструктуры. По порядку: