Nginx vs Caddy vs Traefik: Alin ang piliin?
Ihambing ang Nginx, Caddy, at Traefik sa TLS certificates, config bawat app, websockets, at Docker routing para sa 4 na app sa isang VPS at public IP.
Nginx kumpara sa Caddy kumpara sa Traefik: ang maikling sagot
Pare-pareho ang pangunahing gawain ng Nginx, Caddy, at Traefik bilang reverse proxy: nakikinig sila sa port 443, binabasa ang hostname sa bawat request, at ipinapasa ito sa tamang serbisyo sa iyong VPS. Kahit alin sa tatlo ay maaaring maglagay ng apat na self-hosted app sa likod ng iisang public IP address. Sapat ang bilis ng lahat ng ito kaya ang mga app mo ang malamang na maging mabagal na bahagi. Ang nagkakaiba ay kung paano kumukuha ang bawat isa ng TLS (transport layer security) certificate at kung gaano karaming configuration ang kailangan sa bawat karagdagang app. Lumilitaw ang isa pang pagkakaiba kapag kailangan mo na ng feature na hindi kasama sa mga karaniwang tutorial.
Piliin ang Caddy kung gusto mong awtomatikong pangasiwaan ang HTTPS at karaniwang web app ang iyong mga serbisyo. Piliin ang Traefik kung tumatakbo ang lahat sa Docker Compose at nagdadagdag ka ng bagong serbisyo bawat ilang linggo. Piliin ang Nginx kung ginagamit mo na ito, o kung kailangan mo ng response caching, client certificates, raw TCP forwarding, o malaking kasalukuyang configuration na ayaw mong isulat muli.
Paano kumukuha ng TLS certificate ang bawat isa?
Para sa karamihan, ito ang pangunahing pinagkaiba, kaya magsimula rito. Sa huli, pare-pareho silang may hawak na certificate mula sa iisang authority. Magkakaiba ang mga hakbang para makuha ito.
Humihiling si Caddy ng certificate dahil nagtakda ka ng hostname. Isulat ang app.example.com bilang site address. Hihiling si Caddy ng certificate gamit ang ACME (automatic certificate management environment) mula sa Let's Encrypt. Kung mabigo ito, gagamit ito ng ZeroSSL. Awtomatiko rin nitong ise-serve ang HTTP-to-HTTPS redirect sa port 80 at ire-renew ang certificate. Walang kailangang ikalawang tool o timer na susuriin. Nasa data directory ng caddy user ang mga certificate, sa /var/lib/caddy/.local/share/caddy kapag package install ang ginamit. Idagdag ang path na ito sa backups, o tanggapin ang panibagong issuance pagkatapos ng rebuild. Para sa hostname na hindi public, pipirma si tls internal gamit ang sariling local certificate authority ni Caddy. Pareho ang resulta nito sa paglikha ng self-signed certificate sa Ubuntu, ngunit si Caddy na ang bahala sa renewal.
Walang ACME client ang Nginx. Kinukuha ng Certbot ang certificate, at ginagamit ng --nginx plugin nito upang baguhin ang server block at idagdag ang 443 listener at redirect. Isinasagawa ang renewal ng systemd timer na ini-install ng package. Dahil dito, may dalawang gumagalaw na bahagi at dalawang bagay na kailangang i-verify: ipinapakita ng systemctl list-timers | grep certbot na umiiral ang timer, at pinatutunayan ng sudo certbot renew --dry-run na gumagana pa ang renewal path. Makikita ang step-by-step na proseso sa Certbot sa Ubuntu 24.04 gamit ang Nginx. Saklaw din ng parehong tool ang wildcard certificate sa pamamagitan ng DNS-01 challenge kapag mas marami kang subdomain kaysa sa nais mong isa-isahin.
May sarili nitong ACME client ang Traefik. Mag-configure ng isang certificate resolver sa static configuration. Pagkatapos, magagamit ito ng bawat router. Nasa iisang acme.json file ang lahat ng state, pati ang account key at mga certificate. Hindi gagamitin ng Traefik ang file na ito kung nababasa ito ng ibang user bukod sa owner nito. Ipapaalam nito sa iyo ang problema bago nito alisin ang resolver:
The ACME resolver "le" is skipped from the resolvers list because: unable to get ACME account: permissions 660 for /letsencrypt/acme.json are too open, please use 600Mag-mount ng directory at hayaang si Traefik ang gumawa ng file. Gawin muna ito gamit ang touch. Sa ganitong paraan, mamamana nito ang iyong umask, na siyang karaniwang dahilan kung bakit natutugunan ng karamihan ang requirement na iyon.
May isang bagay na pare-pareho sa tatlo. Kailangang reachable mula sa internet ang port 80 para sa HTTP-01 challenge dahil kokonekta rito ang certificate authority. Kapag port 443 lang ang binuksan mo, mabibigo ang issuance sa paraang magmumukhang DNS fault.
Ang parehong routing job para sa dalawang app sa tatlong config
Ang gawain: mapupunta ang app.example.com sa isang service sa 127.0.0.1:8080, at ang files.example.com sa isang service sa 127.0.0.1:8081, parehong gamit ang HTTPS. Narito ang buong config para sa bawat proxy, para makita ang pagkakaiba sa verbosity sa halip na sabihin lamang ito.
Nginx
# /etc/nginx/sites-available/app.example.com
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
}Pagkatapos, i-link ito, i-test, i-reload, at idagdag ang certificate.
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d app.example.comAng pag-print ng syntax is ok at test is successful ng nginx -t ang check na dapat patakbuhin bago ang bawat reload. Ang pangalawang app ay kaparehong block, ngunit binago ang hostname at port. Hindi palamuti ang mga linyang proxy_set_header: kapag tinukoy ng proxy_pass ang isang address, ipinapadala ng nginx ang Host: 127.0.0.1:8080 upstream bilang default. Dahil dito, ang app na bumubuo ng absolute URL mula sa Host header ay magpapadala sa mga user sa localhost. Ang gamit ng apat na header na ito, pati ang dahilan kung bakit tahimik na binabago ng trailing slash sa proxy_pass ang path na natatanggap ng app, ay ipinaliliwanag directive by directive sa walkthrough na ito ng nginx server block.
Caddy
app.example.com {
reverse_proxy 127.0.0.1:8080
}
files.example.com {
reverse_proxy 127.0.0.1:8081
}sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddyIyon na ang buong file. Awtomatikong itinatakda ng reverse_proxy ang X-Forwarded-For, X-Forwarded-Proto, at X-Forwarded-Host, at bilang default ay binabalewala nito ang ipinadala ng client sa mga header na iyon. Kaya hindi maaaring magsinungaling ang request sa backend tungkol sa pinagmulan nito. Ang certificates, ang redirect mula port 80, at ang renewal ay awtomatikong sumusunod sa dalawang site address. Walang ibang kailangan sa file para sa mga ito.
Traefik
Kailangan muna ng static configuration ng Traefik bago ito makapag-route ng anumang request. Bilang Compose service, gamit ang image tag na kasalukuyan noong August 2026:
services:
traefik:
image: traefik:v3.7
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.le.acme.email=you@example.com"
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencryptPagkatapos, ang bawat application ay may sarili nitong routing sa labels, sa sarili nitong compose file:
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.example.com`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls.certresolver=le"
- "traefik.http.services.app.loadbalancer.server.port=8080"Ang loadbalancer.server.port ay ang port sa loob ng container, hindi isang published port, dahil ina-access ng Traefik ang container sa shared Docker network. Hindi na kailangan ng app ng linyang ports:, at iyon ang tunay na pakinabang: si Traefik lamang ang naka-publish. Makikita ang buong setup, kasama ang shared network at redirect middleware, sa pag-route ng maraming app gamit ang Traefik at Docker Compose.
Magkano ang configuration cost ng bawat dagdag na app?
The data behind this chart
[
{
"tool": "Nginx",
"proxy_setup_lines": 0,
"lines_per_app": 11
},
{
"tool": "Caddy",
"proxy_setup_lines": 0,
"lines_per_app": 3
},
{
"tool": "Traefik",
"proxy_setup_lines": 17,
"lines_per_app": 5
}
]Batay ito sa mga block sa itaas. Ang Nginx server block ay 11 non-blank na linya, at isinusulat mo itong muli para sa bawat hostname. Ang Caddy site block ay 3 linya. Nangangailangan ang Traefik ng 17 linya ng static configuration bago ito makapagsilbi ng kahit isang request, at pagkatapos ay 5 label para sa bawat app.
Unawain ang trade-off, hindi lamang kung alin ang nanalo. Pinakamalaki ang cost ng Traefik bago ang unang app, pero pinakamaliit ang dagdag na cost para sa bawat kasunod na app. Nagtatagpo ang dalawang total sa bandang ikatlong site. Kapag mas kaunti rito, overhead lamang ang static configuration na hindi mo naman kailangan. Kapag mas marami rito, nauungusan ng labels ang iba at patuloy itong lumalaki ang lamang dahil nasa tabi ng service ang routing configuration nito. Kapag dinelete mo ang service, kasama nitong nawawala ang route. Mahina rito ang central config file: nag-iiwan ito ng stale server blocks para sa mga app na ilang buwan nang wala.
Pinapaboran din ng line count ang Nginx. Nangangailangan ang bawat block ng symlink, isang nginx -t, reload, at certbot run, samantalang isang reload lang ang kailangan para sa Caddy edit at wala nang kailangang command para sa Traefik edit. Lahat ng tatlo ay nagre-reload nang hindi ibinababa ang mga aktibong connection. Ang pinagkaiba ay ang dami ng magkakahiwalay na hakbang na kailangan mong tandaan nang ala-una ng madaling-araw.
Alin ang may alam sa iyong mga container?
Binabantayan ng Traefik ang Docker socket at bumubuo ng mga router mula sa container labels habang nagsisimula at humihinto ang mga container. Wala sa iba rito ang gumagawa nito. Kailangang i-edit ang configuration at i-reload ang Nginx at Caddy kapag may bagong container. Kailangan din nila ng address na maaabot nila: maaaring port na naka-publish sa loopback, o shared Docker network na kinabitan ng proxy.
May kapalit ang feature na ito, at kailangang sabihin ito nang malinaw. Binabasa ng Traefik ang /var/run/docker.sock. Ang sinumang makakakonekta sa socket na iyon ay maaaring magsimula ng container na may naka-mount na host filesystem sa loob nito. Nagbibigay ito ng root access sa host. Binabawasan ng read-only na pag-mount ang panganib, pero hindi nito inaalis ang panganib. Kung mahalaga ito sa iyong threat model, maglagay ng socket proxy sa pagitan na naglalantad lamang ng mga container list endpoint na kailangan ng Traefik.
Maaari ring gumamit ang Caddy ng label-based discovery sa pamamagitan ng community plugin. Gayunman, naka-compile sa Caddy ang mga plugin, kaya kailangan mong bumuo ng custom binary o custom image gamit ang xcaddy. Ikaw rin ang mamamahala sa build na iyon at sa mga update nito. Para sa tatlo o apat na serbisyo, mas kaunting trabaho ang pag-edit ng Caddyfile.
WebSocket at streaming: ano ang nasisira, at bakit
Nginx ang kailangang i-configure. Nagsisimula ang isang WebSocket connection bilang HTTP request na may dalang Upgrade: websocket, at hindi ipinapasa ng nginx sa upstream ang hop-by-hop headers maliban kung tahasan mo itong i-configure.
# /etc/nginx/conf.d/upgrade-map.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}Pagkatapos, sa loob ng location block, kailangang naroon ang tatlong linyang ito:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;Kapag wala ang mga ito, magpi-print ang browser console ng WebSocket connection to 'wss://app.example.com/ws' failed habang ordinaryong GET ang ipinapakita ng backend log. Kailangan ang map dahil ang hardcoded na Connection: upgrade ay ipapadala sa bawat request, pati sa mga ordinaryong request na dapat ay may close.
May dalawa pang default ng Nginx na nagdudulot ng problema. Ang proxy_read_timeout ay 60 segundo at nalalapat ito sa tunnel pagkatapos ng upgrade. Kaya isinasara ng proxy ang WebSocket na walang traffic sa loob ng isang minuto. Nahuhuli o dumarating nang burst ang server-sent events hanggang itakda mo ang proxy_buffering off; sa location na iyon, dahil bina-buffer ng nginx ang response habang hinihintay ito ng page.
Isinasagawa ng Caddy ang upgrade at inililipat ang connection sa two-way tunnel nang walang anumang directive. Agad din itong nagfa-flush kapag ang response ay text/event-stream o walang kilalang haba, kaya gumagana ang streaming nang walang karagdagang configuration. Ipinapasa ng Traefik ang mga upgrade at hindi nito bina-buffer ang mga response maliban kung ikaw mismo ang magdagdag ng buffering middleware. Kung may chat, web terminal, log tails, o live dashboards ang mga serbisyo mo, malaking pagkakaiba ito sa dami ng configuration na kailangan mong isulat at i-debug.
Ang buong Nginx server block, kasama ang WebSocket at SSE
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name app.example.com;
client_max_body_size 64m;
location / {
proxy_pass http://127.0.0.1:8080;
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 $connection_upgrade;
proxy_read_timeout 3600s;
proxy_buffering off;
}
}Ang map ay dapat nasa http context, hindi sa loob ng server. Kaya ilagay ito sa sarili nitong file sa ilalim ng /etc/nginx/conf.d/. I-off ang proxy_buffering sa mga location lang na nagse-stream, dahil ang buffering ang nagbibigay-daan sa nginx na maagang palayain ang backend worker para sa mga ordinaryong response. Nire-rewrite ng Certbot ang block na ito kapag pinatakbo mo ito, kaya basahin muli ang file pagkatapos.
Ano ang mangyayari kapag may kailangan kang hindi karaniwan?
Dito nagiging sulit ang mga karagdagang linya ng Nginx.
- Mga client certificate, na tinatawag ding mTLS (mutual TLS), kung saan kailangan ding magpakita ng certificate ang client. Kailangan ng Nginx ang
ssl_client_certificate /etc/ssl/ca.pem;atssl_verify_client on;sa server block. Kailangan naman ng Caddy ngclient_authblock sa loob ngtls. Hindi ito kayang i-express ng Traefik labels: magde-define ka ng TLS option sa isang file provider at ituturo rito ang router gamit angtraefik.http.routers.app.tls.options=mtls@file. May exception ang modelong lahat ay nasa labels sa unang pagkakataong kailangan mo nito. - Malalaking upload. Nililimitahan ng Nginx sa 1 MB ang request body bilang default. Nagbabalik ng
413 Request Entity Too Largeang mas malaking upload, at sinasabi ng error log angclient intended to send too large body. Itaas angclient_max_body_size. Walang body limit ang Caddy at Traefik bilang default, kaya nakakarating ang request sa app at ang sariling limit ng app ang nagpapasya. - Pag-cache ng response. May
proxy_cacheang Nginx, at mature na ito. Kailangan ng Caddy ng plugin na naka-compile. Walang HTTP cache ang open source build ng Traefik, kaya nagugulat ang mga taong umaasang nagca-cache ang bawat proxy. - Raw TCP o UDP, para sa database port o game server. May
streammodule ang Nginx. May sariling TCP at UDP router ang Traefik sa mga entrypoint nito. Kailangan ng Caddy ng isa pang plugin, kaya kailangan na naman ng custom build. - May web server nang nasa likod ng proxy. Kung classic PHP application ang serbisyo, kasama na ng LAMP stack sa Ubuntu 24.04 ang Apache, at kapag naglagay ka pa ng proxy sa unahan, magkakaroon ka ng dalawang lugar na nagse-set ng headers at dalawang lugar na maaaring mag-rewrite ng URL. Magpasya kung alin ang magte-terminate ng TLS, pagkatapos ay panatilihing plain HTTP ang isa at i-bind ito sa loopback.
Ang firewall trap na sumusunod sa pagpiling ito
Ang layunin ng reverse proxy ay 80 at 443 lamang ang bukas. Tahimik itong binabago ng Docker. Kapag nag-publish ka ng port gamit ang -p 8080:80, nagsusulat ito ng DNAT rule sa nat table. Sinusuri ang rule na ito bago ang INPUT rules na mina-manage ng ufw. Dahil dito, hindi ito bina-block ng ufw deny 8080, at napupunta ang app mo sa public internet katabi ng proxy na maingat mong na-configure. I-bind ang mga published port sa loopback gamit ang 127.0.0.1:8080:80. Maaari mo ring alisin nang buo ang ports: at hayaang maabot ng proxy ang container sa Docker network. Ito ang ginagawa ng halimbawa para sa Traefik sa itaas. Ipinaliwanag ang mechanism at fix sa kung bakit nilalampasan ng Docker ang ufw para sa mga published port.
Subukan ito mula sa machine na hindi ang VPS. Palaging nagtatagumpay ang check na pinapatakbo mismo sa server:
curl --max-time 5 http://your.server.address:8080Ang Connection refused o timeout ang resultang gusto mo. Kung may HTTP response, reachable ang app nang hindi dumadaan sa proxy. Ibig sabihin, dekorasyon lamang ang lahat ng na-configure mo sa itaas.
Aling proxy ang dapat piliin?
Karamihan ay static site, kasama ang isa o dalawang app: Caddy. Inaalis ng automatic HTTPS ang pinakamalaking paulit-ulit na gawain. Sapat na maikli ang configuration para mabasa sa isang screen, at ang isang static site ay isang root line at isang file_server line sa loob ng parehong site block. Kapalit nito ang mas maliit na koleksiyon ng mga copy-paste na sagot kapag may hindi pangkaraniwang problema.
Isang docker-compose homelab na patuloy mong dinaragdagan: Traefik. Paglampas sa ikatlong service, mas kaunting trabaho ang labels kaysa sa pag-edit ng central file, at kasama ng dinelete na service ang route nito. Maglaan ng isang hapon para sa unang setup, dahil bagong vocabulary ang entrypoints, routers, services, at middlewares. Karaniwang lumalabas ang typo sa label bilang 404 mula sa Traefik, sa halip na failure sa pagsisimula, kaya basahin ang docker logs traefik para sa parse error bago ipalagay na sira ang app.
May existing na Nginx config, o may requirement mula sa listahan sa itaas: Nginx. Mayroon na itong solusyon para sa response caching at client certificates, at halos lahat ng third-party guide ay ipinapalagay na ginagamit ito. Kapalit nito, ikaw ang nagko-configure ng certificates at websocket support sa halip na awtomatiko mo itong makuha.
May isang patakaran anuman ang piliin mo. Isang process lang ang nakikinig sa public interface, at lahat ng iba pa ay nakikinig sa loopback o sa isang private Docker network.
FAQ
Aling reverse proxy ang pinakamainam para sa ilang Docker app sa isang VPS?
Para sa tatlo o apat na service na paminsan-minsan mong dinaragdagan, sulit ang Traefik dahil dala ng bawat app ang sarili nitong routing labels at hindi kailangang mag-edit ng central file. Kung stable ang mga service at ang pangunahing gusto mo ay hindi na ikaw ang mamroblema sa HTTPS, mas kaunti ang kailangang pag-aralan at mas kaunti ang posibleng masira sa Caddy. Piliin ang Nginx kung pamilyar ka na rito, o kung kailangan mo ng feature na wala sa dalawang iba pa, gaya ng response caching o plain TCP listener.
Kailangan ba talaga ng Caddy ng certificate configuration?
Para sa karaniwang setup, hindi. Ang pagtatakda ng public hostname bilang site address ang buong configuration: hinihiling ng Caddy ang certificate sa pamamagitan ng ACME, inihahatid ang redirect mula sa port 80, at nire-renew ito bago mag-expire. Dalawang bagay pa rin ang kailangang matupad. Dapat reachable mula sa internet ang port 80 para sa HTTP-01 challenge, at dapat nakaturo na sa VPS ang DNS A o AAAA record ng hostname, dahil nire-resolve ng certificate authority ang pangalan at kumokonekta ito pabalik sa VPS.
Maaari ko bang patakbuhin ang Nginx at Traefik sa iisang VPS?
Hindi sa parehong port. Ang proxy na mauunang magsimula ay makakakuha ng port; mabibigo ang susunod na mag-bind, at sinasabi ng nginx ang bind() to 0.0.0.0:443 failed (98: Address already in use) habang nagla-log ang Traefik ng katulad na bind error at humihinto. Patakbuhin ang isang proxy sa 80 at 443, at ilagay sa likod nito ang lahat ng iba pa. Kung nagmi-migrate ka, ilipat ang mga hostname nang paisa-isa: ipasa muna ng front proxy ang traffic sa lumang proxy gamit ang loopback port hanggang mailipat ang huling site.
Bakit napuputol ang mga websocket ko pagkalipas ng 60 segundo sa likod ng Nginx?
Ang proxy_read_timeout ay may default na 60 segundo at nalalapat ito sa tunnel kapag kumpleto na ang upgrade, kaya isinasara ng proxy ang koneksiyong walang traffic sa loob ng isang minuto, hindi ng app mo. Taasan ito sa lokasyong iyon gamit ang proxy_read_timeout 3600s;, o magpadala ang application ng ping frame bawat 30 segundo. Hindi isinasara ng Caddy at Traefik ang mga idle na upgraded connection gamit ang one-minute timer, kaya maaaring stable ang parehong app sa likod ng mga ito ngunit hindi stable sa likod ng Nginx.