30 KiB
30 KiB
Guía de Migración: Nginx Proxy Manager → Traefik
Fecha: 5 de Octubre, 2025 Servidor: nucleo002 Configuraciones Extraídas: 18 Proxy Hosts
📋 Índice
- Resumen de Configuraciones NPM
- Configuración Base de Traefik
- Traducción de Cada Proxy Host
- Middlewares Comunes
- Certificados SSL
- Docker Compose Completo
- Scripts de Migración
1. Resumen de Configuraciones NPM
Proxy Hosts Detectados
| ID | Dominio | Backend | Puerto | SSL | Características Especiales |
|---|---|---|---|---|---|
| 2 | portainer.interno.com | portainer | 9443 | Custom | - |
| 3 | gitea.interno.com | gitea | 3000 | Custom | - |
| 4 | geoids.interno.com | geo-ids | 443 | Custom | Authentik SSO |
| 5 | amigos.nucleoriofrio.com | amigos-app | 3001 | Let's Encrypt | Authentik SSO (parcial) |
| 7 | whatsappbot.interno.com | whatsapp-router | 3001 | Custom | - |
| 8 | conversation-layer.interno.com | - | - | Custom | - |
| 9 | Nginx.interno.com | npm | 81 | Custom | Admin Panel |
| 10 | planilla.interno.com | planilla-ui | 80 | Custom | Multi-location (/, /api, /mcp, /events) |
| 11 | nucleobot.interno.com | whatsapp-router | 3001 | Custom | - |
| 16 | nucleoriofrio.com | z590.interno.com | 3000 | Let's Encrypt | Custom locations (/api/chat/stream, /api/chat/) |
| 17 | musica.interno.com | z590.interno.com | 3000 | Custom | HTTP/2, HMR, Multiple locations |
| 18 | musica.nucleoriofrio.com | repodructor | 3000 | Let's Encrypt | Multiple locations, Cache headers |
| 19 | snatchgame.interno.com | z590.interno.com | 3004 | Custom | 7 locations (/ws, /colyseus, /api/*) |
| 20 | snatchgame.nucleoriofrio.com | snatchgame | 2567 | Let's Encrypt | 6 locations, Asset caching |
| 21 | duplicati.interno.com | duplicati | - | Custom | - |
| 22 | internet.nucleoriofrio.com | z590.interno.com | 80 | Let's Encrypt | - |
| 23 | wifi.nucleoriofrio.com | radiusnucleo-node-1 | - | Let's Encrypt | - |
Características Globales
- HSTS: Habilitado en todos (
max-age=63072000; preload) - Force SSL: Mayoría de hosts
- Block Exploits: Todos
- WebSocket Support: Habilitado donde se necesita
- HTTP/2: Solo musica.interno.com
2. Configuración Base de Traefik
2.1 Docker Compose - Traefik
version: '3.8'
services:
traefik:
image: traefik:v3.1
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- principal
ports:
- "80:80"
- "443:443"
- "8080:8080" # Dashboard
environment:
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN} # Si usas Cloudflare
- CF_ZONE_API_TOKEN=${CF_ZONE_API_TOKEN}
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./config/:/config/:ro
- ./acme.json:/acme.json
- ./certificates/:/certificates/:ro
labels:
- "traefik.enable=true"
# Dashboard
- "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.interno.com`)"
- "traefik.http.routers.traefik-dashboard.entrypoints=https"
- "traefik.http.routers.traefik-dashboard.tls=true"
- "traefik.http.routers.traefik-dashboard.service=api@internal"
- "traefik.http.routers.traefik-dashboard.middlewares=auth@file"
networks:
principal:
external: true
2.2 traefik.yml (Static Configuration)
api:
dashboard: true
debug: false
entryPoints:
http:
address: ":80"
http:
redirections:
entryPoint:
to: https
scheme: https
permanent: true
https:
address: ":443"
http:
tls:
options: default
certResolver: letsencrypt
domains:
- main: "*.interno.com"
- main: "*.nucleoriofrio.com"
http2:
maxConcurrentStreams: 250
metrics:
address: ":8082"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: principal
watch: true
file:
directory: /config
watch: true
certificatesResolvers:
letsencrypt:
acme:
email: admin@nucleoriofrio.com
storage: /acme.json
httpChallenge:
entryPoint: http
# O para wildcard con DNS challenge:
# dnsChallenge:
# provider: cloudflare
# delayBeforeCheck: 0
custom:
acme:
tlsChallenge: {}
storage: /acme.json
# Global HTTP settings
serversTransport:
insecureSkipVerify: true # Solo si usas certificados autofirmados en backend
# Logging
log:
level: INFO
filePath: /logs/traefik.log
format: json
accessLog:
filePath: /logs/access.log
format: json
bufferingSize: 100
# Metrics (opcional)
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels: true
entryPoint: metrics
3. Traducción de Cada Proxy Host
3.1 Portainer (portainer.interno.com)
NPM Original:
- Backend:
portainer:9443(HTTPS) - SSL: Custom certificate
- Force SSL
Traefik Labels:
# En docker-compose del stack portainer
services:
portainer:
# ... resto de config
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`portainer.interno.com`)"
- "traefik.http.routers.portainer.entrypoints=https"
- "traefik.http.routers.portainer.tls=true"
- "traefik.http.routers.portainer.tls.certresolver=custom"
- "traefik.http.services.portainer.loadbalancer.server.port=9443"
- "traefik.http.services.portainer.loadbalancer.server.scheme=https"
# Middlewares
- "traefik.http.routers.portainer.middlewares=hsts-headers,block-exploits"
networks:
- principal
3.2 Gitea (gitea.interno.com)
NPM Original:
- Backend:
gitea:3000(HTTP) - SSL: Custom
- No Force SSL
Traefik Labels:
services:
gitea:
labels:
- "traefik.enable=true"
- "traefik.http.routers.gitea.rule=Host(`gitea.interno.com`)"
- "traefik.http.routers.gitea.entrypoints=https"
- "traefik.http.routers.gitea.tls=true"
- "traefik.http.routers.gitea.tls.certresolver=custom"
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
- "traefik.http.routers.gitea.middlewares=hsts-headers,block-exploits"
networks:
- principal
3.3 GeoIDs con Authentik SSO (geoids.interno.com)
NPM Original:
- Backend:
geo-ids:443(HTTPS) - Auth: Authentik via
auth_request - WebSocket support
- Custom buffers
Traefik Labels + Middleware File:
services:
geo-ids:
labels:
- "traefik.enable=true"
- "traefik.http.routers.geoids.rule=Host(`geoids.interno.com`)"
- "traefik.http.routers.geoids.entrypoints=https"
- "traefik.http.routers.geoids.tls=true"
- "traefik.http.routers.geoids.tls.certresolver=custom"
- "traefik.http.services.geoids.loadbalancer.server.port=443"
- "traefik.http.services.geoids.loadbalancer.server.scheme=https"
# Middlewares (orden importa!)
- "traefik.http.routers.geoids.middlewares=authentik-geoids@file,hsts-headers,websocket-headers"
networks:
- principal
Middleware File (/config/middlewares-authentik.yml):
http:
middlewares:
authentik-geoids:
forwardAuth:
address: "http://authentik-server:9000/outpost.goauthentik.io/auth/nginx"
trustForwardHeader: true
authResponseHeaders:
- "X-authentik-username"
- "X-authentik-groups"
- "X-authentik-email"
- "X-authentik-name"
- "X-authentik-uid"
- "Set-Cookie"
3.4 Planilla Multi-Location (planilla.interno.com)
NPM Original:
/→planilla-ui:80/api→planilla-api:4000/mcp→planilla-mcp:5000/events→planilla-api:4000- WebSocket en todos
Traefik Dynamic Config (/config/planilla.yml):
http:
routers:
planilla-ui:
rule: "Host(`planilla.interno.com`)"
entryPoints:
- https
service: planilla-ui-svc
middlewares:
- hsts-headers
- websocket-headers
tls:
certResolver: custom
priority: 1
planilla-api:
rule: "Host(`planilla.interno.com`) && PathPrefix(`/api`)"
entryPoints:
- https
service: planilla-api-svc
middlewares:
- hsts-headers
- websocket-headers
tls:
certResolver: custom
priority: 10
planilla-mcp:
rule: "Host(`planilla.interno.com`) && PathPrefix(`/mcp`)"
entryPoints:
- https
service: planilla-mcp-svc
middlewares:
- hsts-headers
- websocket-headers
tls:
certResolver: custom
priority: 10
planilla-events:
rule: "Host(`planilla.interno.com`) && PathPrefix(`/events`)"
entryPoints:
- https
service: planilla-api-svc # Mismo que /api
middlewares:
- hsts-headers
- websocket-headers
tls:
certResolver: custom
priority: 10
services:
planilla-ui-svc:
loadBalancer:
servers:
- url: "http://planilla-ui:80"
planilla-api-svc:
loadBalancer:
servers:
- url: "http://planilla-api:4000"
planilla-mcp-svc:
loadBalancer:
servers:
- url: "http://planilla-mcp:5000"
3.5 Música con HMR (musica.interno.com)
NPM Original:
- HTTP/2: ON
- Multiple locations:
/_nuxt/,/__nuxt_devtools__/,/api/ - WebSocket para HMR
- Proxy buffering OFF
- Large client body
Traefik Config (/config/musica.yml):
http:
routers:
musica-nuxt:
rule: "Host(`musica.interno.com`) && PathPrefix(`/_nuxt/`)"
entryPoints:
- https
service: musica-z590-svc
middlewares:
- websocket-headers
- no-buffering
tls:
certResolver: custom
priority: 20
musica-devtools:
rule: "Host(`musica.interno.com`) && PathPrefix(`/__nuxt_devtools__/`)"
entryPoints:
- https
service: musica-z590-svc
middlewares:
- websocket-headers
- no-buffering
tls:
certResolver: custom
priority: 20
musica-api:
rule: "Host(`musica.interno.com`) && PathPrefix(`/api/`)"
entryPoints:
- https
service: musica-z590-svc
middlewares:
- no-buffering
- large-timeout
tls:
certResolver: custom
priority: 15
musica-root:
rule: "Host(`musica.interno.com`)"
entryPoints:
- https
service: musica-z590-svc
middlewares:
- hsts-headers
tls:
certResolver: custom
priority: 1
services:
musica-z590-svc:
loadBalancer:
servers:
- url: "http://192.168.87.135:3000" # z590.interno.com
passHostHeader: true
middlewares:
no-buffering:
buffering:
maxRequestBodyBytes: 104857600 # 100MB
memRequestBodyBytes: 104857600
maxResponseBodyBytes: 0 # Sin límite
memResponseBodyBytes: 0
retryExpression: "IsNetworkError() && Attempts() < 2"
large-timeout:
buffering:
maxRequestBodyBytes: 104857600
# Los timeouts se configuran en service o entrypoint
3.6 SnatchGame Complejo (snatchgame.interno.com)
NPM Original:
- 7 locations diferentes
- WebSocket en todos
- Asset caching
Traefik Config (/config/snatchgame.yml):
http:
routers:
snatch-ws:
rule: "Host(`snatchgame.interno.com`) && PathPrefix(`/ws`)"
entryPoints:
- https
service: snatch-z590-svc
middlewares:
- websocket-headers
- asset-cache
tls:
certResolver: custom
priority: 30
snatch-colyseus:
rule: "Host(`snatchgame.interno.com`) && PathPrefix(`/colyseus`)"
entryPoints:
- https
service: snatch-z590-svc
middlewares:
- websocket-headers
- asset-cache
tls:
certResolver: custom
priority: 25
snatch-api-stream:
rule: "Host(`snatchgame.interno.com`) && Path(`/api/dashboard-stream`)"
entryPoints:
- https
service: snatch-z590-svc
middlewares:
- websocket-headers
tls:
certResolver: custom
priority: 40
snatch-api-uuids-stream:
rule: "Host(`snatchgame.interno.com`) && Path(`/api/uuids-stream`)"
entryPoints:
- https
service: snatch-z590-svc
middlewares:
- websocket-headers
tls:
certResolver: custom
priority: 40
snatch-api-admin:
rule: "Host(`snatchgame.interno.com`) && PathPrefix(`/api/admin/`)"
entryPoints:
- https
service: snatch-z590-svc
middlewares:
- asset-cache
tls:
certResolver: custom
priority: 35
snatch-api-wildcard:
rule: "Host(`snatchgame.interno.com`) && PathRegexp(`^/api/.*`)"
entryPoints:
- https
service: snatch-z590-svc
middlewares:
- asset-cache
tls:
certResolver: custom
priority: 20
snatch-root:
rule: "Host(`snatchgame.interno.com`)"
entryPoints:
- https
service: snatch-z590-svc
middlewares:
- hsts-headers
- asset-cache
tls:
certResolver: custom
priority: 1
services:
snatch-z590-svc:
loadBalancer:
servers:
- url: "http://z590.interno.com:3004"
3.7 Nucleoriofrio.com con API Chat
NPM Original:
/api/chat/stream→ Streaming con buffering OFF/api/chat/→ API normal- CORS headers
- Large timeouts
Traefik Config (/config/nucleoriofrio.yml):
http:
routers:
nucleo-chat-stream:
rule: "Host(`nucleoriofrio.com`) && Path(`/api/chat/stream`)"
entryPoints:
- https
service: nucleo-z590-alt-svc
middlewares:
- no-buffering
- cors-permissive
- large-timeout
tls:
certResolver: letsencrypt
priority: 50
nucleo-chat-api:
rule: "Host(`nucleoriofrio.com`) && PathPrefix(`/api/chat/`)"
entryPoints:
- https
service: nucleo-z590-alt-svc
middlewares:
- cors-permissive
tls:
certResolver: letsencrypt
priority: 40
nucleo-root:
rule: "Host(`nucleoriofrio.com`)"
entryPoints:
- https
service: nucleo-z590-svc
middlewares:
- hsts-headers
tls:
certResolver: letsencrypt
priority: 1
services:
nucleo-z590-svc:
loadBalancer:
servers:
- url: "https://z590.interno.com:3000"
nucleo-z590-alt-svc:
loadBalancer:
servers:
- url: "https://192.168.87.135:3000"
middlewares:
cors-permissive:
headers:
accessControlAllowOriginList:
- "*"
accessControlAllowMethods:
- "GET"
- "POST"
- "OPTIONS"
accessControlAllowHeaders:
- "Accept"
- "Authorization"
- "Cache-Control"
- "Content-Type"
- "DNT"
- "If-Modified-Since"
- "Keep-Alive"
- "Origin"
- "User-Agent"
- "X-Requested-With"
accessControlMaxAge: 100
addVaryHeader: true
3.8 Música Pública (musica.nucleoriofrio.com)
NPM Original:
- Multiple locations con diferentes propósitos
- Caching de assets
- Security headers
- Streaming optimizations
Traefik Config (/config/musica-public.yml):
http:
routers:
musica-pub-nuxt:
rule: "Host(`musica.nucleoriofrio.com`) && PathPrefix(`/_nuxt/`)"
entryPoints:
- https
service: repodructor-svc
middlewares:
- asset-cache-long
tls:
certResolver: letsencrypt
priority: 30
musica-pub-music:
rule: "Host(`musica.nucleoriofrio.com`) && PathPrefix(`/music/`)"
entryPoints:
- https
service: repodructor-svc
middlewares:
- no-buffering
- music-streaming
- music-cache
tls:
certResolver: letsencrypt
priority: 25
musica-pub-api:
rule: "Host(`musica.nucleoriofrio.com`) && PathPrefix(`/api/`)"
entryPoints:
- https
service: repodructor-svc
middlewares:
- api-timeout
tls:
certResolver: letsencrypt
priority: 20
musica-pub-root:
rule: "Host(`musica.nucleoriofrio.com`)"
entryPoints:
- https
service: repodructor-svc
middlewares:
- hsts-headers
- security-headers
tls:
certResolver: letsencrypt
priority: 1
services:
repodructor-svc:
loadBalancer:
servers:
- url: "http://repodructor:3000"
middlewares:
asset-cache-long:
headers:
customResponseHeaders:
Cache-Control: "public, max-age=31536000, immutable"
music-cache:
headers:
customResponseHeaders:
Cache-Control: "public, max-age=3600"
music-streaming:
buffering:
maxRequestBodyBytes: 0 # Sin límite
memRequestBodyBytes: 0
maxResponseBodyBytes: 0
memResponseBodyBytes: 0
api-timeout:
buffering:
maxRequestBodyBytes: 10485760 # 10MB
security-headers:
headers:
customResponseHeaders:
X-Frame-Options: "SAMEORIGIN"
X-Content-Type-Options: "nosniff"
X-XSS-Protection: "1; mode=block"
4. Middlewares Comunes
4.1 Archivo de Middlewares Globales (/config/middlewares.yml)
http:
middlewares:
# HSTS Headers
hsts-headers:
headers:
stsSeconds: 63072000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
# WebSocket Support
websocket-headers:
headers:
customRequestHeaders:
X-Forwarded-Proto: "https"
customResponseHeaders:
Upgrade: "$http_upgrade"
Connection: "$http_connection"
# Block Common Exploits
block-exploits:
headers:
browserXssFilter: true
contentTypeNosniff: true
frameDeny: true
customFrameOptionsValue: "SAMEORIGIN"
ipWhiteList:
sourceRange:
- "127.0.0.1/32"
- "192.168.0.0/16"
- "172.16.0.0/12"
- "10.0.0.0/8"
# Asset Caching
asset-cache:
headers:
customResponseHeaders:
Cache-Control: "public, max-age=86400"
# Security Headers Strict
security-strict:
headers:
sslRedirect: true
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
frameDeny: true
contentTypeNosniff: true
browserXssFilter: true
referrerPolicy: "strict-origin-when-cross-origin"
permissionsPolicy: "camera=(), microphone=(), geolocation=()"
customResponseHeaders:
X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex"
# Rate Limiting
rate-limit:
rateLimit:
average: 100
burst: 200
period: 1m
# Basic Auth (ejemplo)
auth-basic:
basicAuth:
users:
- "admin:$apr1$..." # htpasswd hash
removeHeader: true
# IP Whitelist (ejemplo para admin panels)
admin-whitelist:
ipWhiteList:
sourceRange:
- "192.168.87.0/24"
- "172.19.0.0/16"
# Compress
compress:
compress: {}
# Strip Prefix (útil para algunos backends)
strip-api-prefix:
stripPrefix:
prefixes:
- "/api"
forceSlash: false
# Redirect Scheme (HTTP → HTTPS)
redirect-https:
redirectScheme:
scheme: https
permanent: true
# Custom Headers for Backend
backend-headers:
headers:
customRequestHeaders:
X-Forwarded-Proto: "https"
X-Forwarded-Scheme: "https"
X-Real-IP: "" # Traefik lo rellena automáticamente
X-Forwarded-For: ""
5. Certificados SSL
5.1 Certificado Custom (*.interno.com)
Opción A: Usar el certificado autofirmado existente
# En /config/tls.yml
tls:
stores:
default:
defaultCertificate:
certFile: /certificates/interno.com/fullchain.pem
keyFile: /certificates/interno.com/privkey.pem
certificates:
- certFile: /certificates/interno.com/fullchain.pem
keyFile: /certificates/interno.com/privkey.pem
stores:
- default
Copiar certificados:
mkdir -p /srv/traefik/certificates/interno.com
cp /srv/nginx-proxy-manager/data/custom_ssl/npm-1/fullchain.pem \
/srv/traefik/certificates/interno.com/
cp /srv/nginx-proxy-manager/data/custom_ssl/npm-1/privkey.pem \
/srv/traefik/certificates/interno.com/
5.2 Let's Encrypt (*.nucleoriofrio.com)
Ya configurado en traefik.yml:
certificatesResolvers:
letsencrypt:
acme:
email: admin@nucleoriofrio.com
storage: /acme.json
httpChallenge:
entryPoint: http
Para wildcard con DNS Challenge (Cloudflare):
certificatesResolvers:
letsencrypt:
acme:
email: admin@nucleoriofrio.com
storage: /acme.json
dnsChallenge:
provider: cloudflare
delayBeforeCheck: 10
resolvers:
- "1.1.1.1:53"
- "1.0.0.1:53"
Variables de entorno necesarias:
CF_DNS_API_TOKEN=your_cloudflare_token
CF_ZONE_API_TOKEN=your_zone_token
6. Docker Compose Completo
6.1 Stack Traefik Principal
version: '3.8'
services:
traefik:
image: traefik:v3.1
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- principal
ports:
- "80:80"
- "443:443"
- "8080:8080" # Dashboard (cambiar puerto si hay conflicto)
environment:
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
- CF_ZONE_API_TOKEN=${CF_ZONE_API_TOKEN}
- TZ=America/Argentina/Buenos_Aires
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./config/:/config/:ro
- ./acme.json:/acme.json
- ./certificates/:/certificates/:ro
- ./logs/:/logs/
labels:
- "traefik.enable=true"
# Dashboard
- "traefik.http.routers.dashboard.rule=Host(`traefik.interno.com`)"
- "traefik.http.routers.dashboard.entrypoints=https"
- "traefik.http.routers.dashboard.tls=true"
- "traefik.http.routers.dashboard.tls.certresolver=custom"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.middlewares=admin-whitelist"
networks:
principal:
external: true
6.2 Estructura de Directorios
/srv/traefik/
├── traefik.yml # Config estática
├── acme.json # Certificados LE (chmod 600)
├── docker-compose.yml
├── .env
├── config/
│ ├── middlewares.yml # Middlewares globales
│ ├── middlewares-authentik.yml
│ ├── tls.yml # Certificados custom
│ ├── planilla.yml # Config dinámica planilla
│ ├── musica.yml
│ ├── snatchgame.yml
│ ├── nucleoriofrio.yml
│ └── musica-public.yml
├── certificates/
│ └── interno.com/
│ ├── fullchain.pem
│ └── privkey.pem
└── logs/
├── traefik.log
└── access.log
6.3 Preparación
# Crear estructura
mkdir -p /srv/traefik/{config,certificates/interno.com,logs}
# Crear acme.json
touch /srv/traefik/acme.json
chmod 600 /srv/traefik/acme.json
# Copiar certificados
cp /srv/nginx-proxy-manager/data/custom_ssl/npm-1/fullchain.pem \
/srv/traefik/certificates/interno.com/
cp /srv/nginx-proxy-manager/data/custom_ssl/npm-1/privkey.pem \
/srv/traefik/certificates/interno.com/
# Crear archivos de config
touch /srv/traefik/traefik.yml
touch /srv/traefik/config/{middlewares,tls,planilla,musica,snatchgame}.yml
7. Scripts de Migración
7.1 Script de Pre-Migración
#!/bin/bash
# pre-migration.sh
set -e
echo "🔍 Pre-Migration Checklist para Traefik"
# Backup de NPM
echo "📦 Backing up NPM configs..."
mkdir -p /srv/backups/npm-$(date +%Y%m%d)
cp -r /srv/nginx-proxy-manager/data/nginx /srv/backups/npm-$(date +%Y%m%d)/
cp -r /srv/nginx-proxy-manager/data/custom_ssl /srv/backups/npm-$(date +%Y%m%d)/
# Verificar red principal
echo "🌐 Verificando red principal..."
docker network inspect principal > /dev/null 2>&1 || {
echo "❌ Red 'principal' no existe"
exit 1
}
# Verificar contenedores backend
echo "🔍 Verificando backends..."
backends=(
"portainer"
"gitea"
"geo-ids"
"planilla-ui"
"planilla-api"
"planilla-mcp"
"whatsapp-router"
"repodructor"
"amigos-app"
"duplicati"
"radiusnucleo-node-1"
)
for backend in "${backends[@]}"; do
docker ps --filter "name=$backend" --format "{{.Names}}" | grep -q "$backend" || {
echo "⚠️ Backend '$backend' no encontrado o detenido"
}
done
# Crear estructura Traefik
echo "📁 Creando estructura de directorios..."
mkdir -p /srv/traefik/{config,certificates/interno.com,logs}
touch /srv/traefik/acme.json
chmod 600 /srv/traefik/acme.json
# Copiar certificados
echo "🔐 Copiando certificados SSL..."
cp /srv/nginx-proxy-manager/data/custom_ssl/npm-1/fullchain.pem \
/srv/traefik/certificates/interno.com/
cp /srv/nginx-proxy-manager/data/custom_ssl/npm-1/privkey.pem \
/srv/traefik/certificates/interno.com/
echo "✅ Pre-migration completa"
echo ""
echo "Próximos pasos:"
echo "1. Crear archivos de configuración de Traefik"
echo "2. Actualizar labels en docker-compose de cada stack"
echo "3. Ejecutar migration.sh"
7.2 Script de Migración
#!/bin/bash
# migration.sh
set -e
echo "🚀 Iniciando migración NPM → Traefik"
# Detener NPM
echo "🛑 Deteniendo NPM..."
cd /srv/nginx-proxy-manager
docker-compose down
# Levantar Traefik
echo "🚀 Levantando Traefik..."
cd /srv/traefik
docker-compose up -d
# Esperar a que Traefik esté listo
echo "⏳ Esperando a que Traefik esté listo..."
sleep 10
# Verificar Traefik
echo "✅ Verificando Traefik..."
docker logs traefik --tail 50
# Verificar dashboard
echo "🌐 Dashboard disponible en: https://traefik.interno.com"
# Restart de cada stack para aplicar labels
echo "🔄 Reiniciando stacks con nuevas labels..."
stacks=(
"/srv/gitea"
"/srv/planilla"
"/srv/conversation-layer"
# ... más stacks
)
for stack in "${stacks[@]}"; do
if [ -d "$stack" ]; then
echo " Reiniciando $stack..."
cd "$stack"
docker-compose up -d --force-recreate
fi
done
echo "✅ Migración completa!"
echo ""
echo "Verificar:"
echo "1. Dashboard Traefik: https://traefik.interno.com"
echo "2. Logs: docker logs traefik -f"
echo "3. Probar cada dominio manualmente"
7.3 Script de Rollback
#!/bin/bash
# rollback.sh
set -e
echo "⏪ Rollback a NPM"
# Detener Traefik
echo "🛑 Deteniendo Traefik..."
cd /srv/traefik
docker-compose down
# Levantar NPM
echo "🚀 Levantando NPM..."
cd /srv/nginx-proxy-manager
docker-compose up -d
echo "✅ Rollback completo"
echo "NPM disponible en: http://Nginx.interno.com:81"
7.4 Script de Verificación
#!/bin/bash
# verify-migration.sh
domains=(
"portainer.interno.com"
"gitea.interno.com"
"geoids.interno.com"
"planilla.interno.com"
"musica.interno.com"
"nucleoriofrio.com"
"snatchgame.interno.com"
"amigos.nucleoriofrio.com"
)
echo "🔍 Verificando dominios..."
for domain in "${domains[@]}"; do
echo -n "Testing $domain... "
if curl -skI "https://$domain" | head -1 | grep -q "200\|301\|302"; then
echo "✅ OK"
else
echo "❌ FAIL"
fi
done
echo ""
echo "Verificar manualmente:"
echo "1. Autenticación Authentik en geoids.interno.com"
echo "2. WebSocket en musica.interno.com"
echo "3. Multi-location en planilla.interno.com"
echo "4. API streaming en nucleoriofrio.com/api/chat/stream"
8. Tabla de Mapeo Completa
NPM → Traefik Equivalencias
| Característica NPM | Equivalente Traefik |
|---|---|
proxy_pass |
service.loadBalancer.server.url |
server_name |
router.rule=Host(...) |
location /path |
router.rule=PathPrefix(...) + priority |
proxy_set_header |
middleware.headers.customRequestHeaders |
add_header |
middleware.headers.customResponseHeaders |
auth_request |
middleware.forwardAuth |
ssl_certificate |
tls.certificates o tls.certResolver |
include force-ssl.conf |
entrypoint.http.redirections |
proxy_buffering off |
middleware.buffering |
client_max_body_size |
middleware.buffering.maxRequestBodyBytes |
proxy_read_timeout |
service.loadBalancer.responseForwarding.timeout |
upstream con múltiples IPs |
service.loadBalancer.servers (array) |
map para variables |
Middlewares condicionales o dynamic config |
if statements |
No soportado directamente, usar priorities |
limit_req |
middleware.rateLimit |
access_log |
accessLog en traefik.yml |
error_log |
log en traefik.yml |
9. Checklist de Migración
Antes de Migrar
- Backup completo de NPM configs
- Backup de base de datos de NPM
- Copiar certificados SSL
- Verificar que todos los backends están en red
principal - Documentar configuraciones custom
Durante la Migración
- Crear estructura de directorios de Traefik
- Configurar
traefik.yml - Crear middlewares comunes
- Crear configs dinámicas por servicio
- Actualizar docker-compose de cada stack con labels
- Detener NPM
- Levantar Traefik
- Verificar dashboard de Traefik
Después de Migrar
- Probar cada dominio manualmente
- Verificar certificados SSL
- Verificar autenticación Authentik
- Verificar WebSockets
- Verificar multi-location routing
- Verificar streaming endpoints
- Monitorear logs de Traefik
- Configurar alertas (opcional)
- Actualizar documentación
En Caso de Problemas
- Rollback a NPM
- Revisar logs de Traefik
- Verificar conectividad de red
- Verificar resolución DNS interna
- Verificar permisos de certificados