Aby postawić n8n self-hosted, tworzysz plik docker-compose.yml z obrazem n8nio/n8n, portem 5678 i wolumenem na dane, a potem uruchamiasz docker compose up -d. Na produkcji dokładasz reverse proxy z HTTPS i własną domenę. Całość zajmuje kilkanaście minut na VPS od 2 GB RAM.
Dlaczego tak: Compose zamiast docker run daje powtarzalność, bo cała konfiguracja siedzi w jednym pliku, łatwy update jedną komendą docker compose pull oraz trwałe dane w wolumenie. To różnica między instalacją, którą odtworzysz na nowym serwerze w minutę, a klikaniem, którego nikt po Tobie nie powtórzy. Poniżej rozkładam to na trzy kompletne pliki: minimalny start, wersję z PostgreSQL i wariant produkcyjny z HTTPS. Każdy skopiujesz 1:1.
Czego potrzebujesz przed instalacją
W skrócie: do uruchomienia n8n potrzebujesz serwera z Linuksem, Dockera i - jeśli celujesz w produkcję - własnej domeny. Nic egzotycznego, ale brak któregokolwiek elementu zatrzyma Cię w pół drogi.
- VPS lub serwer z Linuksem (Ubuntu, Debian) - minimum 1 vCPU i 2 GB RAM, komfortowo 2 vCPU i 4 GB. n8n potrafi być pamięciożerny przy wielu równoległych przepływach, więc 2 GB to absolutne dno na start.
- Docker i wtyczka Docker Compose - sprawdź obecność poleceniami
docker --versionorazdocker compose version. Jeśli ich nie ma, zainstaluj Dockera zgodnie z instrukcją dla swojej dystrybucji. - Domena lub subdomena z rekordem A wskazującym na IP serwera - potrzebna do HTTPS i do tego, by webhooki miały stały, publiczny adres.
Jeśli pierwszy raz dotykasz tego stosu, zacznij od podstaw Docker Compose udźwignie realne obciążenie. To zależy od ruchu: dla kilku prostych przepływów wystarczy najtańszy VPS, dla dziesiątek workflow z bazą PostgreSQL celuj wyżej.
Najprostszy docker-compose.yml dla n8n
W skrócie: minimalny plik to obraz, port i wolumen - tyle wystarczy, by n8n ruszyło lokalnie. Poniższy docker-compose.yml skopiujesz 1:1 i odpalisz od razu.
services:
n8n:
image: n8nio/n8n # oficjalny obraz n8n z Docker Hub
restart: unless-stopped # kontener wstaje sam po reboocie serwera
ports:
- "5678:5678" # port n8n (host:kontener), domyślnie 5678
environment:
- GENERIC_TIMEZONE=Europe/Warsaw # strefa czasowa dla harmonogramów (cron w workflow)
- TZ=Europe/Warsaw # strefa czasowa systemu w kontenerze
- N8N_SECURE_COOKIE=false # pozwala zalogować się po HTTP na localhost
volumes:
- n8n_data:/home/node/.n8n # trwałe dane: workflow, credentiale, baza SQLite
volumes:
n8n_data: # named volume - przeżywa "docker compose down"
Zapisz plik jako docker-compose.yml i uruchom usługę:
docker compose up -d # start w tle (detached)
docker compose ps # sprawdź status kontenera
docker compose logs -f # podgląd logów na żywo
Wejdź na http://IP_SERWERA:5678, załóż konto właściciela i masz działającą instancję. Pułapka: N8N_SECURE_COOKIE=false jest OK wyłącznie do lokalnego testu po HTTP. Na produkcji za reverse proxy włączasz HTTPS i ten wpis usuwasz - inaczej obniżasz bezpieczeństwo logowania.
Pro tip: zmienne trzymaj w osobnym pliku .env zamiast wklejać je wprost do compose. Compose sam podczytuje .env z katalogu, w którym leży docker-compose.yml, a Ty nie wrzucasz sekretów do repozytorium.
Kluczowe zmienne środowiskowe n8n
Konkret: kilka zmiennych decyduje o tym, czy n8n zadziała poprawnie na produkcji. Najważniejsza z nich to klucz szyfrowania - o nim za chwilę osobno, bo to najczęstsze źródło utraty dostępu.
| Zmienna | Co ustawia |
|---|---|
N8N_HOST |
domena, pod którą działa n8n (np. n8n.twojadomena.pl) |
N8N_PORT |
port nasłuchu w kontenerze (domyślnie 5678) |
N8N_PROTOCOL |
http lub https - na produkcji https |
WEBHOOK_URL |
pełny publiczny adres dla webhooków (musi być HTTPS) |
GENERIC_TIMEZONE / TZ |
strefa czasowa dla harmonogramów i logów |
N8N_ENCRYPTION_KEY |
klucz szyfrujący zapisane credentiale - ustaw raz, nie zmieniaj |
N8N_SECURE_COOKIE |
wymusza ciasteczko tylko przez HTTPS (na produkcji true) |
Na produkcji: ustaw N8N_ENCRYPTION_KEY ręcznie na długi, losowy ciąg i zapisz go w bezpiecznym miejscu. Jeśli go nie podasz, n8n wygeneruje klucz sam i schowa w wolumenie - dopóki wolumen istnieje, wszystko działa. Ale gdy odtworzysz instancję bez tego samego klucza, stracisz dostęp do wszystkich zapisanych credentiali (hasła, tokeny, klucze API). To nie do odzyskania - musisz wpisać je od nowa. Ustaw ten klucz od pierwszego dnia, a nie po pierwszej awarii.
Trwałe dane - wolumen i backup
W skrócie: bez wolumenu tracisz wszystkie workflow przy pierwszym docker compose down. n8n trzyma dane w katalogu /home/node/.n8n w kontenerze - to tam ląduje baza SQLite i zaszyfrowane credentiale.
Masz dwie opcje montowania. Named volume, jak w przykładach wyżej z wpisem n8n_data:, zarządza Docker i jest wygodny. Bind mount w formie ./n8n-data:/home/node/.n8n wskazuje konkretny katalog na hoście - łatwiej go zbackupować i podejrzeć, ale musisz pilnować uprawnień do plików. Dla większości wdrożeń zaczynaj od named volume - mniej niespodzianek z prawami dostępu.
Backup sprowadza się do skopiowania katalogu danych. Przy named volume najprościej spakować jego zawartość:
docker run --rm -v n8n_data:/data -v $(pwd):/backup alpine \
tar czf /backup/n8n-backup.tar.gz -C /data .
Na produkcji: backup wolumenu połącz ze snapshotem całego VPS, jeśli hosting go oferuje. Pro tip: testuj odtworzenie kopii, a nie tylko jej tworzenie - backup, którego nigdy nie przywróciłeś, to tylko nadzieja, nie kopia zapasowa. Ustaw też prosty harmonogram - cron z tą komendą raz na dobę - i wysyłaj archiwum poza serwer.
n8n z bazą PostgreSQL
W skrócie: SQLite jest OK na start, ale pod większym obciążeniem przesiadasz się na PostgreSQL. n8n wspiera Postgresa natywnie - dorzucasz usługę bazy do compose i przełączasz DB_TYPE.
services:
postgres:
image: postgres:16
restart: unless-stopped
environment:
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=zmien_to_haslo # ustaw mocne hasło
- POSTGRES_DB=n8n
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck: # n8n czeka, aż baza będzie gotowa
test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
interval: 10s
timeout: 5s
retries: 5
n8n:
image: n8nio/n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- DB_TYPE=postgresdb # przełączenie z SQLite na Postgres
- DB_POSTGRESDB_HOST=postgres # nazwa usługi z tego pliku
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=zmien_to_haslo
- N8N_ENCRYPTION_KEY=wstaw_dlugi_losowy_klucz
- GENERIC_TIMEZONE=Europe/Warsaw
- TZ=Europe/Warsaw
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy # start n8n dopiero po zdrowej bazie
volumes:
n8n_data:
postgres_data:
Kiedy ma to sens: gdy masz wiele workflow, dużo wykonań i zależy Ci na stabilności pod obciążeniem. PostgreSQL znosi równoległe zapisy lepiej niż plikowy SQLite. Przy bardzo intensywnym użyciu n8n oferuje też tryb kolejkowy (queue mode) z osobnymi procesami roboczymi - to temat na osobny krok, gdy pojedyncza instancja przestanie wyrabiać. Konkret: składnię zmiennych DB_POSTGRESDB_* warto potwierdzić w aktualnej dokumentacji n8n, bo lista opcji bywa rozszerzana o nowe parametry połączenia.
HTTPS i własna domena
W skrócie: na produkcji HTTPS jest obowiązkowy - bez niego nie zadziałają webhooki, OAuth integracji ani bezpieczne logowanie. n8n samo nie obsługuje TLS, więc terminację SSL robi reverse proxy postawiony przed kontenerem.
Masz trzy sprawdzone ścieżki: Caddy (najprościej, automatyczny certyfikat z Let's Encrypt), Nginx z Certbotem (klasyka, więcej ręcznej konfiguracji) oraz Traefik (wygodny przy wielu usługach, sterowany etykietami). Dla pojedynczej instancji n8n zacznij od Caddy - najmniej ruchomych części. Poniżej najkrótsza droga:
services:
caddy:
image: caddy:latest
restart: unless-stopped
ports:
- "80:80" # przekierowanie HTTP i wyzwanie ACME
- "443:443" # ruch HTTPS
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile # konfiguracja proxy
- caddy_data:/data # tu Caddy trzyma wystawione certyfikaty
- caddy_config:/config
n8n:
image: n8nio/n8n
restart: unless-stopped
expose:
- "5678" # widoczny tylko w sieci Compose, nie na zewnątrz
environment:
- N8N_HOST=n8n.twojadomena.pl
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.twojadomena.pl/
- N8N_ENCRYPTION_KEY=wstaw_dlugi_losowy_klucz
- GENERIC_TIMEZONE=Europe/Warsaw
- TZ=Europe/Warsaw
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:
caddy_data:
caddy_config:
Do tego minimalny Caddyfile w tym samym katalogu:
n8n.twojadomena.pl {
reverse_proxy n8n:5678
}
Caddy sam pobierze i odnowi certyfikat Let's Encrypt dla Twojej domeny - musisz tylko mieć rekord A wskazujący na serwer i otwarte porty 80 oraz 443. Zwróć uwagę: n8n korzysta tu z expose zamiast ports, więc port 5678 nie jest wystawiony publicznie - cały ruch wchodzi przez Caddy.
Na produkcji: ustaw firewall tak, by z internetu dostępne były tylko porty 80 i 443 (oraz SSH). Pułapka: usługa typu localtunnel, którą n8n udostępnia do łatwego odbierania webhooków z usług zewnętrznych (na przykład GitHub), jest wygodna do testów w fazie developmentu, ale nie nadaje się na produkcję - tam zawsze stawiasz własny reverse proxy z HTTPS.
Aktualizacja i utrzymanie n8n
W skrócie: aktualizacja to dwie komendy - pobierasz nowy obraz i restartujesz usługę. Dane zostają w wolumenie, więc nic nie tracisz.
docker compose pull # pobierz najnowszy obraz n8nio/n8n
docker compose up -d # odtwórz kontener na nowym obrazie
Na produkcji: nie polegaj na tagu latest. Przypnij konkretną wersję obrazu (np. image: n8nio/n8n:WERSJA), żeby aktualizacja była świadomą decyzją, a nie niespodzianką po restarcie. Gdy coś po update przestanie działać, robisz rollback, wskazując poprzedni tag. Aktualny numer wersji obrazu sprawdzisz na Docker Hub w repozytorium n8nio/n8n.
Pro tip: restart: unless-stopped (jest w każdym przykładzie) sprawia, że kontener wstaje sam po reboocie serwera. Monitoruj zużycie RAM - jeśli n8n regularnie dobija do limitu i kontener bywa ubijany przez system, to sygnał, że pora na mocniejszy serwer.
Częste błędy i pułapki
Konkret: większość problemów z n8n na Dockerze to nie bugi, tylko brakująca zmienna albo źle ustawiony adres. Oto te, które wracają najczęściej.
| Problem | Przyczyna | Rozwiązanie |
|---|---|---|
| Webhooki nie działają | zły WEBHOOK_URL lub brak HTTPS |
ustaw WEBHOOK_URL na pełny publiczny adres https i postaw reverse proxy |
| Błąd "secure cookie" przy logowaniu | brak HTTPS przy domyślnym ustawieniu | lokalnie N8N_SECURE_COOKIE=false, na produkcji włącz HTTPS |
| Utrata credentiali po odtworzeniu | zmieniony lub niewpisany N8N_ENCRYPTION_KEY |
ustaw stały klucz i trzymaj go w bezpiecznym miejscu |
| Workflow znikają po restarcie | brak wolumenu na /home/node/.n8n |
dodaj named volume lub bind mount |
| Port 5678 zajęty lub brak dostępu | konflikt portu albo firewall | zwolnij port, popraw mapowanie, otwórz porty proxy |
| Kontener ubijany (OOM) | za mało RAM na obciążenie | zwiększ pamięć serwera |
Pułapka numer jeden to brak wolumenu - łatwo postawić n8n "na szybko", zbudować kilka przepływów, a potem stracić je przy pierwszym docker compose down. Pułapka numer dwa to klucz szyfrowania, który opisałem wyżej. Jeśli kontener pada z braku pamięci mimo poprawnej konfiguracji, problemem jest serwer - wtedy warto sprawdzić, jaki VPS pod n8n i lokalny LLM |
n8n Cloud | |
|---|---|---|
| Koszt | cena VPS (od kilkunastu zł/mc) | subskrypcja zależna od planu |
| Gdzie są dane | na Twoim serwerze | na infrastrukturze dostawcy |
| Limity wykonań | brak (zależne od sprzętu) | zależne od planu |
| Utrzymanie | po Twojej stronie (update, backup) | po stronie dostawcy |
| Integracja z lokalnym LLM | tak, w tej samej sieci | utrudniona |
Realny koszt: edycja Community jest darmowa i open-source - przy self-hoście płacisz wyłącznie za serwer i własny czas na utrzymanie. To mocny argument, gdy zależy Ci na prywatności danych. Drugi argument za własnym serwerem to możliwość spięcia n8n z lokalnym LLM na własnym serwerze.
FAQ
Czy n8n self-hosted jest darmowy?
Tak, edycja Community jest bezpłatna i open-source - płacisz wyłącznie za serwer, na którym ją stawiasz. Płatne są n8n Cloud oraz wersja Enterprise z dodatkowymi funkcjami. Dla większości małych wdrożeń darmowy self-host na własnym VPS w zupełności wystarcza do produkcyjnej pracy.
Ile RAM potrzebuje n8n na własnym serwerze?
Minimum to 2 GB RAM, ale komfortowo pracuje się od 4 GB w górę, zwłaszcza przy wielu przepływach i bazie PostgreSQL. Przy złożonych workflow n8n bywa pamięciożerny. Jeśli kontener jest ubijany z braku pamięci, to jednoznaczny sygnał, że pora na mocniejszy serwer.
Czy mogę uruchomić n8n na Dockerze bez Docker Compose?
Można użyć docker run, ale Compose jest standardem na produkcji. Daje powtarzalność (cała konfiguracja w jednym pliku), trwałe dane przez wolumen i łatwy update przez docker compose pull. Dzięki temu odtworzysz instalację na nowym serwerze w minutę, zamiast odtwarzać długą komendę z pamięci.
Jak ustawić HTTPS dla n8n?
Przez reverse proxy postawiony przed kontenerem - najprościej Caddy z automatycznym certyfikatem Let's Encrypt, alternatywnie Nginx z Certbotem lub Traefik. n8n samo nie obsługuje TLS, więc terminację SSL przejmuje proxy. Ustaw przy tym N8N_PROTOCOL=https oraz WEBHOOK_URL na finalny adres swojej domeny.
Gdzie n8n przechowuje dane i workflow?
W katalogu /home/node/.n8n wewnątrz kontenera - tam trafia baza SQLite i zaszyfrowane credentiale. Ten katalog musisz zmapować na wolumen (named volume lub bind mount), inaczej wszystkie workflow i dane znikną przy docker compose down. To najczęstszy powód utraty pracy u początkujących.
n8n na Ubuntu czy Synology - czy działa tak samo?
Tak, plik docker-compose.yml jest identyczny niezależnie od systemu operacyjnego - Docker abstrahuje warstwę pod spodem. Różni się jedynie sposób instalacji samego Dockera na danej platformie. Po jego zainstalowaniu uruchamiasz n8n tym samym poleceniem docker compose up -d na Ubuntu, Debianie czy Synology.
Podsumowanie i następne kroki
W skrócie: n8n stawiasz jednym plikiem compose, a produkcyjną instalację domykasz w kilku krokach. Dobra kolejność jest prosta i powtarzalna na każdym serwerze.
Krok po kroku - checklist wdrożenia: minimalny docker-compose.yml na start, wolumen na trwałe dane, opcjonalnie przesiadka na PostgreSQL przy większym obciążeniu, reverse proxy z HTTPS i własną domeną, a na końcu backup wolumenu. Gdy to działa, masz solidną bazę pod automatyzacje.
Konkret: jeśli chcesz, by przepływy przetwarzały dane bez wysyłania ich do chmury, dorzuć do stosu lokalny LLM na własnym serwerze obok n8n. A jeśli dopiero dobierasz maszynę pod całość, zacznij od tekstu o tym, jaki VPS pod n8n i lokalny LLM udźwignie Twoje obciążenie.