Tutorial on Configuring Traefik on a Cloud Server
Use Traefik, Docker Compose, and Let's Encrypt to set up a unified entry point on a cloud server, enabling multiple web services to be quickly integrated via domain names, HTTPS, and container labels.
- 21 min read
- 2026.08.04
By placing Traefik at the entry point of the cloud server to centrally handle reverse proxying, Let’s Encrypt certificates, and routing, business containers can be integrated simply by joining the same Docker network and using labels to specify their domain names and ports. The server only exposes ports 22, 80, and 443, so there’s no need to modify the Nginx configuration when adding new services.
Tutorial on Configuring Traefik on a Cloud Server: Setting Up a Reusable HTTPS Entry Point with Docker Compose
If you have a cloud server and plan to gradually deploy a blog, backend, API, file service, and WebSocket service on it, you’ll eventually run into a problem: each service requires you to manage its own ports, domain names, HTTPS certificates, and forwarding rules, which can quickly become a mess to maintain.
My approach is to place Traefik at the server’s entry point, where it handles reverse proxying, HTTPS certificates, and traffic routing. Business containers simply need to be added to the same Docker network and use labels to specify their domain names, entry points, certificate resolvers, and internal ports—after which they can be proxied uniformly.
This article isn’t just a list of commands; it shares a reusable approach for deploying Traefik on cloud servers. You can start by using the whoami service to verify connectivity, then gradually integrate your own Spring Boot, Node.js, Nginx, WebSocket, gRPC, Gitea SSH, and other services.
Suitable Scenarios
This article assumes you already meet the following conditions:
- Your cloud server has a public IP address.
- You have purchased a domain name and can configure DNS resolution.
- You use Docker Compose to deploy services on the server.
- You want to use Let’s Encrypt to automatically issue HTTPS certificates.
- Services primarily run as Docker containers.
Here’s what the final setup looks like:
用户浏览器
|
| HTTPS / TCP
v
Traefik
|
| Docker network: traefik
v
业务容器:blog / api / file / whoami / gitea ...
Traefik’s core value lies in having a single entry point, with rules following the containers. When adding new services in the future, you won’t need to modify a bunch of Nginx configuration files—simply add labels to the containers.
TIP
If you’re just running a service temporarily, Nginx can certainly handle it. But if services will continue to be added to a server, Traefik’s Docker provider will make future maintenance much easier.
Overall Solution
This solution consists of several parts:
- The server only exposes necessary ports:
22,80,443, and TCP service ports as needed. - Traefik uses the Docker provider to read container labels.
- All proxied containers are added to the same external Docker network:
traefik. - HTTPS certificates are automatically issued by Let’s Encrypt.
- The dashboard is accessible only via domain name, HTTPS, and Basic Auth.
- Cloudflare users can opt for the DNS-01 challenge to easily enable Cloudflare proxy and wildcard certificates.
I recommend placing Traefik in /opt/traefik:
/opt/traefik
├── .env
├── docker-compose.yml
├── whoami.yml
└── letsencrypt
└── acme.json
where acme.json stores certificate information, and .env stores sensitive configurations such as domain names, email addresses, Basic Auth credentials, and the Cloudflare token.
Preparing the Server Environment
First, log in to the server:
ssh root@你的服务器IP
Update the system and install common tools:
apt update && apt upgrade -y
apt install -y curl vim ufw apache2-utils dnsutils
If Docker is not yet installed on the server, use the official installation script:
curl -fsSL https://get.docker.com | sh
Verify that Docker and Compose are available:
docker version
docker compose version
Then configure the firewall:
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status
WARNING
Do not directly expose the Traefik Dashboard’s 8080 port to the public internet. The Dashboard should be accessed via a domain name, HTTPS, and Basic Auth.
Configure Domain Name Resolution
First, add an A record in your DNS provider to point the domain name to the server’s public IP address.
| Record Name | Type | Value |
|---|
| traefik.example.com
| A | Server’s public IP | | whoami.example.com | A | Server’s public IP | | app.example.com | A | Server’s public IP |
Replace example.com with your own domain name.
Verify that the DNS resolution is working:
Continue configuring Traefik only after ```bash dig traefik.example.com +short dig whoami.example.com +short
returns the server’s public IP.
### Options for Cloudflare Users
If your domain is hosted on Cloudflare, you can first connect the domain to Cloudflare and then manage the DNS records using Cloudflare.
Basic steps:
1. Log in to the [Cloudflare Dashboard](https://dash.cloudflare.com/).
2. Click `Add a domain` and enter the root domain, such as `example.com`.
3. Select a plan; the free plan is usually sufficient.
4. Verify that the DNS records detected by Cloudflare are correct.
5. Go to your domain registrar’s control panel and change the NS servers to the two nameservers provided by Cloudflare.
6. Wait until Cloudflare displays the domain status as `Active`.
The recommended DNS records are as follows:
| Record Name | Type | Value | Proxy Status |
| --------- | ---- | ------------- | ------------------- |
| `traefik`
| A | Server’s public IP | DNS only or Proxied |
| `whoami` | A | Server’s public IP | DNS only or Proxied |
| `app` | A | Server’s public IP | DNS only or Proxied |
If using the HTTP-01 certificate challenge, it is recommended to set this to `DNS only` during the debugging phase to avoid interference from the Cloudflare proxy layer. Once the certificate has been issued and the access path is functioning normally, enable `Proxied` as needed.
It is recommended to set Cloudflare’s SSL/TLS mode to:
Overview -> Full (strict)
```text
SSL/TLS -> Overview -> Full (strict)
Full (strict) requires that the origin server also have a valid certificate, which is provided by Traefik and Let’s Encrypt.
Create the Traefik Working Directory
The permissions for ```bash mkdir -p /opt/traefik/letsencrypt cd /opt/traefik touch letsencrypt/acme.json chmod 600 letsencrypt/acme.json
`acme.json` must be tightened; otherwise, Traefik may refuse to use this file.
Next, create an external Docker network:
```bash
docker network create traefik
All service containers that will be proxied by Traefik must be added to this network.
Generate a Dashboard Login Password
Basic Auth must be enabled for the Dashboard. First, generate a username and password: The
htpasswd -nbB admin '换成你的强密码'
command will output something like:
admin:$2y$05$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
In the Docker Compose label, ````text 用户浏览器 | | HTTPS / TCP v Traefik | | Docker network: traefik v 业务容器:blog / api / file / whoami / gitea ...
Traefik’s core value lies in having a single entry point, with rules following the containers. When adding new services in the future, you won’t need to modify a bunch of Nginx configuration files—simply add labels to the containers.
> [!tip]
> If you’re just running a service temporarily, Nginx can certainly handle it. But if services will continue to be added to a server, Traefik’s Docker provider will make future maintenance much easier.
## Overall Solution
This solution consists of several parts:
1. The server only exposes necessary ports: `22`, `80`, `443`, and TCP service ports as needed.
2. Traefik uses the Docker provider to read container labels.
3. All proxied containers are added to the same external Docker network: `traefik`.
4. HTTPS certificates are automatically issued by Let’s Encrypt.
5. The dashboard is accessible only via domain name, HTTPS, and Basic Auth.
6. Cloudflare users can opt for the DNS-01 challenge to easily enable Cloudflare proxy and wildcard certificates.
I recommend placing Traefik in `/opt/traefik`:
```text
/opt/traefik
├── .env
├── docker-compose.yml
├── whoami.yml
└── letsencrypt
└── acme.json
where acme.json stores certificate information, and .env stores sensitive configurations such as domain names, email addresses, Basic Auth credentials, and the Cloudflare token.
Preparing the Server Environment
First, log in to the server:
ssh root@你的服务器IP
Update the system and install common tools:
apt update && apt upgrade -y
apt install -y curl vim ufw apache2-utils dnsutils
If Docker is not yet installed on the server, use the official installation script:
curl -fsSL https://get.docker.com | sh
Verify that Docker and Compose are available:
docker version
docker compose version
Then configure the firewall:
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status
WARNING
Do not directly expose the Traefik Dashboard’s 8080 port to the public internet. The Dashboard should be accessed via a domain name, HTTPS, and Basic Auth.
Configure Domain Name Resolution
First, add an A record in your DNS provider to point the domain name to the server’s public IP address.
| Record Name | Type | Value |
|---|
| traefik.example.com
| A | Server’s public IP | | whoami.example.com | A | Server’s public IP | | app.example.com | A | Server’s public IP |
Replace example.com with your own domain name.
Verify that the DNS resolution is working:
Continue configuring Traefik only after ```bash dig traefik.example.com +short dig whoami.example.com +short
returns the server’s public IP.
### Options for Cloudflare Users
If your domain is hosted on Cloudflare, you can first connect the domain to Cloudflare and then manage the DNS records using Cloudflare.
Basic steps:
1. Log in to the [Cloudflare Dashboard](https://dash.cloudflare.com/).
2. Click `Add a domain` and enter the root domain, such as `example.com`.
3. Select a plan; the free plan is usually sufficient.
4. Verify that the DNS records detected by Cloudflare are correct.
5. Go to your domain registrar’s control panel and change the NS servers to the two nameservers provided by Cloudflare.
6. Wait until Cloudflare displays the domain status as `Active`.
The recommended DNS records are as follows:
| Record Name | Type | Value | Proxy Status |
| --------- | ---- | ------------- | ------------------- |
| `traefik`
| A | Server’s public IP | DNS only or Proxied |
| `whoami` | A | Server’s public IP | DNS only or Proxied |
| `app` | A | Server’s public IP | DNS only or Proxied |
If using the HTTP-01 certificate challenge, it is recommended to set this to `DNS only` during the debugging phase to avoid interference from the Cloudflare proxy layer. Once the certificate has been issued and the access path is functioning normally, enable `Proxied` as needed.
It is recommended to set Cloudflare’s SSL/TLS mode to:
Overview -> Full (strict)
```text
SSL/TLS -> Overview -> Full (strict)
Full (strict) requires that the origin server also have a valid certificate, which is provided by Traefik and Let’s Encrypt.
Create the Traefik Working Directory
The permissions for ```bash mkdir -p /opt/traefik/letsencrypt cd /opt/traefik touch letsencrypt/acme.json chmod 600 letsencrypt/acme.json
`acme.json` must be tightened; otherwise, Traefik may refuse to use this file.
Next, create an external Docker network:
```bash
docker network create traefik
All service containers that will be proxied by Traefik must be added to this network.
Generate a Dashboard Login Password
Basic Auth must be enabled for the Dashboard. First, generate a username and password: The
htpasswd -nbB admin '换成你的强密码'
command will output something like:
admin:$2y$05$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
In the Docker Compose label, `
Tutorial on Configuring Traefik on a Cloud Server: Setting Up a Reusable HTTPS Entry Point with Docker Compose
If you have a cloud server and plan to gradually deploy a blog, backend, API, file service, and WebSocket service on it, you’ll eventually run into a problem: each service requires you to manage its own ports, domain names, HTTPS certificates, and forwarding rules, which can quickly become a mess to maintain.
My approach is to place Traefik at the server’s entry point, where it handles reverse proxying, HTTPS certificates, and traffic routing. Business containers simply need to be added to the same Docker network and use labels to specify their domain names, entry points, certificate resolvers, and internal ports—after which they can be proxied uniformly.
This article isn’t just a list of commands; it shares a reusable approach for deploying Traefik on cloud servers. You can start by using the whoami service to verify connectivity, then gradually integrate your own Spring Boot, Node.js, Nginx, WebSocket, gRPC, Gitea SSH, and other services.
Suitable Scenarios
This article assumes you already meet the following conditions:
- Your cloud server has a public IP address.
- You have purchased a domain name and can configure DNS resolution.
- You use Docker Compose to deploy services on the server.
- You want to use Let’s Encrypt to automatically issue HTTPS certificates.
- Services primarily run as Docker containers.
Here’s what the final setup looks like:
用户浏览器
|
| HTTPS / TCP
v
Traefik
|
| Docker network: traefik
v
业务容器:blog / api / file / whoami / gitea ...
Traefik’s core value lies in having a single entry point, with rules following the containers. When adding new services in the future, you won’t need to modify a bunch of Nginx configuration files—simply add labels to the containers.
TIP
If you’re just running a service temporarily, Nginx can certainly handle it. But if services will continue to be added to a server, Traefik’s Docker provider will make future maintenance much easier.
Overall Solution
This solution consists of several parts:
- The server only exposes necessary ports:
22
, 80
, 443
, and TCP service ports as needed. 2. Traefik uses the Docker provider to read container labels. 3. All proxied containers are added to the same external Docker network: traefik
. 4. HTTPS certificates are automatically issued by Let’s Encrypt. 5. The dashboard is accessible only via domain name, HTTPS, and Basic Auth. 6. Cloudflare users can opt for the DNS-01 challenge to easily enable Cloudflare proxy and wildcard certificates.
I recommend placing Traefik in /opt/traefik
:
/opt/traefik
├── .env
├── docker-compose.yml
├── whoami.yml
└── letsencrypt
└── acme.json
where acme.json
stores certificate information, and .env
stores sensitive configurations such as domain names, email addresses, Basic Auth credentials, and the Cloudflare token.
Preparing the Server Environment
First, log in to the server:
ssh root@你的服务器IP
Update the system and install common tools:
apt update && apt upgrade -y
apt install -y curl vim ufw apache2-utils dnsutils
If Docker is not yet installed on the server, use the official installation script:
curl -fsSL https://get.docker.com | sh
Verify that Docker and Compose are available:
docker version
docker compose version
Then configure the firewall:
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status
WARNING
Do not directly expose the Traefik Dashboard’s 8080
port to the public internet. The Dashboard should be accessed via a domain name, HTTPS, and Basic Auth.
Configure Domain Name Resolution
First, add an A record in your DNS provider to point the domain name to the server’s public IP address.
| Record Name | Type | Value |
|---|
| traefik.example.com
| A | Server’s public IP | | whoami.example.com
| A | Server’s public IP | | app.example.com
| A | Server’s public IP |
Replace example.com
with your own domain name.
Verify that the DNS resolution is working:
Continue configuring Traefik only after ```bash dig traefik.example.com +short dig whoami.example.com +short
returns the server’s public IP.
### Options for Cloudflare Users
If your domain is hosted on Cloudflare, you can first connect the domain to Cloudflare and then manage the DNS records using Cloudflare.
Basic steps:
1. Log in to the [Cloudflare Dashboard](https://dash.cloudflare.com/
).
2. Click `Add a domain`
and enter the root domain, such as `example.com`
.
3. Select a plan; the free plan is usually sufficient.
4. Verify that the DNS records detected by Cloudflare are correct.
5. Go to your domain registrar’s control panel and change the NS servers to the two nameservers provided by Cloudflare.
6. Wait until Cloudflare displays the domain status as `Active`
.
The recommended DNS records are as follows:
| Record Name | Type | Value | Proxy Status |
| --------- | ---- | ------------- | ------------------- |
| `traefik`
| A | Server’s public IP | DNS only or Proxied |
| `whoami`
| A | Server’s public IP | DNS only or Proxied |
| `app`
| A | Server’s public IP | DNS only or Proxied |
If using the HTTP-01 certificate challenge, it is recommended to set this to `DNS only`
during the debugging phase to avoid interference from the Cloudflare proxy layer. Once the certificate has been issued and the access path is functioning normally, enable `Proxied`
as needed.
It is recommended to set Cloudflare’s SSL/TLS mode to:
Overview -> Full (strict)
```text
SSL/TLS -> Overview -> Full (strict)
Full (strict)
requires that the origin server also have a valid certificate, which is provided by Traefik and Let’s Encrypt.
Create the Traefik Working Directory
The permissions for ```bash mkdir -p /opt/traefik/letsencrypt cd /opt/traefik touch letsencrypt/acme.json chmod 600 letsencrypt/acme.json
`acme.json`
must be tightened; otherwise, Traefik may refuse to use this file.
Next, create an external Docker network:
```bash
docker network create traefik
All service containers that will be proxied by Traefik must be added to this network.
Generate a Dashboard Login Password
Basic Auth must be enabled for the Dashboard. First, generate a username and password: The
htpasswd -nbB admin '换成你的强密码'
command will output something like:
admin:$2y$05$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
In the Docker Compose label, 需要转义成$
Tutorial on Configuring Traefik on a Cloud Server: Setting Up a Reusable HTTPS Entry Point with Docker Compose
If you have a cloud server and plan to gradually deploy a blog, backend, API, file service, and WebSocket service on it, you’ll eventually run into a problem: each service requires you to manage its own ports, domain names, HTTPS certificates, and forwarding rules, which can quickly become a mess to maintain.
My approach is to place Traefik at the server’s entry point, where it handles reverse proxying, HTTPS certificates, and traffic routing. Business containers simply need to be added to the same Docker network and use labels to specify their domain names, entry points, certificate resolvers, and internal ports—after which they can be proxied uniformly.
This article isn’t just a list of commands; it shares a reusable approach for deploying Traefik on cloud servers. You can start by using the whoami
service to verify connectivity, then gradually integrate your own Spring Boot, Node.js, Nginx, WebSocket, gRPC, Gitea SSH, and other services.
Suitable Scenarios
This article assumes you already meet the following conditions:
- Your cloud server has a public IP address.
- You have purchased a domain name and can configure DNS resolution.
- You use Docker Compose to deploy services on the server.
- You want to use Let’s Encrypt to automatically issue HTTPS certificates.
- Services primarily run as Docker containers.
Here’s what the final setup looks like:
用户浏览器
|
| HTTPS / TCP
v
Traefik
|
| Docker network: traefik
v
业务容器:blog / api / file / whoami / gitea ...
Traefik’s core value lies in having a single entry point, with rules following the containers. When adding new services in the future, you won’t need to modify a bunch of Nginx configuration files—simply add labels to the containers.
TIP
If you’re just running a service temporarily, Nginx can certainly handle it. But if services will continue to be added to a server, Traefik’s Docker provider will make future maintenance much easier.
Overall Solution
This solution consists of several parts:
- The server only exposes necessary ports:
22
, 80
, 443
, and TCP service ports as needed. 2. Traefik uses the Docker provider to read container labels. 3. All proxied containers are added to the same external Docker network: traefik
. 4. HTTPS certificates are automatically issued by Let’s Encrypt. 5. The dashboard is accessible only via domain name, HTTPS, and Basic Auth. 6. Cloudflare users can opt for the DNS-01 challenge to easily enable Cloudflare proxy and wildcard certificates.
I recommend placing Traefik in /opt/traefik
:
/opt/traefik
├── .env
├── docker-compose.yml
├── whoami.yml
└── letsencrypt
└── acme.json
where acme.json
stores certificate information, and .env
stores sensitive configurations such as domain names, email addresses, Basic Auth credentials, and the Cloudflare token.
Preparing the Server Environment
First, log in to the server:
ssh root@你的服务器IP
Update the system and install common tools:
apt update && apt upgrade -y
apt install -y curl vim ufw apache2-utils dnsutils
If Docker is not yet installed on the server, use the official installation script:
curl -fsSL https://get.docker.com | sh
Verify that Docker and Compose are available:
docker version
docker compose version
Then configure the firewall:
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status
WARNING
Do not directly expose the Traefik Dashboard’s 8080
port to the public internet. The Dashboard should be accessed via a domain name, HTTPS, and Basic Auth.
Configure Domain Name Resolution
First, add an A record in your DNS provider to point the domain name to the server’s public IP address.
| Record Name | Type | Value |
|---|
| traefik.example.com
| A | Server’s public IP | | whoami.example.com
| A | Server’s public IP | | app.example.com
| A | Server’s public IP |
Replace example.com
with your own domain name.
Verify that the DNS resolution is working:
Continue configuring Traefik only after ```bash dig traefik.example.com +short dig whoami.example.com +short
returns the server’s public IP.
### Options for Cloudflare Users
If your domain is hosted on Cloudflare, you can first connect the domain to Cloudflare and then manage the DNS records using Cloudflare.
Basic steps:
1. Log in to the [Cloudflare Dashboard](https://dash.cloudflare.com/
).
2. Click `Add a domain`
and enter the root domain, such as `example.com`
.
3. Select a plan; the free plan is usually sufficient.
4. Verify that the DNS records detected by Cloudflare are correct.
5. Go to your domain registrar’s control panel and change the NS servers to the two nameservers provided by Cloudflare.
6. Wait until Cloudflare displays the domain status as `Active`
.
The recommended DNS records are as follows:
| Record Name | Type | Value | Proxy Status |
| --------- | ---- | ------------- | ------------------- |
| `traefik`
| A | Server’s public IP | DNS only or Proxied |
| `whoami`
| A | Server’s public IP | DNS only or Proxied |
| `app`
| A | Server’s public IP | DNS only or Proxied |
If using the HTTP-01 certificate challenge, it is recommended to set this to `DNS only`
during the debugging phase to avoid interference from the Cloudflare proxy layer. Once the certificate has been issued and the access path is functioning normally, enable `Proxied`
as needed.
It is recommended to set Cloudflare’s SSL/TLS mode to:
Overview -> Full (strict)
```text
SSL/TLS -> Overview -> Full (strict)
Full (strict)
requires that the origin server also have a valid certificate, which is provided by Traefik and Let’s Encrypt.
Create the Traefik Working Directory
The permissions for ```bash mkdir -p /opt/traefik/letsencrypt cd /opt/traefik touch letsencrypt/acme.json chmod 600 letsencrypt/acme.json
`acme.json`
must be tightened; otherwise, Traefik may refuse to use this file.
Next, create an external Docker network:
```bash
docker network create traefik
All service containers that will be proxied by Traefik must be added to this network.
Generate a Dashboard Login Password
Basic Auth must be enabled for the Dashboard. First, generate a username and password: The
htpasswd -nbB admin '换成你的强密码'
command will output something like:
admin:$2y$05$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
In the Docker Compose label,
. If you place the password hash in .env
and enclose it in single quotes, you can preserve the original format:
TRAEFIK_BASIC_AUTH_USERS='admin:$2y$05$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Writing the Traefik Configuration
First, create /opt/traefik/.env
:
TRAEFIK_IMAGE=traefik:v3.6
TRAEFIK_DOMAIN=traefik.example.com
ACME_EMAIL=你的邮箱@example.com
TRAEFIK_BASIC_AUTH_USERS='admin:$2y$05$替换成你生成的密码哈希'
If using the Cloudflare DNS-01 challenge, add the following:
CF_DNS_API_TOKEN=替换成你的Cloudflare_API_Token
Restrict the permissions for .env
:
chmod 600 /opt/traefik/.env
WARNING
.env
in a public repository, and do not post the actual token in notes, group chats, or screenshots. Although the Basic Auth hash is not a plaintext password, it is still recommended to treat it as sensitive information.
HTTP-01 Version: Suitable for Most Beginner Scenarios
If you’re not using Cloudflare, or if you just want to get the basic setup working first, you can use HTTP-01. It requires that port 80
on the server be accessible from the public internet.
Create /opt/traefik/docker-compose.yml
:
services:
traefik:
image: ${TRAEFIK_IMAGE}
container_name: traefik
restart: unless-stopped
env_file:
- .env
command:
- '--api.dashboard=true'
- '--providers.docker=true'
- '--providers.docker.exposedbydefault=false'
- '--providers.docker.network=traefik'
- '--entrypoints.web.address=:80'
- '--entrypoints.websecure.address=:443'
- '--entrypoints.ssh.address=:2222'
- '--entrypoints.web.http.redirections.entrypoint.to=websecure'
- '--entrypoints.web.http.redirections.entrypoint.scheme=https'
- '--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}'
- '--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json'
- '--certificatesresolvers.letsencrypt.acme.httpchallenge=true'
- '--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web'
ports:
- '80:80'
- '443:443'
- '2222:2222'
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
networks:
- traefik
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.traefik.rule=Host(`${TRAEFIK_DOMAIN}`)'
- 'traefik.http.routers.traefik.entrypoints=websecure'
- 'traefik.http.routers.traefik.tls.certresolver=letsencrypt'
- 'traefik.http.routers.traefik.service=api@internal'
- 'traefik.http.routers.traefik.middlewares=traefik-auth'
- 'traefik.http.middlewares.traefik-auth.basicauth.users=${TRAEFIK_BASIC_AUTH_USERS}'
networks:
traefik:
external: true
Here are a few key points:
exposedbydefault=false
: By default, no containers are exposed; you must explicitly add traefik.enable=true
.
providers.docker.network=traefik
: Tells Traefik to prioritize this Docker network when accessing backend containers.
web
and websecure
: Correspond to 80
and 443
, respectively.
entrypoints.web.http.redirections...
: Automatically redirects HTTP to HTTPS.
api@internal
: Exposes Traefik’s own Dashboard as an internal service, which is then bound to the domain via the router.
DNS-01 Version: Suitable for Cloudflare and Wildcard Certificates
If your domain is hosted on Cloudflare, we recommend using DNS-01. It has three advantages:
- It does not rely on port
80
for certificate verification.
- You can enable Cloudflare’s Orange Cloud proxy.
- You can apply for a wildcard certificate, such as
*.example.com
.
First, create a Cloudflare API token:
- Go to [Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens
). 2. Click Create Token
. 3. You can choose the Edit zone DNS
template or create a custom token. 4. Permissions must include at least Zone / Zone / Read
and Zone / DNS / Edit
. 5. Limit Zone Resources to the domain you need to manage, such as example.com
. 6. After creation, copy the token and paste it into CF_DNS_API_TOKEN
in /opt/traefik/.env
.
Then replace the ACME challenge-related commands with DNS-01:
command:
- '--api.dashboard=true'
- '--providers.docker=true'
- '--providers.docker.exposedbydefault=false'
- '--providers.docker.network=traefik'
- '--entrypoints.web.address=:80'
- '--entrypoints.websecure.address=:443'
- '--entrypoints.ssh.address=:2222'
- '--entrypoints.web.http.redirections.entrypoint.to=websecure'
- '--entrypoints.web.http.redirections.entrypoint.scheme=https'
- '--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}'
- '--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json'
- '--certificatesresolvers.letsencrypt.acme.dnschallenge=true'
- '--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare'
- '--certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53'
. If a service requires a wildcard certificate, add the following to the corresponding router:
labels:
- 'traefik.http.routers.app.tls.domains[0].main=example.com'
- 'traefik.http.routers.app.tls.domains[0].sans=*.example.com'
NOTE
Traefik’s DNS-01 functionality relies on Lego at the core. The Cloudflare provider supports CF_DNS_API_TOKEN
and also supports splitting “Zone Read” and “DNS Edit” into two more granular permission tokens: CF_ZONE_API_TOKEN
and CF_DNS_API_TOKEN
.
Start Traefik
cd /opt/traefik
docker compose up -d
docker compose logs -f traefik
Then visit:
https://traefik.example.com/dashboard/
If you see a Basic Auth login prompt and can view the Dashboard after logging in, it means the entry proxy is running successfully.
TIP
Certificate issuance may take a while on the first startup. When troubleshooting, check docker compose logs -f traefik
first; avoid restarting too many times to prevent triggering Let’s Encrypt’s rate limits.
Verify the Proxy Connection with whoami
Before integrating with production services, I usually deploy a test service at whoami
first. It’s simple enough to quickly verify that the domain, certificate, routing, and Docker network are all functioning correctly.
Create /opt/traefik/whoami.yml
:
services:
whoami:
image: traefik/whoami
container_name: whoami
restart: unless-stopped
networks:
- traefik
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.whoami.rule=Host(`whoami.example.com`)'
- 'traefik.http.routers.whoami.entrypoints=websecure'
- 'traefik.http.routers.whoami.tls.certresolver=letsencrypt'
- 'traefik.http.services.whoami.loadbalancer.server.port=80'
networks:
traefik:
external: true
Start the test service:
docker compose -f /opt/traefik/whoami.yml up -d
Verify:
curl -I https://whoami.example.com
If the response is HTTP/2 200
or HTTP/1.1 200 OK
, the HTTPS proxy is working properly.
Integrating Your Own Business Services
There are only three key steps for integrating business services with Traefik:
- Add the service to the
traefik
external network. 2. Configure traefik.enable=true
. 3. Use labels to declare the domain name, entry point, certificate resolver, and container internal port.
Minimal example:
services:
app:
image: your-app:latest
restart: unless-stopped
networks:
- traefik
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=letsencrypt'
- 'traefik.http.services.app.loadbalancer.server.port=8080'
networks:
traefik:
external: true
loadbalancer.server.port=8080
refers to the container’s internal listening port, not the host machine’s port. After integrating with Traefik, business containers typically no longer need to specify:
ports:
- '8080:8080'
unless this port needs to be exposed directly, bypassing Traefik.
Common Service Integration Templates
Standard Web Services
Spring Boot, Node.js, Nginx, static frontend sites, and admin dashboards can all use this type of template:
services:
app:
image: your-app:latest
restart: unless-stopped
networks:
- traefik
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=letsencrypt'
- 'traefik.http.services.app.loadbalancer.server.port=8080'
networks:
traefik:
external: true
File Upload Services
By default, Traefik does not use client_max_body_size
to limit upload sizes like Nginx does.Actual upload size limits are typically enforced by the business application, Nginx, Spring Boot, the gateway, the object storage SDK, or Cloudflare.
If you want to actively limit upload sizes at the Traefik layer, you can use the buffering
middleware:
services:
file-api:
image: your-file-api:latest
restart: unless-stopped
networks:
- traefik
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.file-api.rule=Host(`file.example.com`)'
- 'traefik.http.routers.file-api.entrypoints=websecure'
- 'traefik.http.routers.file-api.tls.certresolver=letsencrypt'
- 'traefik.http.routers.file-api.middlewares=file-upload-limit'
- 'traefik.http.middlewares.file-upload-limit.buffering.maxRequestBodyBytes=104857600'
- 'traefik.http.middlewares.file-upload-limit.buffering.memRequestBodyBytes=10485760'
- 'traefik.http.services.file-api.loadbalancer.server.port=8080'
networks:
traefik:
external: true
Explanation of the configuration above:
maxRequestBodyBytes=104857600
: Maximum upload request body size is 100 MB.
memRequestBodyBytes=10485760
: Do not load the entire body into memory once it exceeds 10 MB.
WARNING
If Cloudflare’s Orange Cloud proxy is enabled, you must also be aware of Cloudflare’s own upload size limits. For large file uploads, it is recommended to have the client upload directly to object storage—such as S3, R2, or OSS—and then have the business service save the file URL.
File Download Services
For standard download services, you can use the HTTP router directly. When downloading large files, the key is to prevent the application layer from loading the entire file into memory at once.
services:
download:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./downloads:/usr/share/nginx/html:ro
networks:
- traefik
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.download.rule=Host(`download.example.com`)'
- 'traefik.http.routers.download.entrypoints=websecure'
- 'traefik.http.routers.download.tls.certresolver=letsencrypt'
- 'traefik.http.services.download.loadbalancer.server.port=80'
networks:
traefik:
external: true
If you need to limit the size of the download response, you can also use buffering.maxResponseBodyBytes
:
labels:
- 'traefik.http.routers.download.middlewares=download-limit'
- 'traefik.http.middlewares.download-limit.buffering.maxResponseBodyBytes=524288000'
It is not recommended to arbitrarily set maxResponseBodyBytes
for large file downloads, as Traefik will reject requests that exceed the limit. In most cases, limits should be enforced through business authentication, signed object storage URLs, or CDN rules.
WebSocket Services
Traefik automatically handles the HTTP Upgrade for WebSockets; generally, no additional configuration of the Upgrade
or Connection
request headers is required.
services:
ws-app:
image: your-ws-app:latest
restart: unless-stopped
networks:
- traefik
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.ws-app.rule=Host(`ws.example.com`)'
- 'traefik.http.routers.ws-app.entrypoints=websecure'
- 'traefik.http.routers.ws-app.tls.certresolver=letsencrypt'
- 'traefik.http.services.ws-app.loadbalancer.server.port=3000'
networks:
traefik:
external: true
Example of a client connection:
const socket = new WebSocket('wss://ws.example.com/socket');
If WebSockets and regular APIs are on the same domain, you can route them separately by path:
labels:
- 'traefik.http.routers.app-api.rule=Host(`app.example.com`) && PathPrefix(`/api`)'
- 'traefik.http.routers.app-api.entrypoints=websecure'
- 'traefik.http.routers.app-api.tls.certresolver=letsencrypt'
- 'traefik.http.routers.app-api.service=app-api'
- 'traefik.http.services.app-api.loadbalancer.server.port=8080'
- 'traefik.http.routers.app-ws.rule=Host(`app.example.com`) && PathPrefix(`/socket`)'
- 'traefik.http.routers.app-ws.entrypoints=websecure'
- 'traefik.http.routers.app-ws.tls.certresolver=letsencrypt'
- 'traefik.http.routers.app-ws.service=app-ws'
- 'traefik.http.services.app-ws.loadbalancer.server.port=3000'
NOTE
When Cloudflare proxies WebSockets, you must also enable WebSockets on the Cloudflare side. Most accounts support this by default, but if connections frequently drop, you should check your Cloudflare rules, server heartbeats, and application timeout settings.
gRPC Services
gRPC uses HTTP/2. When Traefik handles HTTPS and forwards it to the backend gRPC service, you typically need to specify the service scheme as h2c
.
services:
grpc-api:
image: your-grpc-api:latest
restart: unless-stopped
networks:
- traefik
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.grpc-api.rule=Host(`grpc.example.com`)'
- 'traefik.http.routers.grpc-api.entrypoints=websecure'
- 'traefik.http.routers.grpc-api.tls.certresolver=letsencrypt'
- 'traefik.http.services.grpc-api.loadbalancer.server.scheme=h2c'
- 'traefik.http.services.grpc-api.loadbalancer.server.port=50051'
networks:
traefik:
external: true
SSH Services
SSH is not HTTP and cannot use the traefik.http.*
label; a TCP router must be used instead. Since the host machine typically already occupies port 22
, it is recommended that Traefik use 2222
for external connections, while still forwarding internally within the container to 22
.
The Traefik main configuration must include an SSH entrypoint:
command:
- '--entrypoints.ssh.address=:2222'
ports:
- '2222:2222'
. Gitea SSH example:
services:
gitea:
image: gitea/gitea:latest
restart: unless-stopped
networks:
- traefik
volumes:
- ./gitea:/data
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=letsencrypt'
- 'traefik.http.services.gitea.loadbalancer.server.port=3000'
- 'traefik.tcp.routers.gitea-ssh.rule=HostSNI(`*`)'
- 'traefik.tcp.routers.gitea-ssh.entrypoints=ssh'
- 'traefik.tcp.routers.gitea-ssh.service=gitea-ssh'
- 'traefik.tcp.services.gitea-ssh.loadbalancer.server.port=22'
networks:
traefik:
external: true
. Connection method:
ssh -p 2222 git@git.example.com
git clone ssh://git@git.example.com:2222/用户名/仓库名.git
WARNING
The HostSNI
of a TCP router can only be distinguished by domain name within a TLS connection. Since SSH itself does not support SNI, the common practice is to have one SSH entrypoint correspond to one SSH service; if there are multiple SSH services, assign multiple external ports, such as 2222
and 2223
.
TCP Services Such as Databases, Redis, and MQTT
Databases, Redis, MQTT, and similar services are also TCP services, and their configuration is similar to SSH. However, they generally should not be directly exposed to the public internet; it is more appropriate to access them via the internal network, a VPN, a jump server, or Cloudflare Tunnel.
If you do need to temporarily expose PostgreSQL, the configuration would look something like this:
services:
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_PASSWORD: 强密码
networks:
- traefik
labels:
- 'traefik.enable=true'
- 'traefik.tcp.routers.postgres.rule=HostSNI(`*`)'
- 'traefik.tcp.routers.postgres.entrypoints=postgres'
- 'traefik.tcp.routers.postgres.service=postgres'
- 'traefik.tcp.services.postgres.loadbalancer.server.port=5432'
networks:
traefik:
external: true
. The Traefik main configuration needs to include:
command:
- '--entrypoints.postgres.address=:5432'
ports:
- '5432:5432'
[!danger] Exposing a database directly to the public internet carries high risks. At a minimum, restrict the source IPs in the security group, use strong passwords, enable TLS for the database itself, and prioritize access via VPN or the internal network.
Common Operations Commands
View Traefik logs:
docker logs -f traefik
Check Traefik version:
docker exec traefik traefik version
Restart Traefik:
cd /opt/traefik
docker compose restart traefik
Update Traefik:
cd /opt/traefik
docker compose pull
docker compose up -d
It is recommended to back up certificate files before updating:
cp /opt/traefik/letsencrypt/acme.json /opt/traefik/letsencrypt/acme.json.bak
Check the final Compose configuration:
cd /opt/traefik
docker compose config
This command is very useful when troubleshooting whether the .env variable is taking effect.
Troubleshooting Common Issues
| Issue | Possible Cause | Resolution |
|---|---|---|
| Certificate issuance consistently fails | The domain name is not resolved to the server, port 80 is not open, or DNS changes have not taken effect | Check dig 域名 +short, security groups, and ufw status |
| 404 error when accessing the domain | Container does not have traefik.enable=true configured, or the Host rule is incorrect | Check container labels and Routers in the Traefik Dashboard |
| 502 error when accessing the domain | Traefik cannot find the application container port or the network is unreachable | Check loadbalancer.server.port and whether the container is added to the traefik network |
| Dashboard is inaccessible | The Dashboard domain is not resolved, Basic Auth is configured incorrectly, or the route label is incorrect | Check docker logs -f traefik |
| HTTP Not Redirecting to HTTPS | Missing entrypoint redirection configuration | Check entrypoints.web.http.redirections.entrypoint.* |
| Let's Encrypt Rate Limiting | Frequent retries after repeated failures | Troubleshoot the configuration before restarting; use the ACME staging server during debugging |
| Cloudflare DNS-01 failure | Insufficient API token permissions or incorrect zone scope | Verify that the token has Zone:Read and DNS:Edit, and is bound to the correct domain |
| Access Issues After Enabling Cloudflare Orange Cloud | SSL/TLS mode is not set to “Full strict,” or the origin server certificate has not been issued | Configure Cloudflare SSL/TLS -> Full (strict) and check the Traefik certificate logs |
.env variable not taking effect | File is not in the same directory as Compose, or variable name is incorrect | Execute docker compose config in /opt/traefik |
| WebSocket connection failed | Incorrect backend path, server not handling "Upgrade," or Cloudflare rules affecting the connection | First, bypass Cloudflare to test; then check application logs and routing rules |
| File upload failed | Backend restrictions, Cloudflare upload limits, Traefik buffering limits | Check the application configuration, Cloudflare quota limits, and maxRequestBodyBytes separately |
| Unable to connect via SSH | TCP entrypoint not open, host port conflict, incorrect TCP service port | Check ports, security groups, ufw, and traefik.tcp.services.*.port |
| Multiple SSH services cannot be load-balanced by domain name | SSH lacks SNI | Configure different entrypoints and external ports for different SSH services |
Minimum Security Checklist
After placing Traefik at the public network entry point, security configuration must be taken seriously. My minimum checklist is:
- Do not enable
--api.insecure=truefor public network access. - Do not expose
8080to the public network. - Mount Docker sockets as read-only:
/var/run/docker.sock:/var/run/docker.sock:ro. - Disable automatic port exposure by default:
--providers.docker.exposedbydefault=false. - The dashboard must use HTTPS and Basic Auth.
- Set permissions for
.envto600; do not expose the Cloudflare API token. - Grant the Cloudflare API token access only to necessary domains and with minimal permissions.
- Use fixed-version images, such as
traefik:v3.6; do not uselatestlong-term. - Prioritize adding business containers only to the Traefik network; do not directly expose the host machine’s ports.
- For TCP services such as SSH, databases, and MQTT, assess individually whether public exposure is truly necessary.
- In scenarios where VPNs, internal networks, or Cloudflare Tunnel can be used, do not directly expose database ports to the public internet.
Summary
Traefik is best suited for solving the problem of “increasingly difficult-to-maintain ingress configurations as the number of services on a cloud server grows.” It separates reverse proxy rules from a centralized configuration file and places them on each container’s own labels, keeping the service deployment declaration and routing declaration together.
Once this setup is working, adding new services typically requires only four steps:
- Add DNS resolution for the domain name.
- Add the container to the
traefiknetwork. - Declare the Host, entrypoint, certificate resolver, and internal port in the container labels.
docker compose up -dStart the service.
From a maintenance perspective, this approach is better suited for personal servers, small-team servers, and multi-service experimental environments than manually writing an Nginx server block every time. With clear entry points, automatic certificate renewal, and a unified integration method, you won’t have to start from scratch when later expanding to WebSocket, gRPC, SSH, or TCP services.