Короткий ответ: прод-n8n на Ubuntu 24.04 — это Docker Compose и Postgres 18, образ с пином тега docker.n8n.io/n8nio/n8n:2.38.7, не latest. n8n слушает только 127.0.0.1:5678, снаружи — HTTPS через nginx. Канон n8n по TLS — Traefik или Caddy; ниже ставим nginx и Certbot, потому что так уже устроены большинство VPS. SQLite на круглосуточный сервер не берите. Установка через npm умрёт в n8n 3.0 в октябре 2026.
Нужны: настроенный Ubuntu (гайд), Docker CE и docker compose (установка Docker), домен, который смотрит A-записью на VPS. Сертификат — по nginx + Let’s Encrypt. Факты: 13–14 сентября 2026, релиз n8n 2.38.7 от 11 сентября 2026. Команды копируйте, домен везде замените на свой.
Почему именно Compose и Postgres
Официальный путь для продакшена — Docker Compose. npm/npx ещё работают в 2.x, но n8n 3.0 в октябре 2026 потребует Docker: npm install -g n8n и npx n8n снимут. Ставить «на Node с сервера» сейчас — гарантированная миграция через месяц. Node.js в этой серии нужен для других задач (гайд по Node), не как способ гонять n8n.
База по умолчанию — SQLite в томе /home/node/.n8n. Для «пощупать редактор» хватает. Для инстанса, который крутится 24/7 с вебхуками и несколькими пользователями, n8n пишет прямо: берите Postgres. В документации Compose — образ postgres:18. Не монтируйте в n8n хостовый Postgres «потому что так привычнее», если не умеете сами: канон этого гайда — Postgres в том же compose. Отдельная установка Postgres на хост — другой материал серии.
Тег образа пините. :latest на проде — это чужой релиз в ночь с пятницы на субботу. На дату гайда актуальный патч — 2.38.7 (релиз на GitHub). Реестр — docker.n8n.io, не случайный форк на Docker Hub.
| Параметр | Значение |
|---|---|
| Образ n8n | docker.n8n.io/n8nio/n8n:2.38.7 |
| База | Postgres 18 в Compose, не SQLite |
| Порт на хосте | 127.0.0.1:5678, не 0.0.0.0 |
| Публичный URL | HTTPS, переменные N8N_HOST, N8N_PROTOCOL, N8N_WEBHOOK_URL |
| Прокси | N8N_PROXY_HOPS=1, в nginx — Upgrade для WebSocket |
| npm | Не использовать: deprecate к 3.0, октябрь 2026 |
Про RAM. Обычный n8n+Postgres в этом файле не требует цифры «минимум N гигабайт» сверх того, что влезет в ваш VPS. Исключение из документации n8n: стек sandbox для n8n Assistant (Docker-in-Docker) — не меньше 4 GB RAM и 2 vCPU. Этот гайд Assistant и sandbox не поднимает. Если включите их по официальному Compose с sandbox — ориентируйтесь на эти 4 GB.
TLS: Traefik, Caddy или nginx
В документации n8n канон такой: TLS лучше не пихать в сам n8n, а повесить обратный прокси. Рекомендуемые примеры — Traefik или NLB; в гайдах n8n по облакам часто берут Caddy (сам выпускает сертификаты). Это правильный путь, если вы собираете стек с нуля вокруг Compose.
На типичном VPS уже стоит или планируется nginx: сайты, WordPress, вебхуки. Поэтому ниже — nginx + Certbot snap, не Traefik. Честно: это не «официальный любимый прокси n8n», это практичный вариант, когда один nginx уже слушает 80/443. Цена — руками прописать WebSocket Upgrade. Без Upgrade редактор «отваливается», а Telegram Trigger в режиме прослушивания тестовых событий залипает. Про вебхуки Telegram отдельно: гайд.
Файлы Compose
Каталог и секреты. Пароль Postgres не оставляйте n8n. Домен — тот, для которого уже есть DNS.
mkdir -p ~/apps/n8n
cd ~/apps/n8n
umask 077
openssl rand -base64 24
# вставьте вывод в POSTGRES_PASSWORD ниже
cat > .env <<'EOF'
POSTGRES_USER=n8n
POSTGRES_PASSWORD=замените_на_случайную_строку
POSTGRES_DB=n8n
N8N_HOST=n8n.example.com
N8N_WEBHOOK_URL=https://n8n.example.com/
GENERIC_TIMEZONE=Europe/Moscow
EOF
chmod 600 .env
В N8N_HOST — только имя хоста, без https://. В N8N_WEBHOOK_URL — полный URL со схемой и со слэшем в конце. Переменная WEBHOOK_URL с 2.35.0 deprecated: ещё работает, но n8n пишет предупреждение. Нужна N8N_WEBHOOK_URL. Справка: webhook URL за прокси.
Минимальный compose.yaml (имя файла — канон Compose v2):
cat > compose.yaml <<'EOF'
services:
postgres:
image: postgres:18
restart: always
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
PGDATA: /var/lib/postgresql/data
volumes:
- db-storage:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
n8n:
image: docker.n8n.io/n8nio/n8n:2.38.7
restart: always
ports:
- "127.0.0.1:5678:5678"
environment:
N8N_HOST: ${N8N_HOST}
N8N_PORT: "5678"
N8N_PROTOCOL: https
N8N_WEBHOOK_URL: ${N8N_WEBHOOK_URL}
N8N_PROXY_HOPS: "1"
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: "5432"
DB_POSTGRESDB_DATABASE: ${POSTGRES_DB}
DB_POSTGRESDB_USER: ${POSTGRES_USER}
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
TZ: ${GENERIC_TIMEZONE}
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
volumes:
db-storage:
n8n_data:
EOF
Зачем PGDATA: /var/lib/postgresql/data: Postgres 18 сменил каталог данных внутри образа. Без этой переменной том может оказаться не там, и база поднимется пустой. Не удаляйте строку. Если когда-нибудь будете мигрировать со старого мажора Postgres, простой сменой тега на 18 данные не откроются — нужен dump/restore, см. апгрейд PostgreSQL.
Порт 127.0.0.1:5678:5678 — n8n с интернета не виден. Это важно ещё и потому, что публикация портов Docker обходит ufw (см. гайд по Docker). Проверка с хоста:
cd ~/apps/n8n
docker compose pull
docker compose up -d
docker compose ps
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5678/healthz
ss -tulpn | grep 5678
Ожидаемо: оба сервиса running, postgres healthy, healthz отвечает 200, сокет 127.0.0.1:5678, не 0.0.0.0:5678. Если healthz не 200 — docker compose logs n8n postgres --tail=80. Частая ошибка: опечатка в пароле или n8n стартанул раньше healthcheck (в файле выше этого не должно быть: есть depends_on: condition: service_healthy).
SQLite в этот момент не используется: DB_TYPE=postgresdb. Том n8n_data всё равно нужен — там ключ шифрования учёток и файлы инстанса. Не удаляйте его «для чистоты».
nginx: HTTPS и WebSocket
Сначала HTTP-сайт на домене и сертификат — по гайду Certbot + nginx. Кратко: пакет nginx из apt, Certbot только snap, порт 80 открыт, sudo certbot --nginx. Когда https://n8n.example.com уже открывается (хотя бы заглушка), добавьте прокси на loopback.
Каркас server-блока после Certbot. Certbot сам пропишет ssl_certificate на fullchain.pem и ssl_certificate_key на privkey.pem. Вам нужно location / с Upgrade. Без этих двух заголовков UI n8n и Telegram Trigger «слушают и молчат».
sudo tee /etc/nginx/sites-available/n8n.example.com <<'EOF'
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
server_name n8n.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
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;
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-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
}
EOF
sudo nginx -t
sudo systemctl reload nginx
Если Certbot уже создал файл в sites-enabled, не плодите второй server_name на тот же домен — влейте proxy_pass и заголовки в существующий ssl-блок. map живёт на уровне http: либо в этом файле (Ubuntu включает sites-enabled/* внутри http), либо в /etc/nginx/conf.d/. Два одинаковых map $http_upgrade nginx не проглотит.
Проверка снаружи:
curl -sS -o /dev/null -w '%{http_code}\n' https://n8n.example.com/healthz
curl -sS -I https://n8n.example.com/ | head
healthz с HTTPS — 200. В браузере откройте https://n8n.example.com/, создайте владельца (первый пользователь становится owner). В консоли браузера не должно быть обрывов wss:// на /rest/push.
Про вебхук: в любом workflow с Webhook n8n должен показывать URL вида https://n8n.example.com/webhook/..., не http://localhost:5678. Если видите localhost — не подхватились N8N_WEBHOOK_URL / N8N_PROTOCOL, перезапуск: cd ~/apps/n8n && docker compose up -d.
Обновление патча n8n — смена тега в compose.yaml (например на следующий 2.38.x), затем docker compose pull && docker compose up -d. Не переключайтесь на latest.
FAQ
Почему не npm i -g n8n?
В 2.x ещё ставится, в n8n 3.0 (октябрь 2026) npm/npx убирают. Плюс на npm нет n8n Assistant. Ставьте Compose сразу, не собирайте миграцию на октябрь.
Можно ли оставить SQLite?
Только чтобы один раз открыть редактор. Для 24/7, вебхуков и нескольких пользователей n8n рекомендует Postgres. В этом гайде — Postgres 18 в том же compose.yaml. SQLite в проде не используем.
Зачем N8N_WEBHOOK_URL, если есть N8N_HOST?
За прокси n8n сам склеивает URL из протокола, хоста и внутреннего порта 5678 — получится неверный адрес. N8N_WEBHOOK_URL задаёт публичную базу для тестовых и боевых вебхуков. WEBHOOK_URL — старое имя, deprecated с 2.35.0. N8N_PROXY_HOPS=1 обязателен, чтобы доверять X-Forwarded-* от одного nginx.
Telegram Trigger зависает на «Listen for test events»
Почти всегда нет WebSocket Upgrade в nginx: нужны proxy_http_version 1.1, Upgrade и Connection. Проверьте wss в консоли браузера. Порт n8n должен быть 127.0.0.1:5678, N8N_WEBHOOK_URL — https://ваш-домен/.
Почему не latest и не :2.38?
latest плавает. Плавающий минор тоже может приехать в неудобный момент. На 13–14 сентября 2026 канон серии — пин 2.38.7. Патчи поднимайте сознательно, не автоподпиской тега.