Namestitev Nginx Proxy Manager z Dockerjem

Linux Branje: 13 min

Na strežniku lahko vrata 80 in 443 hkrati uporablja le en program. Če v Dockerju poganjaš več spletnih aplikacij (na primer stran WordPress, n8n in lastno aplikacijo), potrebuješ pred njimi obratni proxy (reverse proxy): ta sprejme vse zahteve, jih glede na zahtevano domeno posreduje ustreznemu vsebniku in upravlja tudi potrdila SSL.

Nginx Proxy Manager (NPM) je tak proxy, s spletnim vmesnikom. Konfiguracijskih datotek Nginx ni treba pisati ročno: domeno, ciljni vsebnik in potrdilo vneseš v nekaj polj, potrdila Let’s Encrypt pa NPM pridobi in podaljšuje sam. Idealen je na lastnem KVM VPS, kjer si več manjših aplikacij deli en naslov IP.

Korake smo novembra 2026 preizkusili v sistemu Ubuntu 24.04, z Docker Engine 29.9.3, Docker Compose 5.7.0 in Nginx Proxy Manager 2.16.0. Testni računalnik ni imel javne domene, zato zahteve za potrdilo Let’s Encrypt nismo mogli izpeljati do konca: predstavimo njen vmesnik, delovanje pa opišemo na podlagi dokumentacije in izvorne kode programa, kar v članku tudi označimo. Posredovanje smo na strežniku preverili z ukazom curl, z navedbo glave Host.

Infografika: zahteve iz interneta prispejo na vrata 80 in 443 Nginx Proxy Managerja, ki jih glede na domeno posreduje vsebnikom app1, app2 in WordPress v omrežju Docker z imenom proxy; MariaDB je v lastnem omrežju projekta, vrata 81 upraviteljskega vmesnika poslušajo le na naslovu 127.0.0.1

Preden začneš

  • Docker in Compose. Njuno namestitev opisuje članek Namestitev Dockerja v sisteme Ubuntu, Debian in AlmaLinux, osnove Compose pa članek Uporaba Docker Compose.
  • Prosta vrata 80 in 443. Če na strežniku teče izvorni Apache ali Nginx, ga ustavi in onemogoči, sicer se vsebnik NPM ne zažene („address already in use”). Kaj posluša na vratih, pokaže sudo ss -ltnp.
  • DNS. Za zahtevo za potrdilo mora zapis A domen (na primer app1.pelda.hu), pri IPv6 pa zapis AAAA, kazati na naslov IP strežnika.
  • Požarni zid. Vrata 80 in 443 morajo biti dosegljiva od zunaj (glej: Kako nastavim požarni zid na svojem VPS?). Docker objavljena vrata odpre z lastnimi pravili, ki imajo prednost pred pravili ufw in firewalld; zato je treba vrata upraviteljskega vmesnika vezati na lokalni naslov, kot je prikazano spodaj.

1. Skupno omrežje Docker

NPM in aplikacije se srečajo v skupnem omrežju Docker. Tako aplikacijam na strežniku ni treba objaviti niti enih vrat: NPM jih doseže znotraj omrežja, z imenom vsebnika. Ustvari ga enkrat:

docker network create proxy

2. Datoteka compose.yaml za NPM

mkdir -p ~/npm && cd ~/npm
nano compose.yaml
services:
  npm:
    image: jc21/nginx-proxy-manager:2.16.0
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "127.0.0.1:81:81"
    environment:
      TZ: "Europe/Ljubljana"
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    networks:
      - proxy

networks:
  proxy:
    external: true
  • Vrata: vrata 80 (HTTP) in 443 (HTTPS) so odprta za vse, to je naloga proxyja. Vrata 81 so upraviteljski vmesnik: zaradi 127.0.0.1:81:81 so dosegljiva le znotraj strežnika. V uradnem opisu je '81:81', tako pa bi bila prijavna stran odprta proti celotnemu internetu, ne glede na požarni zid.
  • Slika: različico zakleni (2.16.0), ne uporabljaj oznake latest, tako posodobitev ostane tvoja odločitev.
  • Nosilci: v mapi data so podatkovna baza (SQLite), nastavitve in dnevniki, v mapi letsencrypt pa potrdila. Ti dve sta predmet varnostnega kopiranja.
  • networks: NPM se poveže z zgoraj ustvarjenim omrežjem proxy; external: true označuje, da omrežja ne upravlja ta projekt.
  • Če strežnik nima IPv6, je treba po dokumentaciji dodati tudi okoljsko spremenljivko DISABLE_IPV6: 'true'.

Zagon in dnevnik zagona:

docker compose up -d
docker compose logs -f

Ob prvem zagonu NPM ustvari podatkovno bazo in svoje ključe. Ko se v dnevniku pojavi vrstica „Backend PID … listening on port 3000”, je pripravljen; iz dnevnika izstopiš s Ctrl+C.

3. Dostop do upraviteljskega vmesnika prek tunela SSH

Ker vrata 81 poslušajo le znotraj strežnika, jih s svojega računalnika dosežeš prek tunela SSH. Na svojem računalniku zaženi (uporabniško ime in naslov zamenjaj s svojima):

ssh -L 8181:127.0.0.1:81 felhasznalo@szerver-ip

Dokler je ta povezava odprta, naslov http://localhost:8181 v brskalniku odpre upraviteljski vmesnik NPM. Če ga pogosto uporabljaš, lahko tunel dodaš tudi v datoteko ~/.ssh/config z vrstico LocalForward (glej: Uporaba datoteke SSH config). O povezavi SSH na splošno govori članek Kako se s SSH povežem s svojim VPS?

4. Prva prijava

Starejše različice so se zagnale s privzetim računom (admin@example.com, geslo changeme) in veliko opisov ga omenja še danes. V različici 2.16.0 takega računa ni: ob prvem odprtju se prikaže stran Welcome!, kjer je treba ustvariti skrbniški račun.

Stran Welcome! Nginx Proxy Managerja ob prvem zagonu s polji Full Name, Email address in New Password ter gumbom Save

Vnesi ime (Full Name), pravi e-poštni naslov (Email address) in močno geslo (New Password), nato klikni gumb Save. V preizkusu je shranjevanje trajalo nekaj sekund, nato se je prikazala prijavna stran.

E-poštni naslov ni potreben le za prijavo: po izvorni kodi programa se NPM s tem naslovom registrira pri Let’s Encrypt, ko zahteva potrdilo, in samodejno sprejme tudi pogoje uporabe Let’s Encrypt. Če ob zahtevi za potrdilo sporočilo o napaki javi neveljaven naslov, ga popravi v svojem računu (zgoraj desno, klik na tvoje ime: Edit Profile).

Po prijavi začetna stran (Dashboard) pokaže število dodanih vnosov:

Začetna stran Nginx Proxy Managerja (Dashboard): 2 Proxy Hosts, 0 Redirection Hosts, 0 Streams, 0 404 Hosts

Upraviteljski vmesnik se zažene v angleščini. Z ikono globusa v zgornjem desnem kotu ga lahko preklopiš v drug jezik (na primer v madžarščino, Magyar); v članku navajamo angleške napise. V istem meniju (klik na tvoje ime) je tudi Two-Factor Auth: upraviteljski vmesnik daje poln dostop do spletnega prometa strežnika, zato je dobro vklopiti dvostopenjsko preverjanje.

5. Dve testni aplikaciji

Za preizkus v omrežju proxy zaženi dve majhni aplikaciji: Nginx s preprosto stranjo (app1) in sliko traefik/whoami (app2), ki izpiše, kakšno zahtevo je prejela.

mkdir -p ~/apps/app1 && cd ~/apps
echo "<h1>To je app1</h1>" > app1/index.html
nano compose.yaml
services:
  app1:
    image: nginx:alpine
    restart: unless-stopped
    volumes:
      - ./app1:/usr/share/nginx/html:ro
    networks:
      - proxy

  app2:
    image: traefik/whoami
    restart: unless-stopped
    networks:
      - proxy

networks:
  proxy:
    external: true
docker compose up -d

Nobena storitev nima dela ports: vsebniki niso dosegljivi na nobenih vratih strežnika, le v omrežju proxy, prek NPM. Člane omrežja pokaže docker network inspect proxy.

6. Dodajanje proxy hosta

V zgornjem meniju izberi Hosts › Proxy Hosts, nato gumb Add Proxy Host. Na zavihku Details:

  • Domain Names: domena, na katero prihajajo zahteve (app1.pelda.hu). Po tipkanju pritisni Enter; navedeš lahko več domen.
  • Scheme: http. Vsebniki v notranjem omrežju večinoma govorijo navaden HTTP, za HTTPS poskrbi NPM.
  • Forward Hostname / IP: ime vsebnika v skupnem omrežju (app1).
  • Forward Port: notranja vrata vsebnika, na katerih posluša aplikacija (pri Nginx in whoami 80), ne objavljena vrata.
  • Block Common Exploits: filtrira nekaj pogostih vzorcev napadov; lahko ga vklopiš.
  • Websockets Support: vklopi, če aplikacija uporablja WebSocket (na primer vmesniki v realnem času in klepetalnice).

Zavihek Details okna Add Proxy Host: Domain Names app1.pelda.hu, Scheme http, Forward Hostname app1, Forward Port 80, Access List Publicly Accessible, vklopljen Block Common Exploits

Po shranjevanju (Save) na enak način dodaj drugo aplikacijo (app2.pelda.hu, app2, 80).

Seznam Proxy Hosts s tremi vnosi: app1.pelda.hu, app2.pelda.hu in wp.pelda.hu, s ciljnim naslovom, SSL HTTP Only, v stolpcu Access pri enem seznam dostopa „Csak jelszóval” (samo z geslom), vsi v stanju Online

Stanje Online pomeni le, da je nastavitev Nginx veljavna, ne pa, da ciljni vsebnik odgovarja. V preizkusu sta bila Online tudi vnosa, ki sta kazala na napačna vrata (app1:8080) in na neobstoječ vsebnik, brskalnik pa je pri obeh dobil napako 502 Bad Gateway. Drugi vnosi so medtem delovali brezhibno.

Preizkus brez DNS

Preden preusmeriš DNS, lahko proxy preizkusiš na strežniku tudi z ukazom curl: stikalo -H "Host: ..." pošlje domeno, ki bi jo poslal brskalnik.

curl -s -H "Host: app1.pelda.hu" http://127.0.0.1/
curl -s -H "Host: app2.pelda.hu" http://127.0.0.1/ | head -3

Izpis curl na strežniku: app1.pelda.hu vrne stran „To je app1”, app2.pelda.hu odgovor whoami z geslom, brez gesla kodo 401, pod tem vrata vsebnika NPM

Na sliki je app2 že zaščiten s spodaj predstavljenim seznamom dostopa, zato je brez gesla dobil kodo 401.

S svojega računalnika ga v brskalniku preizkusiš tako, da v datoteko hosts (v Linuxu in macOS /etc/hosts, v Windows C:\Windows\System32\drivers\etc\hosts) dodaš domeno z naslovom IP strežnika.

Če prispe domena, za katero ni proxy hosta (ali če kdo vpiše neposredno naslov IP), NPM pokaže svojo stran „Congratulations”. To lahko v točki Settings › Default Site zamenjaš z nastavitvijo 404 Page, No Response (444), Redirect ali Custom HTML; na produkcijskem strežniku je običajna stran 404 ali zaprtje brez odgovora (444).

Povezava obstoječega projekta Compose

Že delujočo aplikacijo povežeš tako, da njeno spletno storitev v compose.yaml dodaš tudi v omrežje proxy in izbrišeš njen del ports. Na primeru WordPress in MariaDB to prikazuje članek Uporaba Docker Compose, v delu o obratnem proxyju. Podatkovna baza naj ostane v lastnem omrežju projekta: v preizkusu ime db s strani proxyja sploh ni bilo razrešljivo.

V polje Forward Hostname je takrat dobro vpisati polno ime vsebnika (v preizkusu wordpress-wordpress-1, izpiše ga docker compose ps). Delovalo je tudi kratko ime storitve (wordpress), vendar kratko ime ni enoznačno, če je v omrežju proxy storitev z istim imenom v dveh projektih.

Datoteka wp-config.php uradne slike WordPress Docker spremlja glavo X-Forwarded-Proto, ki jo pošlje proxy, zato tudi za HTTPS ne pride v zanko preusmeritev; v preizkusu je WordPress zahtevo z glavo X-Forwarded-Proto: https že preusmeril na naslov https://. Pri drugih aplikacijah v dokumentaciji poglej, kako nastaviti delovanje za proxyjem (pogosto nastavitev „trusted proxy” ali „base URL”).

7. Potrdilo SSL z Let’s Encrypt

Potrdilo zahtevaš na zavihku SSL okna za urejanje proxy hosta (na seznamu ga odpre ⋮ › Edit na koncu vrstice):

  1. SSL Certificate: izberi možnost Request a new Certificate.
  2. Vklopi stikalo Force SSL (zahteve HTTP preusmeri na HTTPS) in stikalo HTTP/2 Support.
  3. Save.

Zavihek SSL okna Add Proxy Host: SSL Certificate Request a new Certificate, vklopljena Force SSL in HTTP/2 Support, izklopljena HSTS Enabled in Use DNS Challenge

Tega koraka nismo mogli preizkusiti, ker testni strežnik ni imel javne domene; naslednje temelji na dokumentaciji in izvorni kodi. Privzeto NPM potrdilo zahteva s preverjanjem HTTP-01: Let’s Encrypt strežnik poišče na vratih 80, pod imenom domene. Zato potrdilo nastane le, če DNS domene že kaže na strežnik in so vrata 80 dosegljiva od zunaj. Če kaj od tega ni izpolnjeno, se zahteva ustavi z napako. Potrdila NPM podaljšuje sam: v preizkusu sta se v dnevniku ob zagonu pojavili vrstici „Let’s Encrypt Renewal Timer initialized” in „Renewing SSL certs expiring within 30 days”.

Stikalo HSTS Enabled vklopi le, če si prepričan, da bo stran trajno ostala na HTTPS: brskalniki je nato nekaj časa sploh ne poskušajo odpreti prek HTTP.

Potrdila lahko upravljaš tudi posebej v meniju Certificates. Gumb Add Certificate ponuja možnosti Let’s Encrypt via HTTP, Let’s Encrypt via DNS in Custom Certificate (nalaganje lastnega, na primer kupljenega potrdila). Gumb Test v oknu HTTP po izvorni kodi z zunanjo storitvijo preveri, ali je domena dosegljiva od zunaj, preden se obrne na Let’s Encrypt; dobro ga je zagnati pred zahtevo.

Preverjanje DNS in potrdilo wildcard

Če vrata 80 ne smejo biti odprta ali potrebuješ potrdilo wildcard (*.pelda.hu), vklopi stikalo Use DNS Challenge. Takrat NPM prek API-ja ponudnika DNS ustvari zapis TXT, Let’s Encrypt pa ga preveri. S preverjanjem HTTP potrdila wildcard ni mogoče zahtevati, vmesnik to tudi izpiše: „Wildcards not permitted for this type”.

Zavihek SSL z vklopljenim stikalom Use DNS Challenge: DNS Provider Cloudflare, v polju Credentials File Content vzorčni žeton API, pod njim opozorilo, da se podatki shranijo nešifrirani, in polje Propagation Seconds

Na seznamu DNS Provider je bilo v preizkusu 88 ponudnikov, med njimi Cloudflare, DigitalOcean, Hetzner, OVH, Amazon Route 53 in standardni RFC 2136 (za lastni strežnik DNS). V polje Credentials File Content pride dostop do API-ja ponudnika, polje vnaprej pokaže vzorec izpolnjevanja. NPM opozori, da to shrani nešifrirano v podatkovno bazo in v datoteko („This data will be stored as plaintext in the database and in a file!”), zato vnesi le žeton API, omejen na zapise DNS določene cone, ne ključa celotnega računa. Propagation Seconds lahko pustiš prazno; če ponudnik zapise objavlja počasi, ga povečaj.

Če NPM ne uporabljaš, lahko potrdilo Let’s Encrypt zahtevaš tudi neposredno, glej: Pridobitev potrdila Let’s Encrypt s Certbotom.

8. Seznami dostopa (Access Lists)

Aplikacijo lahko omejiš z geslom ali po naslovu IP, na primer skrbniški vmesnik ali napol dokončano testno stran. V meniju Access Lists z gumbom Add Access List ustvari seznam:

  • Details: ime seznama (Name). Vklopljen Satisfy Any pomeni, da zadošča geslo ali dovoljeni naslov IP; izklopljen zahteva oboje. Pass Auth to Upstream glavo z geslom posreduje tudi aplikaciji.
  • Authorizations: pari uporabniškega imena in gesla (Username, Password, Add).
  • Rules: pravila Allow in Deny za naslov IP ali obseg (na primer 192.168.1.0/24), v navedenem vrstnem redu. Če je vsaj eno pravilo, se na konec seznama samodejno doda „deny all”.

Zavihek Authorizations okna Add Access List: Username teszt, izpolnjen Password, gumb Add

Po shranjevanju v polju Access List proxy hosta namesto Publicly Accessible izberi seznam. V preizkusu je tako zaščiten app2.pelda.hu brez gesla vrnil kodo 401, s pravilnim uporabniškim imenom in geslom (curl -u teszt:jelszo ...) pa odgovor whoami. Geslo po HTTP potuje nešifrirano, zato zaščito z geslom uporabljaj le skupaj s potrdilom SSL.

9. Streams: posredovanje TCP in UDP

V meniju Hosts › Streams lahko vsebniku posreduješ nespletni promet (na primer vrata podatkovne baze ali igralnega strežnika). Tega nismo preizkusili. Po dokumentaciji je treba vrata streama objaviti tudi v datoteki compose.yaml NPM (na primer - "5432:5432"), sicer zahteva sploh ne pride do NPM. Podatkovno bazo tako izpostavi le, če jo res potrebuješ od zunaj; večinoma je tunel SSH varnejši.

10. Zaščita upraviteljskega vmesnika

  • Ne objavi vrat 81. Z zgornjo nastavitvijo 127.0.0.1:81:81 prijavna stran od zunaj ni dosegljiva, le prek tunela SSH. Da je res tako, na strežniku pokaže sudo ss -ltnp | grep ':81 ': v preizkusu je bil naveden naslov 127.0.0.1:81, ne 0.0.0.0:81.
  • Močno geslo in dvostopenjsko preverjanje na računu (Two-Factor Auth).
  • Lasten račun za vsakogar. V meniju Users lahko dodaš še druge uporabnike; skrbniškega računa ne deli z drugimi, pravico Administrator pa daj le tistemu, ki jo res potrebuje.
  • O zaščiti SSH govori članek Varnostne nastavitve strežnika SSH, saj je upraviteljski vmesnik nato dosegljiv prek SSH.

11. Varnostno kopiranje

Celotno stanje NPM je v mapah data in letsencrypt. Shrani ju skupaj z datoteko compose.yaml, ob kratki zaustavitvi:

cd ~/npm
docker compose stop
sudo tar czf ~/npm-mentes-$(date +%F).tar.gz -C ~/npm data letsencrypt compose.yaml
docker compose start

Lastnik map je root, zato je potreben sudo. V preizkusu je zaustavitev trajala le nekaj sekund; v tem času tudi strani za proxyjem niso dosegljive, zato pri prometni strani varnostno kopiranje časovno načrtuj v mirno obdobje. Ob obnovitvi kopijo razpakiraj v mapo ~/npm novega strežnika, ustvari omrežje proxy in ga zaženi z ukazom docker compose up -d. Kopijo prekopiraj tudi na drug računalnik.

Varnostna kopija vsebuje tudi zasebne ključe potrdil in žetone API ponudnika DNS: ravnaj z njo temu primerno.

12. Posodobitev

Pred posodobitvijo naredi varnostno kopijo in preberi opombe ob izdaji nove različice na strani projekta na GitHubu. Nato v compose.yaml sliko prepiši na novo različico (na primer namesto jc21/nginx-proxy-manager:2.16.0 na novejšo), nato:

cd ~/npm
docker compose pull
docker compose up -d

Spremembe podatkovne baze NPM ob zagonu opravi sam (v dnevniku so vidne kot vrstice „Migrating Up…”). O brisanju stare slike govori članek Brisanje slik, vsebnikov in nosilcev Docker.

Pogoste napake

  • 502 Bad Gateway: NPM ne doseže ciljnega vsebnika. Preveri, ali vsebnik teče (docker compose ps), ali je v omrežju proxy (docker network inspect proxy) in ali je Forward Port notranja vrata vsebnika.
  • Zahteva za potrdilo ne uspe: DNS domene še ne kaže na strežnik (širjenje spremembe lahko traja tudi več ur) ali vrata 80 niso dosegljiva od zunaj. Če vrat 80 ne moreš odpreti, uporabi preverjanje DNS.
  • Vsebnik NPM se ne zažene, „address already in use”: vrata 80 ali 443 zaseda drug program; sudo ss -ltnp pokaže, kateri.
  • Zanka preusmeritev za HTTPS: aplikacija ne ve, da jo dosegajo prek HTTPS. V aplikaciji nastavi delovanje za proxyjem (glej zgoraj).
Nazaj: Linux – članki (76)