Як розгорнути n8n на VPS через Docker і HTTPS
Налаштуйте n8n на VPS із Docker Compose, Postgres і reverse proxy. Розберіть пастки WEBHOOK_URL та encryption key і виправте типові помилки.
Що ви створюєте
n8n — це інструмент автоматизації робочих процесів: візуальний редактор, у якому тригер, webhook, розклад або надсилання форми запускає ланцюжок вузлів, що викликають API, змінюють дані та записують їх в інші системи. Він став стандартним засобом інтеграції для робочих процесів з AI-агентами, оскільки взаємодіє з усіма основними постачальниками моделей і базами даних без написання окремого сервісу. Один docker run отримає робочий редактор за дві хвилини. Цей посібник присвячений решті 90%: забезпеченню надійної роботи з Postgres замість стандартного файла SQLite, доступу через HTTPS і, що найчастіше налаштовують неправильно, передаванню webhook URL, до якого зовнішній світ справді може підключитися.
Готовий стек складається з двох контейнерів в одній Docker network: самого n8n і бази даних Postgres, у якій зберігаються його робочі процеси та облікові дані. Reverse proxy на хості завершує TLS і передає запити до n8n через localhost, тому жоден сервіс не доступний з інтернету безпосередньо — лише через цей проксі. Він працює поруч з іншими сервісами з короткого списку self-hosting на 2026 рік.
Передумови та реальні обмеження
Вам потрібен VPS щонайменше з 1 GB RAM. Коли workflows почнуть виконувати реальну роботу, плануйте 2 GB, оскільки executions разом із середовищем виконання Node.js споживають пам’ять. OOM killer, який зупиняє контейнер посеред виконання, — найгірший спосіб виявити це. На початку достатньо одного vCPU.
Якщо на цьому сервері працюватиме ще щось ресурсоємніше, спочатку підберіть конфігурацію під той сервіс. Зазвичай найбільше ресурсів потребує фотобібліотека, а реальні мінімальні вимоги до RAM для PhotoPrism та Immich набагато вищі за вимоги n8n. Те саме стосується медіасервера: Jellyfin разом із frontend для перегляду бібліотеки, наприклад Halcyon, який відтворює формат відеопрокату 90-х, використає доступну RAM і запас ресурсів для transcoding задовго до того, як n8n це помітить.
Вам потрібен домен або піддомен, наприклад n8n.example.com, із A record, що вказує на публічну IP-адресу VPS і доступний до запиту сертифіката. Порти 80 і 443 мають бути відкриті для proxy. Власний порт n8n 5678 не має бути доступним з інтернету. Потрібні Docker Engine і Compose plugin. Якщо docker compose version завершується помилкою docker: 'compose' is not a docker command, у вас стара standalone binary, а plugin має назву sudo apt install docker-compose-plugin.
SQLite підходить для тестування, а для всього важливого використовуйте Postgres
Стандартна база даних n8n — це файл SQLite у /home/node/.n8n/database.sqlite. Для швидкої перевірки цього достатньо: не монтуйте volume, і після першого пересоздання контейнера дані буде втрачено. Це також окремий корисний урок. Причина переходу на Postgres не в максимальній швидкості. SQLite підтримує лише один блокувальний запис, тому екземпляр, який одночасно виконує кілька workflow, або queue mode, який вам зрештою знадобиться, за паралельного виконання видає SQLITE_BUSY: database is locked. У Postgres такого обмеження немає. Його легко резервно копіювати за допомогою pg_dump. Саме Postgres документація n8n рекомендує для сервера, від якого ви залежите. Подальший перехід потребує ручної міграції даних. Тому, якщо цей сервер важливий, одразу використовуйте Postgres.
DNS і firewall
Спочатку налаштуйте DNS-запис і відкрийте порти. Тоді подальший етап отримання сертифіката не завершиться помилкою через ім’я, яке не резолвиться.
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 прив’язує n8n до 127.0.0.1:5678, тому доступ до нього має лише reverse proxy хоста. Правило 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. Причини наведено в розділі про оновлення нижче.
Файл секретів
Ніколи не зберігайте паролі у файлі compose. Розмістіть їх у файлі .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 .envN8N_ENCRYPTION_KEY — найважливіший рядок у цьому файлі. Це ключ, яким шифруються всі збережені облікові дані. Установіть його явно, а не дозволяйте n8n згенерувати ключ, оскільки згенероване вами значення можна записати та відновити. Після того як n8n зашифрує цим ключем перші облікові дані, його зміна зробить усі облікові дані неможливими для розшифрування. Тому встановіть цей ключ один раз зараз і більше ніколи не змінюйте цей рядок.
Змінні середовища, які визначають роботу webhook
Чотири змінні визначають, як n8n представляє себе зовнішньому світу. Неправильне значення цих змінних — найчастіше питання до служби підтримки n8n.
N8N_HOST— це публічне ім’я хоста,n8n.example.com. Якщо за проксі залишити значення за замовчуваннямlocalhost, редактор намагається завантажити власний API зlocalhostу вашому браузері. Це завершується помилкою.N8N_PROTOCOL=httpsповідомляє n8n, що сервіс працює через TLS. Тому n8n позначає cookie сеансу якSecureі формує URLhttps://.N8N_PORT=5678— це порт, на якому n8n прослуховує з’єднання усередині контейнера. Це не публічний порт: проксі використовує 443.WEBHOOK_URL=https://n8n.example.com/— найважливіша змінна. n8n формує адреси webhook, які ви додаєте в 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, що перед ним працює один проксі. Завдяки цьому обмеження частоти запитів і всі функції, які читають IP-адресу клієнта, бачать реальну адресу, а не адресу проксі. Одну змінну тут навмисно не потрібно задавати — N8N_RUNNERS_ENABLED. Task runners — це механізм, у якому n8n виконує логіку Code node в окремому процесі, ізольованому від основного. Протягом усієї гілки 1.x цей механізм був вимкнений за замовчуванням: змінна мала значення false, а для ввімкнення потрібно було задати true. У n8n 2.0 task runners за замовчуванням увімкнені, тому в гілці 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, перегляньте журнали. Майже завжди причина полягає в підключенні до бази даних або правах доступу до тому, які описано нижче.
TLS із reverse proxy
n8n сам працює через звичайний HTTP на порту 5678; HTTPS завершується на проксі перед ним. Є два прості варіанти.
Якщо ви вже запускаєте кілька контейнерів, розмістіть n8n за reverse proxy Traefik, який автоматично випускає TLS-сертифікати. Для цього достатньо кількох labels: Traefik сам запитає та поновить сертифікат.
Якщо це єдиний застосунок на сервері, простіше налаштувати virtual host nginx із сертифікатом Let’s Encrypt. Скористайтеся налаштуванням TLS для Certbot і nginx в 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. Без цих двох рядків сторінка входу завантажиться, а потім зависне з повідомленням про втрату з’єднання. proxy_read_timeout 3600 не дає nginx переривати тривалі виконання через стандартні 60 секунд. Заголовок X-Forwarded-Proto $scheme доповнює N8N_PROXY_HOPS=1: він повідомляє n8n, що початковий запит надходив через HTTPS, хоча проксі підключається до нього через звичайний HTTP. Завдяки цьому n8n не вважає з’єднання небезпечним і не відхиляє власний cookie.
Ваш перший workflow: як перевірити його роботу
Відкрийте https://n8n.example.com/, створіть обліковий запис власника (у наступному розділі) і побудуйте найменший workflow, який підтверджує роботу всього ланцюжка: вхідний webhook, HTTP-виклик і вихідна відповідь.
- Додайте вузол Webhook. Встановіть метод
POSTі шлях на кшталтhello. Він показує 2 URL: Test URL і Production URL. Через це виникає значна частина повідомлень «мій webhook не працює». Test URL обробляє 1 виклик і лише після натискання Listen for test event; після цього він стає недійсним. Production URL реєструється після натискання Publish для workflow і надалі обробляє запити. - Додайте після нього вузол HTTP Request і вкажіть будь-який загальнодоступний JSON API. GET-запит до
https://api.github.com/zenповертає однорядковий рядок, чого достатньо. - Додайте вузол Respond to Webhook і встановіть для параметра Respond у вузлі Webhook значення «Using Respond to Webhook node», щоб викликач отримав результат роботи HTTP-вузла.
- Натисніть Publish у правому верхньому куті та виконайте виклик:
curl -X POST https://n8n.example.com/webhook/hello. Ви маєте отримати назад рядок zen: POST-запит, виклик API і відповідь. Це структура більшості реальних автоматизацій. У старіших інструкціях радять перемкнути тут параметр Active. У n8n 2.0 цей параметр замінено кнопками Publish і Unpublish.
У запланованому варіанті вузол Webhook замінюють на Schedule Trigger, а натомість викликають endpoint моделі. Self-hosted варіант із Ollama, запущеним на тому самому VPS, зручно використовувати для створення нічного підсумовувача.
Керування користувачами, а не basic auth
У старих інструкціях для n8n радять установити N8N_BASIC_AUTH_ACTIVE=true. Ці змінні видалили в n8n 1.0, і тепер вони нічого не роблять. Сьогодні автентифікація здійснюється через обліковий запис власника: під час першого відкриття редактора n8n пропонує створити обліковий запис власника з email і паролем. Цей захист є обов’язковим, а анонімного режиму немає. Створіть обліковий запис одразу після першого запуску, перш ніж передавати комусь URL: у проміжку між docker compose up і надсиланням першої форми екземпляр може привласнити той, хто першим отримає до нього доступ. Додатковий рівень basic auth на reverse proxy є доцільним, але це другий рівень захисту, а не основна автентифікація. Обліковий запис власника та всі інші описані в цій інструкції функції працюють у безкоштовній community edition; якщо згодом вам знадобляться додаткові користувачі з детальними ролями або SSO, варто прочитати, які функції n8n потребують платної ліцензії, перш ніж планувати їх використання.
Резервні копії: спочатку ключ шифрування, потім база даних
Потрібно резервувати два компоненти, але відновити їх рівнозначно неможливо.
N8N_ENCRYPTION_KEY. Усі облікові дані, які ви зберігаєте в n8n, зокрема API-токени, паролі до бази даних і секрети OAuth, шифруються в стані спокою за допомогою цього ключа. Робочі процеси в 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-файлі навмисно зафіксовано n8nio/n8n:2.29.10, а не latest. n8n випускає новий minor-реліз у більшість тижнів і час від часу змінює схему бази даних або поведінку node між такими релізами, тому latest означає, що автоматичне завантаження може передати вам збірку, яка мігрує базу даних одразу після запуску. Зафіксуйте версію, прочитайте нотатки до релізу перед оновленням; n8n зазначає там несумісні зміни. Оновлюйте навмисно:
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Переходи між major-версіями мають найбільше значення. Наприклад, у лінійці 2.0 за замовчуванням було змінено N8N_BLOCK_ENV_ACCESS_IN_NODE на true, тому будь-який Code node, який читав process.env, непомітно втратить доступ, доки ви не повернете значення false. У тому самому релізі також почалася перевірка суворих прав доступу до settings-файлу, task runners було ввімкнено за замовчуванням, а перемикач Activate для workflow замінено на Publish. Прочитайте сторінку про несумісні зміни у версії 2.0, перш ніж переходити через межу major-версії. n8n автоматично запускає потрібні міграції бази даних під час запуску. Саме тому попередній pg_dump не є необов’язковим. Облікові дані зберігаються в зашифрованому вигляді з ключем у .env, а дані — у Postgres, тому контейнери можна замінювати: для оновлення замініть їх, а для відкату зафіксуйте попередній тег і відновіть дамп.
Режими відмови та повідомлення, які ви побачите
The requested webhook "POST hello" is not registered. Відповідь 404 з’являється під час виклику webhook, workflow якого не опубліковано, або під час виклику тестового шляху, якщо ніхто не очікує запитів. Тестові шляхи (/webhook-test/...) відповідають лише після натискання "Listen for test event"; production-шляхи (/webhook/...) відповідають лише після публікації workflow. Сусідній This webhook is not registered for GET requests. Did you mean to make a POST request? означає, що використано неправильний метод: node очікує POST, а ви надіслали GET.
URL webhook містить :5678 або localhost. Node показує https://n8n.example.com:5678/webhook/... або http://localhost:5678/.... Параметр WEBHOOK_URL не задано або вказано неправильно, тому n8n сформував адресу на основі N8N_HOST:N8N_PORT, а не вашої публічної базової адреси. Задайте WEBHOOK_URL=https://n8n.example.com/, повторно створіть контейнер за допомогою docker compose up -d, і порт зникне.
There was a problem loading init data у браузері. Редактор завантажився, але не може підключитися до власного backend API. За reverse proxy це майже завжди означає неправильне значення N8N_HOST або WEBHOOK_URL, відсутні в проксі заголовки WebSocket Upgrade або невідповідність N8N_PROTOCOL способу підключення. Перевірте чотири публічні змінні та переконайтеся, що проксі передає Upgrade і Connection.
password authentication failed for user "n8n" у журналах, після чого контейнер перезапускається. Пароль, який n8n надсилає базі даних, не відповідає паролю, з яким базу даних було ініціалізовано. Важливий нюанс: Postgres читає POSTGRES_PASSWORD лише під час ініціалізації порожнього каталогу даних. Запустіть stack один раз, потім змініть POSTGRES_PASSWORD у .env, але наявний том postgres_data все одно міститиме старий пароль. Поверніть початкове значення або, якщо зберігати дані не потрібно, виконайте docker compose down і docker volume rm для тому postgres, а потім запустіть stack заново.
EACCES: permission denied, open '/home/node/.n8n/config' під час запуску. n8n працює від імені користувача node (UID 1000) і не може записати дані до свого каталогу конфігурації. Це трапляється, коли каталог на хості змонтовано через bind mount (./n8n_data:/home/node/.n8n) і він належить root. Використайте іменований том, показаний вище, або, якщо потрібен bind mount, спочатку виконайте sudo chown -R 1000:1000 ./n8n_data.
Permissions 0644 for n8n settings file /home/node/.n8n/config are too wide. Changing permissions to 0600.. Після переходу на гілку 2.x n8n за замовчуванням забезпечує 0600 для цього файла та виправляє його під час запуску. Це повідомлення в журналі означає, що режим доступу вже виправлено. Зазвичай причина — bind mount або відновлення, під час якого файл було скопійовано назад із надто широкими правами доступу. Додаткових дій не потрібно; задавайте N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false лише тоді, коли файлова система справді не підтримує права доступу.
Mismatching encryption keys, повний рядок повідомлення вказує, що ключ шифрування у файлі налаштувань /home/node/.n8n/config не відповідає значенню N8N_ENCRYPTION_KEY у вашому середовищі. Ключ у середовищі відрізняється від ключа, який n8n записав до свого тому даних під час попереднього запуску. Найчастіше це трапляється, коли під час попереднього запуску змінну не було задано, n8n згенерував випадковий ключ, а потім ви задали інший. Поверніть початковий ключ у .env або, лише якщо збережені облікові дані вам справді не потрібні, видаліть файл config у томі n8n_data і дозвольте n8n створити його повторно. Після цього наявні облікові дані стануть непридатними для читання.
Повідомлення під час входу про захищені cookie: Your n8n server is configured to use a secure cookie, however you are either visiting this via an insecure URL, or using Safari. Ви задали N8N_PROTOCOL=https, але підключилися до n8n через звичайний HTTP, зазвичай відкривши IP-адресу та порт безпосередньо замість HTTPS-проксі. Відкрийте сервіс через https://n8n.example.com/. Лише якщо використання HTTPS справді неможливе, задайте N8N_SECURE_COOKIE=false. Не робіть цього на сервері, доступному з Інтернету.
Щоб додати мовну модель до цих workflow, див. створення AI workflow із Claude та 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/ і переконайтеся, що node показує URL без порту. Друга причина — виклик production URL workflow, який ви не опублікували. У такому разі повертається The requested webhook ... is not registered.
Що потрібно резервувати в n8n?
Дві речі. По-перше, N8N_ENCRYPTION_KEY із вашого файлу .env, оскільки кожен збережений credential зашифровано за допомогою цього ключа. У разі його втрати розшифрувати credentials буде неможливо. Скопіюйте ключ на інший сервер у день його створення. По-друге, pg_dump бази даних Postgres, що містить workflow, історію та credentials. Для відновлення потрібні обидва компоненти: той самий ключ і dump.
Як розмістити n8n за HTTPS?
n8n обслуговує звичайний HTTP на порту 5678, а reverse proxy перед ним завершує TLS. Прив’яжіть n8n до 127.0.0.1:5678, щоб доступ до нього мав лише proxy, а потім використовуйте Traefik з автоматичним отриманням сертифікатів або nginx із сертифікатом Let's Encrypt. Задайте N8N_PROTOCOL=https і WEBHOOK_URL=https://your-host/ та переконайтеся, що proxy передає заголовки Upgrade WebSocket. Інакше editor зависає.
Як безпечно оновити n8n?
Зафіксуйте конкретний тег image замість latest. Спочатку створіть pg_dump, оскільки під час запуску n8n автоматично виконує міграції. Прочитайте release notes на наявність несумісних змін, потім змініть тег і виконайте docker compose pull n8n && docker compose up -d n8n. Container можна відтворити, тому для відкату зафіксуйте попередній тег і відновіть dump, створений перед оновленням.