Traefik v3 के साथ Docker Compose में 5 ऐप्स कैसे चलाएं
Traefik v3 का उपयोग करके एक IP पर पांच ऐप्स को कॉन्फ़िगर करें। Host rule और Let's Encrypt सेटअप सीखें और acme.json की उस सामान्य गलती से बचें जो कंटेनर को शुरू होने से रोकती है।
एक IP, पाँच ऐप्स, एक पोर्ट 443
आपके VPS पर एक ही public IPv4 address और एक ही TCP port 443 है। आप उस पर Gitea, अपने ऐप की एक staging copy, एक internal dashboard, एक status page और एक webhook receiver चलाना चाहते हैं—पाँच hostnames, एक ही सर्वर। एक reverse proxy वह process है जो :80 और :443 को control करती है, हर request पर Host header को पढ़ती है और उसे सही container तक पहुँचाती है। Traefik यह काम करता है, और यह आपके द्वारा हाथ से certbot चलाए बिना ही हर hostname के लिए certificate प्राप्त और renew करता है। Nginx और Caddy भी इन्हीं पाँच hostnames को कुशलतापूर्वक handle कर सकते हैं, इसलिए यदि चुनाव अभी बाकी है, तो certificate handling और प्रति ऐप configuration लागत के आधार पर तीनों proxies की तुलना करना फायदेमंद होगा, इससे पहले कि आप सब कुछ किसी एक के साथ जोड़ें।
Traefik को nginx के server {} block से जो चीज़ अलग करती है, वह है इसकी configuration का स्रोत। Nginx के साथ आप एक file edit करते हैं और reload करते हैं, और certificate का lifecycle एक अलग काम बना रहता है, जो कि वह workflow है जिसका आप तब पालन करते हैं जब आप nginx पर certbot के साथ Let's Encrypt certificates जारी करते हैं, जहाँ renewal timer web server से पूरी तरह बाहर होता है। Traefik का Docker provider Docker event stream को monitor करता है और आपके containers से labels पढ़ता है: एक ऐसा container start करें जिसमें Host() rule label हो और वह एक सेकंड के भीतर routable हो जाता है; उसे stop करें और route गायब हो जाता है। यही इसमें एक जोखिम भी है। Labels में मौजूद configuration एक साथ पाँच जगहों पर होती है, और यदि कोई label गलत हो, तो कोई error नहीं आता; container बस route नहीं होता और Traefik कुछ नहीं कहता।
चार संज्ञाएं
- Entrypoints listening sockets हैं। आप दो परिभाषित करेंगे:
web,:80पर औरwebsecure,:443पर। - Routers एक request (
Host(...)) का मिलान करते हैं और उसे एक service से जोड़ते हैं। Certificates का अनुरोध प्रति router,tls.certresolverके माध्यम से किया जाता है। - Services backend हैं, यानी एक container और वह port जिस पर वह Docker network के अंदर listen करता है।
- Middlewares router और service के बीच स्थित होते हैं: basic auth, IP allow-lists, header rewrites, redirects।
ये चार संज्ञाएं Traefik द्वारा उन कार्यों के लिए दिए गए नाम हैं जिन्हें आप अन्यथा मैन्युअल रूप से लिखते: router एक server_name है, service एक proxy_pass target है, और middlewares वे header और auth directives हैं जिन्हें आप तब सेट करते हैं जब आप nginx reverse proxy server block को पंक्ति-दर-पंक्ति बनाते हैं।
Static configuration (entrypoints, providers, ACME) को Traefik की command line पर या traefik.yml में पास किया जाता है, और इसे बदलने का अर्थ है Traefik को restart करना। Dynamic configuration (routers, services, middlewares) container labels से प्राप्त होती है और hot-reload होती है। इन दोनों के बीच भ्रम होना ही "मेरी flag काम नहीं कर रही है" का सामान्य कारण है।
Compose file
एक साझा Docker network जिसका नाम proxy है, इसकी रीढ़ है। Traefik किसी container तक केवल तभी पहुँच सकता है जब दोनों उस पर मौजूद हों।
name: edge
networks:
proxy:
name: proxy
services:
traefik:
image: traefik:v3.5
restart: unless-stopped
command:
- --providers.docker=true
- --providers.docker.exposedByDefault=false
- --providers.docker.network=proxy
- --entryPoints.web.address=:80
- --entryPoints.websecure.address=:443
- --entryPoints.web.http.redirections.entryPoint.to=websecure
- --entryPoints.web.http.redirections.entryPoint.scheme=https
- --certificatesresolvers.le.acme.email=you@example.com
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.le.acme.tlschallenge=true
# while you iterate, point at staging so a mistake costs nothing:
# - --certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
- --api.dashboard=true
- --log.level=INFO
- --accesslog=true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)
- traefik.http.routers.dashboard.entrypoints=websecure
- traefik.http.routers.dashboard.tls.certresolver=le
- traefik.http.routers.dashboard.service=api@internal
- traefik.http.routers.dashboard.middlewares=dashboard-auth
- traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$REPLACE$$THIS
gitea:
image: gitea/gitea:1 # major-only pin keeps this demo copy-pasteable; pin an exact release in production
restart: unless-stopped
volumes:
- ./gitea:/data
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.gitea.rule=Host(`git.example.com`)
- traefik.http.routers.gitea.entrypoints=websecure
- traefik.http.routers.gitea.tls.certresolver=le
- traefik.http.services.gitea.loadbalancer.server.port=3000पहले docker compose up -d, फिर docker compose logs -f traefik। प्रत्येक अतिरिक्त app, gitea ब्लॉक की एक प्रति है जिसमें अपना router name, अपना Host() और अपना internal port होता है। एक TLS और backups के साथ Docker में Nextcloud install भी इसी तरह काम करता है; इसके published ports हटा दें, इसे proxy से जोड़ें, और router labels को hostname तथा certificate संभालने दें।
वहाँ मौजूद पाँच विवरण बहुत महत्वपूर्ण हैं।
exposedByDefault=false एक container को Traefik के लिए तब तक अदृश्य रखता है जब तक कि उसमें traefik.enable=true न हो। यदि आप इसे छोड़ देते हैं, तो आपके द्वारा शुरू किया गया हर container, जिसमें कुछ जाँचने के लिए चलाया गया अस्थायी postgres भी शामिल है, उसके लिए एक route generate हो जाएगा।
providers.docker.network=proxy Traefik को बताता है कि जब कोई container कई networks से जुड़ा हो, तो किस network का उपयोग करना है। यदि आप इसे छोड़ देते हैं, तो Traefik गलत container IP चुन सकता है, जो 502 error के रूप में सामने आता है और application fault जैसा दिखता है।
loadbalancer.server.port=3000 container के अंदर का port है; Gitea वहाँ 3000 पर listen करता है। ध्यान दें कि कोई भी app container port publish नहीं करता है, केवल Traefik ऐसा करता है।
web entrypoint पर redirect, plaintext requests को HTTPS पर 308 के रूप में बदल देता है। Port 80 वैसे भी खुला रहता है: ACME HTTP challenge को इसकी आवश्यकता होती है, और उन लोगों को भी जो सीधे hostname टाइप करते हैं।
basic-auth hash में दोहराया गया $$, Compose का escaping है, न कि कोई typo। इसे htpasswd -nbB admin 'your-password' (package apache2-utils) के साथ generate करें, फिर हर $ को double कर दें।
Certificate और acme.json का जाल
tlschallenge=true TLS-ALPN-01 को चुनता है: Let's Encrypt आपके सर्वर से 443 पोर्ट पर जुड़ता है और Traefik TLS हैंडशेक के भीतर ही challenge का उत्तर देता है। इसका विकल्प HTTP-01 है, जो पोर्ट 80 पर काम करता है। Traefik की command: सूची में tlschallenge लाइन को इन दो लाइनों से बदलें:
- --certificatesresolvers.le.acme.httpchallenge=true
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=webदोनों ही तरीके काम करते हैं। दोनों के लिए यह आवश्यक है कि hostname का पब्लिक DNS पहले से ही आपके VPS पर पॉइंट कर रहा हो, ताकि certificate authority नाम को resolve कर सके और बाहर से कनेक्ट हो सके। पहले A (और AAAA) रिकॉर्ड बनाएँ, dig +short git.example.com के साथ पुष्टि करें, और फिर Traefik शुरू करें।
अब वह जाल जो लोगों का काफी समय बर्बाद करता है। Traefik अपनी ACME अकाउंट की (key) और जारी किए गए सभी सर्टिफिकेट्स को एक acme.json में रखता है। यदि वह फाइल group-readable या world-readable है, तो Traefik इस तरह की एक लाइन प्रिंट करेगा और रुक जाएगा:
error: unable to get ACME account: permissions 644 for /letsencrypt/acme.json are too open, please use 600इसका सही समाधान ऊपर दिया गया है: directory को bind-mount करें और Traefik को सही मोड के साथ फाइल खुद बनाने दें। यदि आपने touch का उपयोग करके acme.json बनाया है, तो आपके umask ने इसे 644 बना दिया होगा। इसे होस्ट पर ठीक करें:
chmod 600 ./letsencrypt/acme.json
docker compose restart traefikउस directory का बैकअप अपने ऐप वॉल्यूम के साथ लें। इसे खोना ठीक किया जा सकता है, सर्टिफिकेट दोबारा जारी हो जाते हैं, लेकिन एक साथ पांच hostnames के लिए दोबारा जारी करने पर आप rate limits में फंस सकते हैं।
जब आप प्रयोग कर रहे हों तो staging CA का उपयोग करें। caserver लाइन से कमेंट हटाएँ, हर रूट को काम करने दें, फिर उसे वापस कमेंट कर दें और acme.json को डिलीट कर दें ताकि प्रोडक्शन सर्टिफिकेट नए सिरे से मांगे जाएं। प्रोडक्शन Let's Encrypt एक ही hostnames के सेट के लिए प्रति सप्ताह पांच डुप्लिकेट सर्टिफिकेट की अनुमति देता है, और एक ही नाम के लिए बार-बार विफल validations को सीमित करता है। Staging अविश्वसनीय (untrusted) सर्टिफिकेट जारी करता है, आपका ब्राउज़र चेतावनी देता है, और वह चेतावनी ही इस बात का संकेत है कि यह काम कर गया, साथ ही इसमें सीमाएं भी बहुत कम सख्त हैं।
Dashboard एक कंट्रोल सरफेस है, न कि कोई डेमो
अधिकांश quickstarts --api.insecure=true सेट करते हैं, जो बिना किसी authentication के port 8080 पर dashboard को serve करता है। एक public IP वाले सर्वर पर यह आपकी routing topology, hostnames, middleware names और backend ports को किसी भी ऐसे व्यक्ति को सौंप देता है जो इसे स्कैन करता है।
ऊपर दिए गए traefik service पर मौजूद labels इसका विकल्प हैं: dashboard को किसी अन्य app की तरह, एक वास्तविक hostname पर, TLS के माध्यम से, basicauth के पीछे route किया जाता है। service=api@internal वह है जो router को Traefik के built-in API से जोड़ता है। इसे और अधिक सुरक्षित बनाने के लिए एक IP allow-list को chain करें, जिसे बाएं से दाएं लागू किया जाता है। यदि आपका office address dynamic है, तो range को उसी VPS पर self-host किए गए WireGuard VPN द्वारा दिए गए subnet पर सेट करें और dashboard तक केवल tunnel के माध्यम से पहुँचें:
- traefik.http.middlewares.office.ipallowlist.sourcerange=10.0.0.7/32
- traefik.http.routers.dashboard.middlewares=office,dashboard-authएक साझा basicauth पासवर्ड तब तक काम का नहीं रहता जब तक कि पांच apps को अपने स्वयं के accounts की आवश्यकता न हो, और वही middleware slot एक forwardauth लेता है जो निर्णय को Authentik, एक self-hosted single sign-on server को सौंप देता है, ताकि dashboard और उसके बगल का प्रत्येक route एक ऐसे login के पीछे हो जिसे आप एक ही स्थान पर revoke कर सकें।
Docker socket root है
/var/run/docker.sock एक API है जो ऐसा container बना सकता है जो host से / को mount करता है। इस तक पहुँच का मतलब मशीन पर root access होना है, और Traefik को labels पढ़ने के लिए इसकी आवश्यकता होती है।
mount पर :ro बनाए रखें, लेकिन यह स्पष्ट रहें कि इससे क्या लाभ मिलता है: यह socket file को केवल-पठनीय (read-only) बनाता है। यह Docker API पर आने वाले POST अनुरोधों को नहीं रोकता है। वास्तविक सुरक्षा उपाय यह है कि Traefik को कभी भी socket न दें, और बीच में एक filtering proxy लगाएँ:
dockerproxy:
image: tecnativa/docker-socket-proxy # pin the current tag
restart: unless-stopped
environment:
CONTAINERS: 1
NETWORKS: 1
POST: 0
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxyTraefik से socket volume हटा दें और provider को proxy की ओर निर्देशित करें:
--providers.docker.endpoint=tcp://dockerproxy:2375Traefik के पास containers और networks का read access बना रहता है, और वह कुछ भी नया बनाने की क्षमता खो देता है।
Firewall, ports, और वह नियम जिसे हर कोई गलत समझता है
दो ports खुले हैं, साथ में SSH:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableDocker के published ports ufw को bypass कर देते हैं। Docker अपने स्वयं के iptables rules डालता है, जिनका मूल्यांकन ufw की chains से पहले किया जाता है। इसलिए, ports: ["3000:3000"] के साथ शुरू किया गया container इंटरनेट से तब भी पहुँच योग्य होता है, भले ही ufw में deny rule लगा हो। इसका बचाव संरचनात्मक है, न कि firewall configuration: केवल Traefik से ports publish करें, और बाकी हर container को networks: [proxy] दें, इसके अलावा कुछ नहीं। यदि किसी चीज़ को वास्तव में host तक पहुँचना ही है, तो उसे loopback, "127.0.0.1:3000:3000" पर bind करें।
समस्या निवारण: वे त्रुटियाँ जो आपको वास्तव में दिखाई देंगी
404 page not found, जिसे Traefik द्वारा सर्व किया गया है। कोई भी router मेल नहीं खाया। संभावना के क्रम में: container में traefik.enable=true की कमी है (जिसमें exposedByDefault=false सेट है); Host() नियम आपके द्वारा टाइप किए गए नाम से मेल नहीं खाता है; एक label में router का नाम दूसरे से अलग है (routers.gitea.rule और routers.gitea.entrypoints एक ही शब्द होने चाहिए); या आपने hostname को backticks के बजाय quotes में रखा है। Traefik v3 के लिए matchers के अंदर backticks की आवश्यकता होती है।
502 Bad Gateway. एक router मेल खा गया और backend तक नहीं पहुँचा जा सका। लगभग हमेशा इसका कारण यह होता है कि container proxy नेटवर्क पर नहीं है, docker inspect -f '{{json .NetworkSettings.Networks}}' gitea की जाँच करें। दूसरा संभावित कारण गलत loadbalancer.server.port है: आपने इसे एक published port दिया है, या app कहीं और listen कर रहा है। Log में प्रयास का नाम दिया गया है: dial tcp 172.18.0.5:8080: connect: connection refused।
Browser चेतावनी देता है, और certificate TRAEFIK DEFAULT CERT के नाम पर जारी किया गया है। उस hostname के लिए कोई certificate मौजूद नहीं है और Traefik ने अपना self-signed placeholder सर्व किया है। ACME लाइनों को पढ़ें:
unable to obtain ACME certificate for domains "git.example.com" ...
acme: error: 400 ... DNS problem: NXDOMAIN looking up A for git.example.comDNS अभी तक बॉक्स की ओर पॉइंट नहीं कर रहा है। record को ठीक करें, TTL समाप्त होने तक प्रतीक्षा करें, और Traefik को restart करें।
HTTP challenge पर Invalid response from http://git.example.com/.well-known/acme-challenge/...: port 80 बाहर से Traefik तक नहीं पहुँच रहा है, आमतौर पर यह ufw नहीं, बल्कि VPS के सामने provider-level का firewall होता है।
Certificates कभी जारी नहीं होते, और आपका DNS Cloudflare पर है और orange cloud चालू है। Cloudflare अपने edge पर TLS को terminate करता है और TLS-ALPN-01 इसके माध्यम से पूरा नहीं हो सकता है। जारी करते समय record को DNS-only पर सेट करें, या API token के साथ DNS-01 challenge पर स्विच करें। DNS-01 एकमात्र challenge है जो wildcards जारी करता है।
Redirect loop. Traefik के सामने किसी चीज़ ने पहले ही TLS को terminate कर दिया है और plaintext को :80 पर forward कर दिया है; entrypoint redirect इसे वापस HTTPS पर भेज देता है। दो redirects में से एक को हटा दें।
इसे चालू रखना
Docker unit का boot-enabled होना आवश्यक है (systemctl is-enabled docker), और restart: unless-stopped reboot के बाद stack को वापस ले आता है। स्पष्ट नियंत्रण के लिए, RemainAfterExit=yes के साथ docker compose -f /srv/edge/compose.yml up -d चलाने वाली एक छोटी systemd unit आपको systemctl status edge और ordering का नियंत्रण देती है।
Traefik tag को पिन करें (traefik:v3.5, कभी भी latest का उपयोग न करें)। v2-to-v3 upgrade ने rule syntax और provider names को बदल दिया है, और एक unattended latest खुशी-खुशी ऐसी config को reload कर देगा जिसे वह अब समझ नहीं पाता है। जानबूझकर upgrade करें: migration notes पढ़ें, tag को bump करें, docker compose up -d traefik चलाएं, और log पर नज़र रखें। यदि आप अभी भी v2 tag पर हैं, तो Traefik v2 से v3 migration guide हर rename, compatibility mode, और एक ऐसे rollback के बारे में बताता है जो आपके certificates को सुरक्षित रखता है।
./letsencrypt और प्रत्येक app के data volume का backup लें। Traefik में ऐसी कोई अन्य state नहीं होती जिसे आप compose file से फिर से न बना सकें।
स्केल करने पर क्या समस्याएं आती हैं
पहली सीमा throughput नहीं, बल्कि एक सिंगल सर्वर है: एक VPS पर एक Traefik पांच apps के लिए single point of failure है, और acme.json flat-file storage है, जिसे दो Traefik instances द्वारा एक साथ लिखने पर वह corrupt हो जाएगा। स्केल करने का अर्थ है certificate storage को फाइल से बाहर ले जाना, या TLS termination कहीं और करना।
दूसरी समस्या long-lived connections की है। Server-sent events, बड़ी uploads और slow clients entrypoint के response timeouts का सामना करते हैं; --entryPoints.websecure.transport.respondingTimeouts.readTimeout और इसके writeTimeout तथा idleTimeout भाई-बहन इसके लिए मुख्य knobs हैं। WebSockets बिना किसी अतिरिक्त configuration के काम करते हैं।
तीसरी समस्या डिस्क की है। --accesslog=true stdout पर लिखता है, और Docker का json-file driver इसे तब तक सुरक्षित रखता है जब तक कि इसकी सीमा तय न की जाए। Traefik service पर logging.options.max-size सेट करें, या access log को एक फाइल में लिखें और उसे rotate करें।
इसके लिए किसी orchestrator की आवश्यकता नहीं है। आपको बस एक ऐसे सर्वर की जरूरत है जिसे आप नियंत्रित करते हों, जिसका एक वास्तविक IP हो और जिस पर 80 तथा 443 ports दुनिया के लिए खुले हों; एक छोटा सा VPS ही पूरी dependency list है।
FAQ
क्या Traefik चलाने पर भी मुझे certbot की आवश्यकता है?
नहीं। Traefik का ACME resolver हर उस hostname के लिए certificate का अनुरोध करता है और उसे renew करता है जिसे वह route करता है, और इन सबको acme.json में स्टोर करता है। जब nginx या कोई अन्य सर्वर स्वयं TLS terminate करता है, तब Certbot ही सही टूल रहता है; एक ही hostname के लिए दोनों को चलाने से केवल Let's Encrypt की rate limits समाप्त होती हैं।
मेरा container Traefik के माध्यम से 404 क्यों लौटा रहा है?
Traefik द्वारा 404 का मतलब है कि request के लिए कोई भी router match नहीं हुआ। जाँचें कि container में traefik.enable=true मौजूद है (एक बार exposedByDefault=false सेट हो जाने पर यह अनिवार्य है), कि Host() का मान आपके द्वारा टाइप किए गए नाम से मेल खाता है, और यह कि उस app के लिए सभी labels में router का नाम एक समान है। Traefik v3 में matcher के अंदर quotes के बजाय backticks की आवश्यकता होती है।
यहाँ 404 और 502 के बीच क्या अंतर है?
404 का मतलब है कि routing कभी हुई ही नहीं; 502 का मतलब है कि एक router match हुआ और backend ने connection को अस्वीकार कर दिया। 502 के सामान्य कारण हैं: container का proxy network से न जुड़ा होना, और loadbalancer.server.port का उस port के बजाय किसी published port की ओर इशारा करना जिस पर app container के अंदर listen कर रही है। access log उस सटीक address को दर्शाता है जिसे Traefik ने dial किया था।
क्या Docker socket को read-only mount करना पर्याप्त है?
:ro flag socket file को read-only बनाता है, न कि उसके पीछे की API को। POST अनुरोध अभी भी इसके माध्यम से यात्रा करते हैं, और Docker API का access host पर root के बराबर होता है। अधिक सुरक्षित व्यवस्था ऊपर दिखाया गया docker-socket-proxy container है, जो Traefik के लिए केवल container और network reads को expose करता है और writes को पूरी तरह से block करता है।
क्या Traefik wildcard certificate जारी कर सकता है?
केवल DNS-01 challenge के माध्यम से, आपके DNS provider के लिए एक API token के साथ। TLS-ALPN-01 और HTTP-01 प्रत्येक एक single hostname को validate करते हैं और wildcard उत्पन्न नहीं कर सकते। जब Cloudflare जैसा कोई CDN आपके VPS के सामने TLS terminate करता है और अन्य दो challenges पूरे नहीं हो पाते, तब भी DNS-01 ही समाधान है।