[{"data":1,"prerenderedAt":212},["ShallowReactive",2],{"seo-verification":3,"blog-deploy-your-applications-with-traefik-on-a-vps-ma-en":6,"promo-bar-likely":211},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-deploy-your-applications-with-traefik-on-a-vps-ma-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"noindex":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":26,"featuredImage":28,"bgImage":29,"posterImage":30,"relatedSolution":28,"intro":31,"sections":32,"ctaTitle":154,"ctaBody":155,"ctaButton":156,"ctaUrl":157,"relatedPosts":158},38,"deploy-your-applications-with-traefik-on-a-vps",{"fr":12,"en":10,"ar":13,"es":14},"deployer-avec-traefik","انشر-تطبيقاتك-باستخدام-traefik-على-خادم-vps","desplegar-con-traefik","Deploy your applications with Traefik on a VPS","Deploy Traefik as a reverse proxy on your VPS: automatic routing of Docker containers, Let's Encrypt SSL certificates and load balancing.",11,3,false,"2026-05-13T00:00:00+00:00","2026-11-08T01:13:13+00:00",{"id":18,"name":23,"slug":24,"color":25,"icon":24},"Deployment","deploiement","bg-success\u002F10 text-success",[27],{"id":18,"name":23,"slug":24,"color":25,"icon":24},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fdeployer-avec-traefik-poster.svg","Traefik is a cloud-native reverse proxy and load balancer that discovers your Docker containers automatically and assigns them routes and SSL certificates without manual reloading. Deployed on your VPS, it becomes the single entry point for all your applications, with a declarative configuration through Docker labels. This guide installs the v3 branch (v3.7 at the time of writing), with Let's Encrypt HTTPS, a protected dashboard, HTTP\u002F3, production middlewares and the pitfalls to know if you are coming from Traefik v2.",[33,37,49,52,77,80,88,91,94,97,106,140,143,151],{"type":34,"title":35,"body":36},"h2","Why put Traefik in front of your VPS?","When you run several containerized applications on the same VPS, manually managing Nginx virtual hosts and renewing each certificate quickly becomes unmanageable. Traefik solves this problem by reading your Docker containers' labels directly: you declare a route at `docker run` time or in your `docker-compose.yml`, and Traefik configures it on the fly, generates the Let's Encrypt certificate and exposes it over HTTPS. No config file to edit, no reload: everything is dynamic.\n\nNginx remains an excellent HTTP server, but its configuration is static: each new service needs a vhost, an `nginx -s reload` and a separate ACME tool (certbot and its renewal job). Traefik was designed for a topology that keeps changing, which is exactly the case of a VPS where containers are added, updated and removed every week. The v3 branch, released in April 2024, stabilized HTTP\u002F3 and OpenTelemetry trace export, and tightened the routing rule syntax.",{"type":38,"title":39,"items":40},"ul","What Traefik brings concretely on a VPS",[41,42,43,44,45,46,47,48],"Automatic discovery of Docker containers via the Docker provider (routes appear without a restart)","Let's Encrypt SSL certificates generated and renewed automatically (HTTP-01, TLS-ALPN-01 or DNS-01 challenges), renewed by default 30 days before they expire","Routing by domain, by path or by header, managed entirely through Docker labels","Built-in web dashboard to visualize routers, services and middlewares in real time","Ready-to-use middlewares: HTTPS redirection, basic auth, rate limiting, security headers, compression","Automatic load balancing when you scale a service to several replicas","HTTP\u002F3 (QUIC) enabled on an entrypoint with a single option","Providers other than Docker (Swarm, Kubernetes, Nomad, files) if your infrastructure grows",{"type":34,"title":50,"body":51},"Prerequisites","Traefik itself is very lightweight: 1 vCPU and 1 GB of RAM are amply enough for the proxy alone, the overhead coming from your applications behind it. Plan for a VPS running Debian 12 or Ubuntu 22.04\u002F24.04 with Docker Engine and the Docker Compose v2 plugin installed (the `docker compose` command, without a hyphen), a domain name whose A\u002FAAAA records point to the VPS IP **before** the first start (otherwise Let's Encrypt cannot validate the domain), and ports 80 and 443 open for inbound TCP in your firewall (UFW), plus 443 over UDP if you enable HTTP\u002F3. Keep a valid email address for the ACME account. For wildcard certificates, you will also need API access to your DNS provider.",{"type":53,"title":54,"steps":55},"steps","Deploy Traefik with Docker Compose",[56,59,62,65,68,71,74],{"title":57,"body":58},"Create the network and the directory structure","Create a shared Docker network that Traefik and your applications will use, then a dedicated folder containing the `acme.json` file (the storage for certificates and their private keys):\n\n```bash\ndocker network create proxy\nmkdir -p \u002Fopt\u002Ftraefik && cd \u002Fopt\u002Ftraefik\ntouch acme.json && chmod 600 acme.json\n```\n\nCreate the file **before** the first start: if the mounted path does not exist, Docker creates a directory in its place and Traefik can no longer write to it. The 600 permissions are mandatory: Traefik refuses a file readable by other users.",{"title":60,"body":61},"Configure the entrypoints and the ACME resolver","Place this file in `\u002Fopt\u002Ftraefik\u002Fdocker-compose.yml`. It declares two entrypoints (`web` on :80, `websecure` on :443), a Let's Encrypt certificateResolver validated through the HTTP challenge, container discovery through the Docker socket mounted read-only, and the dashboard:\n\n```yaml\nservices:\n  traefik:\n    image: traefik:v3.7\n    container_name: traefik\n    restart: unless-stopped\n    ports:\n      - \"80:80\"\n      - \"443:443\"\n      - \"443:443\u002Fudp\"\n    command:\n      - \"--providers.docker=true\"\n      - \"--providers.docker.exposedbydefault=false\"\n      - \"--providers.docker.network=proxy\"\n      - \"--entrypoints.web.address=:80\"\n      - \"--entrypoints.web.http.redirections.entrypoint.to=websecure\"\n      - \"--entrypoints.web.http.redirections.entrypoint.scheme=https\"\n      - \"--entrypoints.websecure.address=:443\"\n      - \"--entrypoints.websecure.http3=true\"\n      - \"--certificatesresolvers.letsencrypt.acme.email=admin@example.com\"\n      - \"--certificatesresolvers.letsencrypt.acme.storage=\u002Facme.json\"\n      - \"--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web\"\n      - \"--api.dashboard=true\"\n    volumes:\n      - \"\u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock:ro\"\n      - \".\u002Facme.json:\u002Facme.json\"\n    networks:\n      - proxy\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)\"\n      - \"traefik.http.routers.dashboard.entrypoints=websecure\"\n      - \"traefik.http.routers.dashboard.tls.certresolver=letsencrypt\"\n      - \"traefik.http.routers.dashboard.service=api@internal\"\n      - \"traefik.http.routers.dashboard.middlewares=dashboard-auth\"\n      - \"traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$05$$REMPLACER_PAR_LE_HASH\"\n\nnetworks:\n  proxy:\n    external: true\n```\n\nThe `exposedbydefault=false` option is essential: without it, every container on the VPS would be published, including your database. Pin the branch (`v3.7`) rather than `latest`, and follow the releases before moving to a new minor version.",{"title":63,"body":64},"Force the HTTP to HTTPS redirection","The redirection is declared directly on the `web` entrypoint, with the two options `--entrypoints.web.http.redirections.entrypoint.to=websecure` and `...entrypoint.scheme=https` already present in the file above. It is simpler and safer than a catch-all router with a middleware: all cleartext traffic is switched to HTTPS by a permanent redirect, with no label to repeat on each service, and the Let's Encrypt HTTP challenge still goes through on port 80.",{"title":66,"body":67},"Secure the dashboard","The dashboard is exposed on `traefik.example.com`, behind the `basicauth` middleware named `dashboard-auth`. Generate a bcrypt hash (the `apache2-utils` package on Debian\u002FUbuntu) while doubling the `$` signs, as a label value in a Compose file requires:\n\n```bash\napt install -y apache2-utils\nhtpasswd -nB admin | sed -e 's\u002F\\$\u002F\\$\\$\u002Fg'\n```\n\nPaste the result in place of `admin:$$2y$$05$$REMPLACER_PAR_LE_HASH`. A hash pasted with single `$` signs is read by Compose as variables and breaks authentication. Never leave the `--api.insecure=true` flag in production: it publishes the dashboard without authentication.",{"title":69,"body":70},"Launch Traefik","Start the stack and follow the logs:\n\n```bash\ncd \u002Fopt\u002Ftraefik\ndocker compose up -d\ndocker compose logs -f traefik\n```\n\nYou should see the Docker provider start, then the `dashboard` router attach, with no ACME error. During your tests, temporarily add `--certificatesresolvers.letsencrypt.acme.caserver=https:\u002F\u002Facme-staging-v02.api.letsencrypt.org\u002Fdirectory`: the Let's Encrypt staging server issues certificates that browsers do not trust, but it protects you from the production rate limit if a configuration keeps failing. Remove the option and empty `acme.json` before going to production.",{"title":72,"body":73},"Connect an application","Each application lives in its own `docker-compose.yml`, attached to the `proxy` network and described by its labels:\n\n```yaml\nservices:\n  app:\n    image: ghcr.io\u002Fexample\u002Fapp:1.0.0\n    restart: unless-stopped\n    networks:\n      - proxy\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.app.rule=Host(`app.example.com`)\"\n      - \"traefik.http.routers.app.entrypoints=websecure\"\n      - \"traefik.http.routers.app.tls.certresolver=letsencrypt\"\n      - \"traefik.http.services.app.loadbalancer.server.port=3000\"\n\nnetworks:\n  proxy:\n    external: true\n```\n\nOn `docker compose up -d`, Traefik detects the container, obtains the certificate and activates the route within seconds, without touching its own configuration. The `loadbalancer.server.port` label gives the application's internal port; it becomes mandatory as soon as the image exposes several. The router name (`app` here) must be unique across the whole VPS.",{"title":75,"body":76},"Check the certificate and the redirection","Check from a machine outside the VPS:\n\n```bash\ncurl -I http:\u002F\u002Fapp.example.com\ncurl -I https:\u002F\u002Fapp.example.com\ndig +short app.example.com\n```\n\nThe first request must return a permanent redirect to `https:\u002F\u002F`, the second a `200` (or your application's code) served with a valid Let's Encrypt certificate, and `dig` must show the VPS IP. The dashboard also lists each router with its certificate resolver. A port 80 that refuses the connection almost always points to the firewall.",{"type":34,"title":78,"body":79},"Useful middlewares in production","A Traefik middleware is declared by label, once, then attached to one or several routers with `traefik.http.routers.\u003Crouter>.middlewares=\u003Cname1>,\u003Cname2>`. You therefore define them on the service concerned, without touching Traefik's central configuration. Here are the ones that serve on almost every VPS.",{"type":38,"title":81,"items":82},"Five middlewares to know",[83,84,85,86,87],"**BasicAuth on an admin tool** (Adminer, Flower…): `traefik.http.middlewares.adminauth.basicauth.users=admin:$$2y$$05$$...`, then `traefik.http.routers.myadmin.middlewares=adminauth`. Same doubled-`$` rule as for the dashboard.","**Rate limit** in front of an API or a form: `traefik.http.middlewares.ratelimit.ratelimit.average=100`, `...ratelimit.period=1s` and `...ratelimit.burst=200` allow 100 requests per second on average, with bursts up to 200. Without `average`, the limit is disabled.","**Security headers**: the `headers` middleware sets HSTS (`stsSeconds=31536000`, `stsIncludeSubdomains=true`), `X-Frame-Options: DENY` (`frameDeny=true`) and `X-Content-Type-Options: nosniff` (`contentTypeNosniff=true`), with no separate configuration file.","**Redirect www to the bare domain**: `redirectregex` with `regex=^https:\u002F\u002Fwww\\.(.+)`, `replacement=https:\u002F\u002F$${1}` and `permanent=true`. Remember to declare both hosts in the router rule so that the certificate covers `www`.","**Compression**: `traefik.http.middlewares.compress.compress=true` enables gzip, Brotli and Zstandard depending on what the browser accepts, with no setting on the application side.",{"type":89,"body":90},"tip","To obtain a wildcard certificate (`*.example.com`) and avoid exposing each subdomain, switch from the `httpChallenge` to the `dnsChallenge`: it is the only challenge that issues a wildcard. With Cloudflare, declare `--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare` and pass Traefik the `CF_DNS_API_TOKEN` variable, an API token restricted to the Zone \u002F DNS \u002F Edit and Zone \u002F Zone \u002F Read permissions. The older `CF_API_EMAIL` + `CF_API_KEY` pair also works, but it requires the global API key, which opens the whole account: prefer the token. Traefik then proves ownership of the domain through a TXT record, which works even for services not publicly exposed.",{"type":34,"title":92,"body":93},"Enable HTTP\u002F3 (QUIC)","The Compose file above already enables it: `--entrypoints.websecure.http3=true` on the TLS entrypoint, and the publication of port `443:443\u002Fudp`. Traefik then listens over UDP on the same port as the TCP entrypoint; a connection always starts over TCP, then the browser switches to QUIC on the next round. Two conditions: only TLS routers are served over HTTP\u002F3, and the firewall must let port 443 through over UDP (`ufw allow 443\u002Fudp`). If you publish a UDP port other than the entrypoint's, the `http3.advertisedPort` option announces the right one. To check, use a `curl` built with HTTP\u002F3: `curl --http3 -I https:\u002F\u002Fapp.example.com`.",{"type":34,"title":95,"body":96},"Coming from Traefik v2: what breaks","v3 keeps the v2 routing configuration compatible, but it changes the default rule syntax and refuses to start on several removed options. To migrate without rewriting everything at once, add `--core.defaultRuleSyntax=v2` to the install configuration: your old rules keep working, you move routers one by one to the v3 syntax, then you remove the option. Traefik's logs flag every deprecated option still in use: read them after the first start. The changes that catch people most often:",{"type":38,"title":98,"items":99},"v3 breaking changes",[100,101,102,103,104,105],"**Swarm**: the Docker provider's `swarmMode` option no longer exists and blocks startup. Docker Swarm now has its own provider (`--providers.swarm`).","**Routing rules**: `Headers` and `HeadersRegexp` become `Header` and `HeaderRegexp`, `HostHeader` disappears in favor of `Host`, and most matchers now accept a single value.","**Paths**: `Path` and `PathPrefix` no longer read a regular expression or a placeholder such as `{id}`; such a route no longer matches anything. Switch to `PathRegexp`, which follows Go regexp syntax, like `HostRegexp`.","**Renamed or trimmed middlewares**: `IPWhiteList` becomes `IPAllowList` (same options); the `headers` middleware loses `sslRedirect`, `sslTemporaryRedirect`, `sslHost`, `sslForceHost` and `featurePolicy`.","**Content-Type**: Traefik no longer guesses the type of a response the backend did not type; add the `ContentType` middleware if an application relies on it.","**Removed providers and observability**: Marathon, Rancher v1, Traefik Pilot and InfluxDB v1 metrics prevent startup; tracing goes through OpenTelemetry only (no more direct export to Jaeger, Zipkin or Datadog).",{"type":107,"title":108,"headers":109,"rows":114},"comparison","Traefik v3, Nginx or Caddy in front of your containers",[110,111,112,113],"Criterion","Traefik v3","Nginx","Caddy 2",[115,120,125,130,135],[116,117,118,119],"Configuration","Docker labels, YAML or TOML, applied on the fly","Static vhost files, `nginx -s reload` on every change","Caddyfile or JSON API, reload without downtime",[121,122,123,124],"Automatic HTTPS","Built in (ACME: HTTP-01, TLS-ALPN-01, DNS-01)","External tool (certbot) and a renewal job","Built in and on by default",[126,127,128,129],"Container discovery","Native, through the Docker socket","None: third-party tool or manual editing","Through the community caddy-docker-proxy module",[131,132,133,134],"HTTP\u002F3","Stable since v3.0 (April 2024), one option per entrypoint","Experimental since nginx 1.25.0 (May 2023), dedicated configuration","On by default since Caddy 2.6 (September 2022)",[136,137,138,139],"Learning curve","Moderate: routers, services and middlewares to learn","Known by everyone, but HTTPS + Docker need tooling","The fastest for a few sites",{"type":34,"title":141,"body":142},"Troubleshooting: the most frequent errors","Most Traefik incidents come from four sources: DNS or the firewall preventing Let's Encrypt validation, the `acme.json` file, a missing Docker network and badly written labels. The symptoms and their fixes:",{"type":38,"title":144,"items":145},"Symptoms and fixes",[146,147,148,149,150],"**Certificate not obtained, ACME `unauthorized` or `403` error in the logs**: Let's Encrypt cannot reach your domain. Check from the outside that port 80 answers (`curl -I http:\u002F\u002Fapp.example.com`) and that DNS points to the VPS (`dig +short app.example.com`). Behind a CDN in proxy mode, prefer the `dnsChallenge`.","**`permissions 644 for \u002Facme.json are too open, please use 600`**: the resolver refuses the file. Fix it with `chmod 600 \u002Fopt\u002Ftraefik\u002Facme.json` then restart Traefik; this check protects the private keys stored inside.","**Route visible in the dashboard but `502 Bad Gateway`**: the container is not on the `proxy` network, or the internal port is wrong. Check with `docker inspect \u003Ccontainer>` (`Networks` section), add the network or the `loadbalancer.server.port` label, then recreate the container. On a container attached to several networks, the `traefik.docker.network=proxy` label removes the ambiguity.","**Labels ignored, route missing from the dashboard**: `traefik.enable=true` is missing (mandatory with `exposedbydefault=false`), or two services declare the same router name. Traefik's logs then report the conflict.","**BasicAuth refusing a correct password**: the `$` signs of the hash are not doubled in the `docker-compose.yml`. In a Compose file, each `$` becomes `$$`; in a YAML or TOML dynamic configuration file, the hash is pasted as is.",{"type":34,"title":152,"body":153},"Next step: harden the stack","Traefik handles routing and TLS, but a production-ready stack needs more: healthchecks, a restart policy, log rotation, secrets kept out of the Compose file, volume backups (including `acme.json`). Our \u003Ca href=\"\u002Fen\u002Fblog\u002Fdocker-compose-in-production-10-point-checklist\">Docker Compose production checklist\u003C\u002Fa> covers these points one by one. And if you are still hesitating over the tool, the \u003Ca href=\"\u002Fen\u002Fblog\u002Fcaddy-traefik-or-nginx-proxy-manager-which-to-choose\">Caddy, Traefik or Nginx Proxy Manager comparison\u003C\u002Fa> helps you decide based on the number of services and the need for automatic discovery.","Launch Traefik in minutes on a ServOrbit Cloud VPS","Order a Cloud VPS with Docker preinstalled and deploy your Traefik stack from the Deployment template. Your applications are routed over HTTPS automatically.","See the Cloud VPS","\u002Fvps-cloud",[159,179,195],{"id":160,"slug":161,"slugs":162,"title":166,"excerpt":167,"readTime":168,"views":169,"isPinned":19,"noindex":19,"publishedAt":170,"updatedAt":171,"category":172,"categories":173,"featuredImage":28,"bgImage":29,"posterImage":175,"relatedSolution":176},33,"deploy-your-applications-with-caprover-on-a-vps",{"fr":163,"en":161,"ar":164,"es":165},"deployer-avec-caprover","انشر-تطبيقاتك-باستخدام-caprover-على-خادم-vps","desplegar-aplicaciones-con-caprover-en-un-vps","Deploy your applications with CapRover on a VPS","Complete CapRover guide: installation, captain-definition, GitHub Actions CI\u002FCD, multi-node cluster, backups and troubleshooting 502, build failed and Docker API errors.",10,2,"2026-05-18T00:00:00+00:00","2026-11-04T16:30:25+00:00",{"id":18,"name":23,"slug":24,"color":25,"icon":24},[174],{"id":18,"name":23,"slug":24,"color":25,"icon":24},"\u002Fblog\u002Fcovers\u002Fdeployer-avec-caprover-poster.svg",{"categorySlug":177,"appSlug":178},"application-deployment-devops","caprover",{"id":180,"slug":181,"slugs":182,"title":186,"excerpt":187,"readTime":17,"views":188,"isPinned":19,"noindex":19,"publishedAt":189,"updatedAt":190,"category":191,"categories":192,"featuredImage":28,"bgImage":29,"posterImage":194,"relatedSolution":28},34,"deploy-your-applications-with-dokku-on-a-vps",{"fr":183,"en":181,"ar":184,"es":185},"deployer-avec-dokku","انشر-تطبيقاتك-باستخدام-dokku-على-خادم-vps","desplegar-aplicaciones-con-dokku-en-un-vps","Deploy your applications with Dokku on a VPS","Complete Dokku v0.35 guide: installation, buildpacks, env vars, rollback, postgres\u002Fredis\u002Fletsencrypt plugins and troubleshooting common errors.",1,"2026-05-17T00:00:00+00:00","2026-10-18T21:57:59+00:00",{"id":18,"name":23,"slug":24,"color":25,"icon":24},[193],{"id":18,"name":23,"slug":24,"color":25,"icon":24},"\u002Fblog\u002Fcovers\u002Fdeployer-avec-dokku-poster.svg",{"id":196,"slug":197,"slugs":198,"title":202,"excerpt":203,"readTime":168,"views":188,"isPinned":19,"noindex":19,"publishedAt":204,"updatedAt":171,"category":205,"categories":206,"featuredImage":28,"bgImage":29,"posterImage":208,"relatedSolution":209},35,"deploy-your-applications-with-portainer-on-a-vps",{"fr":199,"en":197,"ar":200,"es":201},"deployer-avec-portainer","انشر-تطبيقاتك-باستخدام-portainer-على-خادم-vps","desplegar-aplicaciones-con-portainer-en-un-vps","Deploy your applications with Portainer on a VPS","Manage Docker visually with Portainer on your VPS: containers, stacks, volumes and networks managed from a clear web interface.","2026-05-16T00:00:00+00:00",{"id":18,"name":23,"slug":24,"color":25,"icon":24},[207],{"id":18,"name":23,"slug":24,"color":25,"icon":24},"\u002Fblog\u002Fcovers\u002Fdeployer-avec-portainer-poster.svg",{"categorySlug":177,"appSlug":210},"portainer",true,1794270388685]