Volver al laboratorio
Completado
15 de enero de 202525 min de lectura

Montar Traefik + Portainer en VPS desde Cero

Tutorial completo para configurar un stack de gestión Docker con Traefik, Portainer, Uptime Kuma y Dozzle en tu propia VPS.

DockerTraefikPortainerDevOpsSSL
SL

Sebastian

Full-Stack Developer

El origen de todo esto

Estoy construyendo mi propia plataforma para subir mis proyectos y que sean accesibles desde cualquier lugar. Esta es mi primera opción para alojar mis proyectos.

Este tutorial te guía paso a paso para configurar un stack completo de gestión Docker con Traefik, Portainer, Dozzle y Uptime Kuma.


¿Qué hace cada servicio?

Traefik

Reverse proxy y load balancer moderno para microservicios. Detecta automáticamente los contenedores de Docker y genera certificados SSL con Let's Encrypt. Básicamente, le dices qué dominio quieres y él se encarga de todo.

Portainer

Interfaz web para gestionar Docker. Permite ver contenedores, logs, volúmenes, redes y desplegar stacks sin tocar la terminal. Muy útil cuando tienes varios proyectos corriendo.

Uptime Kuma

Sistema de monitoreo para tus servicios. Envía alertas a Telegram, Discord, Email o Slack cuando algo se cae. La tranquilidad de saber que si algo falla, te enterás al instante.

Dozzle

Visor de logs en tiempo real de todos los contenedores. Interfaz web ligera y sin base de datos. Perfecto para cuando necesitás ver qué está pasando en tus apps sin conectarte por SSH.


1. Requisitos Previos

VPS mínimo recomendado

  • RAM: 2GB mínimo (4GB recomendado)
  • CPU: 1 vCPU
  • Disco: 20GB SSD
  • OS: Ubuntu 22.04/24.04 LTS

Dominio configurado

Necesitas un dominio con registros DNS tipo A apuntando a tu VPS:

  • traefik.tudominio.com → IP_VPS
  • portainer.tudominio.com → IP_VPS
  • logs.tudominio.com → IP_VPS
  • uptime.tudominio.com → IP_VPS

Los DNS deben estar propagados ANTES de levantar los servicios, o Let's Encrypt fallará al generar certificados.


2. Preparar la VPS

2.1 Conectar por SSH

bash
ssh usuario@IP_VPS

2.2 Actualizar sistema

bash
sudo apt update && sudo apt upgrade -y

2.3 Instalar Docker

bash
# Instalar dependencias
sudo apt install -y ca-certificates curl gnupg lsb-release

# Agregar GPG key de Docker
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Agregar repositorio
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Instalar Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Permitir usar Docker sin sudo
sudo usermod -aG docker $USER

# Aplicar cambios (o reconectar SSH)
newgrp docker

2.4 Verificar instalación

bash
docker --version
docker compose version

2.5 Configurar firewall

bash
sudo ufw allow 22/tcp   # SSH
sudo ufw allow 80/tcp   # HTTP
sudo ufw allow 443/tcp  # HTTPS
sudo ufw enable
sudo ufw status

3. Crear Estructura del Proyecto

3.1 Crear directorios

bash
mkdir -p ~/apps/traefik
cd ~/apps/traefik

3.2 Crear red de Docker

bash
docker network create traefik_network

3.3 Crear volumen para Portainer

bash
docker volume create portainer_data

3.4 Crear archivo acme.json (certificados SSL)

bash
touch acme.json
chmod 600 acme.json

El archivo acme.json DEBE tener permisos 600, o Traefik no arrancará.


4. Archivos de Configuración

4.1 Crear traefik.yml

yaml
api:
  dashboard: true

entryPoints:
  web:
    address: ':80'
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

  websecure:
    address: ':443'

providers:
  docker:
    endpoint: 'unix:///var/run/docker.sock'
    exposedByDefault: false
    network: traefik_network

certificatesResolvers:
  letsencrypt:
    acme:
      email: TU_EMAIL@ejemplo.com
      storage: acme.json
      httpChallenge:
        entryPoint: web
  • exposedByDefault: false - Los contenedores NO se exponen automáticamente, debes usar labels
  • httpChallenge - Let's Encrypt valida tu dominio via HTTP (puerto 80)
  • Redirección automática HTTP → HTTPS

4.2 Crear docker-compose.yml

yaml
services:
  traefik:
    image: traefik:v2.10
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    ports:
      - "80:80"
      - "443:443"
    environment:
      - TZ=America/Bogota
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/traefik.yml:ro
      - ./acme.json:/acme.json
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`traefik.tudominio.com`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
      - "traefik.http.routers.traefik.service=api@internal"
    networks:
      - traefik_network

  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.portainer.rule=Host(`portainer.tudominio.com`)"
      - "traefik.http.routers.portainer.entrypoints=websecure"
      - "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
      - "traefik.http.services.portainer.loadbalancer.server.port=9000"
    networks:
      - traefik_network

  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    volumes:
      - uptime_kuma_data:/app/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.uptime.rule=Host(`uptime.tudominio.com`)"
      - "traefik.http.routers.uptime.entrypoints=websecure"
      - "traefik.http.routers.uptime.tls.certresolver=letsencrypt"
      - "traefik.http.services.uptime.loadbalancer.server.port=3001"
    networks:
      - traefik_network

  dozzle:
    image: amir20/dozzle:latest
    container_name: dozzle
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    environment:
      - DOZZLE_AUTH_PROVIDER=simple
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./dozzle_data:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dozzle.rule=Host(`logs.tudominio.com`)"
      - "traefik.http.routers.dozzle.entrypoints=websecure"
      - "traefik.http.routers.dozzle.tls.certresolver=letsencrypt"
      - "traefik.http.services.dozzle.loadbalancer.server.port=8080"
    networks:
      - traefik_network

networks:
  traefik_network:
    name: traefik_network
    driver: bridge

volumes:
  portainer_data:
    external: true
  uptime_kuma_data:

5. Configurar Autenticación Dozzle

bash
mkdir -p dozzle_data

# Generar usuario (cambiar admin/admin por tus credenciales)
docker run --rm amir20/dozzle generate admin --password TU_PASSWORD --email tu@email.com --name Admin > dozzle_data/users.yml

6. Levantar los Servicios

6.1 Iniciar stack

bash
cd ~/apps/traefik
docker compose up -d

6.2 Verificar que todo esté corriendo

bash
docker ps

6.3 Ver logs de Traefik

bash
docker logs traefik -f

7. Acceder a los Servicios

Una vez levantado, accede a:

  • Traefik Dashboard: https://traefik.tudominio.com (solo visualización)
  • Portainer: https://portainer.tudominio.com (crear usuario admin)
  • Uptime Kuma: https://uptime.tudominio.com (crear usuario admin)
  • Dozzle (logs): https://logs.tudominio.com (usuario creado en paso 5)

8. Agregar Nuevos Servicios

Para exponer cualquier nuevo servicio a través de Traefik, agrega estas labels:

yaml
services:
  mi-app:
    image: mi-imagen
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.miapp.rule=Host(`app.tudominio.com`)"
      - "traefik.http.routers.miapp.entrypoints=websecure"
      - "traefik.http.routers.miapp.tls.certresolver=letsencrypt"
      - "traefik.http.services.miapp.loadbalancer.server.port=PUERTO_INTERNO"
    networks:
      - traefik_network

networks:
  traefik_network:
    external: true

Los servicios en OTRO docker-compose deben usar external: true en la red.


9. Comandos Útiles

bash
# Ver estado de contenedores
docker ps

# Ver logs de un servicio
docker logs traefik -f

# Reiniciar un servicio
docker compose restart traefik

# Actualizar imágenes
docker compose pull
docker compose up -d

# Limpiar imágenes viejas
docker image prune -f

# Ver certificados generados
cat acme.json | jq '.letsencrypt.Certificates[].domain'

10. Solución de Problemas

Certificado SSL no se genera

  1. Verificar que el DNS esté propagado: dig +short traefik.tudominio.com
  2. Verificar permisos de acme.json (debe ser 600)
  3. Ver logs de Traefik para errores ACME: docker logs traefik 2>&1 | grep -i acme

Traefik no arranca

bash
# Verificar configuración
docker compose config

# Ver logs de error
docker logs traefik

Bad Gateway (502)

  • Verificar que el contenedor destino esté corriendo
  • Verificar que el puerto en loadbalancer.server.port sea correcto
  • Verificar que ambos contenedores estén en traefik_network

11. Seguridad Adicional

Proteger Dashboard de Traefik con BasicAuth

bash
# Generar credenciales
sudo apt install apache2-utils -y
echo $(htpasswd -nb admin TU_PASSWORD_SEGURO)

Agregar a labels de traefik:

yaml
- "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$..."
- "traefik.http.routers.traefik.middlewares=traefik-auth"

En docker-compose, escapar $ con $$.


12. Backup

Archivos críticos a respaldar

  • acme.json (certificados SSL)
  • docker-compose.yml (configuración)
  • traefik.yml (config de Traefik)
  • dozzle_data/ (usuarios Dozzle)

Script de backup simple

bash
#!/bin/bash
BACKUP_DIR=~/backups/traefik-$(date +%Y%m%d)
mkdir -p $BACKUP_DIR
cp -r ~/apps/traefik/* $BACKUP_DIR/
docker run --rm -v portainer_data:/data -v $BACKUP_DIR:/backup alpine tar czf /backup/portainer_data.tar.gz /data
echo "Backup completado en $BACKUP_DIR"

Referencias

Experimentos relacionados