Traefik es un proxy inverso que lee la configuración de enrutado directamente de las etiquetas de tus contenedores Docker: al arrancar un contenedor con las etiquetas adecuadas, Traefik crea la ruta y solicita el certificado TLS sin que tengas que recargar nada. En este tutorial desplegarás Traefik v3 con Docker Compose en Ubuntu 24.04, publicarás un servicio de ejemplo con HTTPS de Let's Encrypt, balancearás carga entre varias réplicas y protegerás el panel de Traefik con autenticación básica.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS y una IP pública, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudoque pertenezca al grupodocker. - Docker Engine y el plugin Docker Compose instalados desde el repositorio oficial de Docker.
- Un dominio con dos registros DNS de tipo A apuntando a la IP del servidor:
whoami.your_domainytraefik.your_domain. Sustituyeyour_domainpor tu dominio en todo el tutorial. - Los puertos 80 y 443 libres en el servidor (sin Nginx ni Apache escuchando en ellos).
Conceptos básicos
Traefik separa la configuración en dos partes:
- Configuración estática: se lee al arrancar (fichero
traefik.yml). Define los puntos de entrada (entryPoints, los puertos en los que escucha), los proveedores (en este caso Docker) y los resolvedores de certificados. - Configuración dinámica: cambia en caliente. Con el proveedor Docker procede de las etiquetas de cada contenedor y define routers (qué peticiones aceptar, por ejemplo por
Host), services (a qué contenedor y puerto enviarlas) y middlewares (transformaciones intermedias como autenticación o limitación de peticiones).
Paso 1: Abrir el cortafuegos
Traefik necesita el puerto 80 para el reto HTTP de Let's Encrypt y para redirigir a HTTPS, y el 443 para servir el tráfico cifrado. Si usas UFW, permite ambos junto con SSH:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Comprueba las reglas:
sudo ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
Importantelos puertos que publicas con
ports:en Docker se abren mediante reglas de iptables propias de Docker y no pasan por UFW. Publica solo los puertos de Traefik y deja el resto de contenedores sinports:, accesibles únicamente a través del proxy.
Paso 2: Crear la red compartida
Traefik y los contenedores que publica tienen que compartir una red Docker. Crea una red externa llamada proxy, que podrán usar todos tus proyectos de Compose:
docker network create proxy
3f1c8a2d9b7e4f0a6c5d1e2b8a9f7c6d5e4b3a2c1d0e9f8a7b6c5d4e3f2a1b0c
Paso 3: Escribir la configuración estática
Crea el directorio del proyecto:
sudo mkdir -p /opt/traefik
sudo chown "$USER": /opt/traefik
cd /opt/traefik
Crea el fichero de configuración estática:
nano traefik.yml
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
providers:
docker:
exposedByDefault: false
network: proxy
certificatesResolvers:
letsencrypt:
acme:
email: admin@your_domain
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
api:
dashboard: true
log:
level: INFO
accessLog: {}
Qué hace cada bloque:
entryPoints: Traefik escucha en el 80 (web) y en el 443 (websecure). Todo lo que llega al 80 se redirige a HTTPS, y todos los routers dewebsecureusan por defecto el resolvedorletsencrypt.providers.docker: conexposedByDefault: false, Traefik ignora los contenedores que no tengan la etiquetatraefik.enable=true.network: proxyindica por qué red llegar a ellos.certificatesResolvers: registra una cuenta ACME con tu correo y guarda los certificados enacme.json. Cambiaadmin@your_domainpor un correo real, ya que Let's Encrypt lo usa para avisos.api.dashboard: activa el panel web, que publicarás de forma protegida en el paso 6.
Paso 4: Arrancar Traefik con Docker Compose
Crea el fichero de Compose:
nano compose.yaml
services:
traefik:
image: traefik:v3
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./letsencrypt:/letsencrypt
networks:
- proxy
networks:
proxy:
external: true
Traefik lee por defecto /etc/traefik/traefik.yml, así que no hace falta pasarle ningún argumento. El socket de Docker se monta en solo lectura porque Traefik solo necesita consultar los contenedores.
Advertenciael acceso al socket de Docker equivale a acceso root en el host. Usa siempre la imagen oficial
traefiky no expongas el panel sin autenticación.
Arranca el contenedor:
docker compose up -d
Comprueba que está en marcha y que no hay errores en el registro:
docker compose ps
docker compose logs traefik
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
traefik traefik:v3 "/entrypoint.sh trae…" traefik 10 seconds ago Up 9 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
traefik | time="..." level=info msg="Traefik version 3.x.x built on ..."
Paso 5: Publicar un servicio con HTTPS
Como servicio de ejemplo usarás traefik/whoami, un pequeño servidor web que responde con los datos de la petición y el nombre del contenedor. Crea un proyecto aparte, como harías con una aplicación real:
mkdir -p /opt/traefik/whoami
nano /opt/traefik/whoami/compose.yaml
services:
whoami:
image: traefik/whoami
restart: unless-stopped
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.your_domain`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
networks:
proxy:
external: true
Las etiquetas crean un router llamado whoami que acepta peticiones HTTPS para whoami.your_domain y las envía al puerto 80 del contenedor. No hay sección ports:: el contenedor no es accesible desde fuera salvo a través de Traefik.
Arranca el servicio:
cd /opt/traefik/whoami
docker compose up -d
La primera vez, Traefik solicita el certificado a Let's Encrypt, lo que tarda unos segundos. Comprueba primero que HTTP redirige a HTTPS:
curl -sI http://whoami.your_domain | grep -i location
Location: https://whoami.your_domain/
Después comprueba la respuesta por HTTPS:
curl https://whoami.your_domain
Hostname: 8d2f4c1b7a9e
IP: 127.0.0.1
IP: 172.18.0.3
RemoteAddr: 172.18.0.2:41234
GET / HTTP/1.1
Host: whoami.your_domain
...
Si curl devuelve un error de certificado, revisa el registro de Traefik con docker logs traefik; el mensaje de ACME indica si falló la resolución DNS o el acceso al puerto 80.
Balancear carga entre réplicas
Cuando un servicio de Compose tiene varios contenedores, Traefik los añade todos al mismo backend y reparte las peticiones en round-robin. Escala whoami a tres réplicas:
docker compose up -d --scale whoami=3
Lanza varias peticiones y fíjate en el campo Hostname, que cambia en cada respuesta:
for i in 1 2 3; do curl -s https://whoami.your_domain | grep Hostname; done
Hostname: 8d2f4c1b7a9e
Hostname: 1a7c3e9f2b4d
Hostname: 5e0b8d6a3c1f
Paso 6: Publicar el panel de Traefik con contraseña
El panel muestra todos los routers, servicios y middlewares activos, así que debe ir protegido. Usarás el middleware basicAuth con un fichero de usuarios en formato htpasswd.
Instala htpasswd, que está en el paquete apache2-utils:
sudo apt install apache2-utils
Genera el usuario admin con hash bcrypt. El comando te pedirá la contraseña dos veces:
htpasswd -cB /opt/traefik/usersfile admin
chmod 600 /opt/traefik/usersfile
New password:
Re-type new password:
Adding password for user admin
Edita /opt/traefik/compose.yaml y añade el fichero de usuarios y las etiquetas del panel al servicio traefik:
nano /opt/traefik/compose.yaml
services:
traefik:
image: traefik:v3
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./letsencrypt:/letsencrypt
- ./usersfile:/etc/traefik/usersfile:ro
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.your_domain`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
- "traefik.http.middlewares.dashboard-auth.basicauth.usersfile=/etc/traefik/usersfile"
networks:
proxy:
external: true
api@internal es el servicio interno de Traefik que sirve el panel y su API. Al usar un fichero de usuarios en lugar de la etiqueta basicauth.users evitas tener que escapar los caracteres $ del hash en el YAML de Compose.
Recrea el contenedor para aplicar los cambios:
cd /opt/traefik
docker compose up -d
Comprueba que sin credenciales se deniega el acceso:
curl -I https://traefik.your_domain/dashboard/
HTTP/2 401
www-authenticate: Basic realm="traefik"
Abre https://traefik.your_domain/dashboard/ en el navegador (la barra final es necesaria), introduce el usuario admin y la contraseña, y verás los routers whoami y dashboard en la sección HTTP.
Paso 7: Añadir middlewares a un servicio
Los middlewares se declaran con etiquetas y se asignan a un router con routers.<nombre>.middlewares. Como ejemplo, limita whoami a una media de 10 peticiones por segundo por IP de origen, con ráfagas de hasta 20, y añade la cabecera HSTS. Edita /opt/traefik/whoami/compose.yaml y amplía las etiquetas:
nano /opt/traefik/whoami/compose.yaml
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.your_domain`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.middlewares=whoami-ratelimit,whoami-headers"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
- "traefik.http.middlewares.whoami-ratelimit.ratelimit.average=10"
- "traefik.http.middlewares.whoami-ratelimit.ratelimit.burst=20"
- "traefik.http.middlewares.whoami-headers.headers.stsSeconds=31536000"
Aplica el cambio y comprueba que la cabecera aparece en la respuesta:
cd /opt/traefik/whoami
docker compose up -d
curl -sI https://whoami.your_domain | grep -i strict-transport
strict-transport-security: max-age=31536000
Si superas el límite, Traefik responde con 429 Too Many Requests.
Solución de problemas
404 page not foundal acceder al dominio: Traefik no tiene un router para ese host. Comprueba que el contenedor tienetraefik.enable=true, que está en la redproxyy que la reglaHostcoincide exactamente con el dominio.Gateway TimeoutoBad Gateway: el contenedor está en una red a la que Traefik no llega o el puerto deloadbalancer.server.portno es el puerto en el que escucha la aplicación dentro del contenedor.- El navegador muestra el certificado
TRAEFIK DEFAULT CERT: Let's Encrypt no pudo validar el dominio. Revisadocker logs traefik | grep -i acme; lo habitual es que el registro DNS aún no apunte al servidor o que el puerto 80 esté bloqueado. - Errores de límite de Let's Encrypt (
too many certificates): no borresletsencrypt/acme.jsonpara reintentar, ya que Traefik vuelve a pedir todos los certificados. Durante las pruebas puedes añadircaServer: https://acme-staging-v02.api.letsencrypt.org/directorydentro del bloqueacme.
Conclusión
Tienes Traefik funcionando como punto de entrada único de tu servidor, con redirección a HTTPS, certificados de Let's Encrypt que se renuevan solos, balanceo entre réplicas y un panel protegido. Para publicar cualquier otra aplicación basta con conectarla a la red proxy y añadirle las etiquetas del paso 5. Como siguientes pasos, puedes usar el reto DNS para obtener certificados comodín, activar las métricas de Prometheus de Traefik o añadir el middleware ipAllowList para restringir el panel a tu IP.
