Files
Portainer/NUEVA-APP.md
T
Eduardo David Paredes Vara 5e220591e7 chore: actualizar meta del repo y configuración base
- .gitignore: añadir reglas para .env, *.live.yaml, .codex, .vscode, certs
- README.md y COOLIFY-TEMPLATE.md: actualizados con nuevos stacks
- NUEVA-APP.md: guía para añadir nuevas apps al homelab
- PROMPT-SSO-ISSUE.md: contexto de debugging SSO/Authentik
- .github/copilot-instructions.md: instrucciones de arquitectura para Copilot
- docker-compose.yml raíz: ajustes al stack de Portainer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 05:47:11 +00:00

6.6 KiB

Checklist: Añadir una nueva app

Cada vez que levantes un nuevo servicio que deba ser accesible desde internet, sigue estos pasos.


1. Registrar el dominio (DNS + Tunnel)

cd cloudflared
./add-domain.sh <subdominio>

Esto hace en un solo comando:

  • Crea el registro DNS <subdominio>.sherlockhomeless.net → Cloudflare Tunnel (CNAME con proxy naranja)
  • Añade el hostname al ingress del tunnel → http://traefik:80

Espera ~30 segundos para que Cloudflare propague el DNS.


2. Labels de Traefik en el docker-compose.yml

Añade estas labels al servicio que quieres exponer. Sustituye miapp por el nombre corto del servicio y ${MI_DOMINIO} por la variable de entorno del dominio (ej. ${MIAPP_DOMAIN}).

Caso A — App pública con CrowdSec y sin login (APIs, webhooks)

labels:
  traefik.enable: "true"
  traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
  traefik.http.routers.miapp.rule: "Host(`${MI_DOMINIO}`)"
  traefik.http.routers.miapp.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
  traefik.http.routers.miapp.tls: "true"
  traefik.http.routers.miapp.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
  traefik.http.routers.miapp.middlewares: "crowdsec-bouncer@file"
  traefik.http.services.miapp.loadbalancer.server.port: "XXXX"

Caso B — App privada con CrowdSec + Authentik SSO (panel de admin, dashboards)

labels:
  traefik.enable: "true"
  traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
  traefik.http.routers.miapp.rule: "Host(`${MI_DOMINIO}`)"
  traefik.http.routers.miapp.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
  traefik.http.routers.miapp.tls: "true"
  traefik.http.routers.miapp.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
  traefik.http.routers.miapp.middlewares: "crowdsec-bouncer@file,${TRAEFIK_AUTH_MIDDLEWARE}"
  traefik.http.services.miapp.loadbalancer.server.port: "XXXX"

El orden importa: CrowdSec bloquea primero, luego Authentik autentica. ${TRAEFIK_AUTH_MIDDLEWARE} se resuelve a ths-authentik@docker.

Caso C — App con su propio login (no necesita Authentik)

labels:
  traefik.enable: "true"
  traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
  traefik.http.routers.miapp.rule: "Host(`${MI_DOMINIO}`)"
  traefik.http.routers.miapp.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
  traefik.http.routers.miapp.tls: "true"
  traefik.http.routers.miapp.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
  traefik.http.routers.miapp.middlewares: "crowdsec-bouncer@file"
  traefik.http.services.miapp.loadbalancer.server.port: "XXXX"

Igual que el Caso A — CrowdSec siempre, Authentik solo cuando la app no tiene auth propia.


3. Variables de entorno obligatorias en stack.env / .env

Incluye siempre estas variables en el stack.env de la nueva app para que las labels funcionen:

##### Traefik / dominios #####
TRAEFIK_DOCKER_NETWORK=proxy
MIAPP_DOMAIN=miapp.sherlockhomeless.net
TRAEFIK_ENTRYPOINT_SECURE=websecure
TRAEFIK_CERTRESOLVER=letsencrypt
TRAEFIK_AUTH_MIDDLEWARE=ths-authentik@docker    # solo si usas Caso B

Gotcha conocido: Traefik NO expande variables de entorno dentro de labels. Los valores como puertos y dominios que van dentro de Host(...) deben estar ya resueltos cuando docker compose procesa el archivo. Usa variables de entorno solo donde docker compose las expande (fuera de las comillas de las labels si es un valor directo).


4. Configurar Authentik (solo Caso B)

Si usas ths-authentik@docker, Authentik debe conocer la app para que el outpost la sirva correctamente.

En el panel de Authentik (https://auth.sherlockhomeless.net):

  1. Crear Provider → Applications → Providers → Create

    • Tipo: Proxy Provider
    • Name: Mi App
    • External host: https://miapp.sherlockhomeless.net
    • Authentication flow: default-authentication-flow
    • Authorization flow: default-provider-authorization-implicit-consent
    • Mode: Forward auth (single application)
  2. Crear Application → Applications → Create

    • Name: Mi App
    • Slug: miapp
    • Provider: el creado en el paso anterior
  3. Asociar al Embedded Outpost → Outposts → authentik Embedded Outpost → Edit

    • Añade la nueva aplicación a la lista de aplicaciones seleccionadas
    • Guarda

Si no completas el paso 3, Authentik redirige al login pero luego da error 404.


5. Red Docker

El contenedor debe estar en la red proxy para que Traefik lo pueda alcanzar:

networks:
  proxy:
    external: true
  # red interna opcional para bases de datos, etc.
  miapp_internal:
    driver: bridge

Y el servicio que expone la app debe tener:

services:
  miapp:
    networks:
      - proxy
      - miapp_internal   # si tiene db

Los contenedores de base de datos u otros servicios internos solo necesitan la red interna, no la red proxy.


6. Desplegar

cd miapp
docker compose --env-file .env up -d

Resumen visual

Nueva app
   │
   ├── 1. ./cloudflared/add-domain.sh <sub>     → DNS + Tunnel
   │
   ├── 2. Labels en docker-compose.yml
   │       ├── Caso A (pública)    → crowdsec-bouncer@file
   │       ├── Caso B (privada)    → crowdsec-bouncer@file + ths-authentik@docker
   │       └── Caso C (auth propia)→ crowdsec-bouncer@file
   │
   ├── 3. Variables en stack.env / .env
   │
   ├── 4. Authentik Provider + App + Outpost    (solo Caso B)
   │
   ├── 5. Red proxy en docker-compose.yml
   │
   └── 6. docker compose --env-file .env up -d

Middlewares de referencia

Middleware Definido en Qué hace
crowdsec-bouncer@file /opt/traefik/dynamic/crowdsec-bouncer.yml Bloquea IPs baneadas por CrowdSec y filtra con AppSec WAF
ths-authentik@docker Labels del stack authentik ForwardAuth hacia Authentik — requiere login SSO

Gotchas conocidos

# Problema Causa Solución
1 Traefik no enruta la app traefik.enable: "true" falta Añadir el label
2 Traefik no encuentra el contenedor Contenedor no está en la red proxy Añadir red proxy al servicio
3 SSL no funciona tls.certresolver mal escrito o dominio no existe en DNS Verificar DNS con dig miapp.sherlockhomeless.net
4 Authentik redirige pero da 404 App no añadida al Embedded Outpost Paso 4.3
5 CrowdSec da 403 IP baneada docker exec crowdsec cscli decisions list — si apareces, cscli decisions delete --ip <tu-ip>
6 Variable no expandida en label Traefik no expande vars en labels de docker Hardcodear el valor o usar la variable en stack.env