Armado de infraestructura local, para trabajar con docker en local.
Go to file
merios 33c368936e feat: infraestructura base de Traefik + generador de proyectos
- core/: reverse proxy Traefik v3 (HTTP 80 / HTTPS 443)
  - traefik.yml con provider Docker (exposedByDefault: false) y provider file
  - dynamic_tls.yml sanitizado con certificados .local comentados
  - certs/README.md con procedimiento de certificados auto-firmados
- init_project.sh: generador de proyectos base (SRV_DIR configurable)
  - plantilla compose con redireccion HTTP->HTTPS + Let's Encrypt
  - bloque alternativo comentado para dominios .local
- README.md: documentacion integral (puesta en marcha, HTTP/HTTPS,
  troubleshooting y seguridad)
- docs/arquitectura.md: guia de instalacion y gestion de URLs
- docs/http-https.md: referencia de etiquetas Traefik, TLS y certresolver
- .gitignore: excluye .env, acme.json, certificados, datos y respaldos
2026-10-15 11:34:39 -03:00
core feat: infraestructura base de Traefik + generador de proyectos 2026-10-15 11:34:39 -03:00
docs feat: infraestructura base de Traefik + generador de proyectos 2026-10-15 11:34:39 -03:00
.env.global feat: infraestructura base de Traefik + generador de proyectos 2026-10-15 11:34:39 -03:00
.gitignore feat: infraestructura base de Traefik + generador de proyectos 2026-10-15 11:34:39 -03:00
init_project.sh feat: infraestructura base de Traefik + generador de proyectos 2026-10-15 11:34:39 -03:00
README.md feat: infraestructura base de Traefik + generador de proyectos 2026-10-15 11:34:39 -03:00

TrafficServerLocal

Infraestructura base para alojar múltiples proyectos Docker detrás de un único proxy inverso Traefik v3, con soporte para HTTP y HTTPS:

  • Dominios públicos → certificados de Let's Encrypt (automático).
  • Dominios locales .local → certificados auto-firmados montados desde el filesystem.

Este repositorio contiene solo la infraestructura reutilizable. El código, las bases de datos y los datos persistentes de cada proyecto viven en ~/srv/projects/<proyecto>/ y se publican en sus propios repositorios.


1. Estructura

~/srv/
├── core/                         # Reverse proxy Traefik
│   ├── docker-compose.yml
│   └── data/
│       ├── traefik.yml           # Configuración estática
│       ├── dynamic_tls.yml       # Certificados estáticos (.local)
│       ├── acme.json             # Clave ACME (NO versionado)
│       └── certs/                # .crt / .key locales (NO versionados)
├── projects/                     # Un directorio por proyecto
│   └── <proyecto>/
│       ├── app/                  # Código fuente / Dockerfile
│       ├── data/{db,storage}/    # Persistencia
│       ├── .env                  # Secretos (chmod 600)
│       └── docker-compose.yml    # Etiquetas de Traefik
└── backups/                      # Respaldos (fuera de Git)

En este repositorio (lo publicable):

TrafficServerLocal/
├── README.md
├── .gitignore
├── .env.global
├── init_project.sh               # Generador de proyectos base
├── docs/
│   ├── arquitectura.md           # Guía de instalación y gestión de URLs
│   └── http-https.md             # Referencia de etiquetas HTTP/HTTPS y TLS
└── core/
    ├── docker-compose.yml
    └── data/
        ├── traefik.yml
        ├── dynamic_tls.yml
        └── certs/README.md       # Cómo generar certificados .local

2. Requisitos

  • Docker Engine y Docker Compose v2 (docker compose)

  • El usuario debe pertenecer al grupo docker:

    sudo usermod -aG docker "$USER"
    newgrp docker      # o cerrá y volvé a iniciar sesión
    
  • Puertos 80/443 libres en el host

  • Para HTTPS público: el dominio debe apuntar por DNS a la IP del servidor


3. Puesta en marcha

3.1 Estructura y red compartida

mkdir -p ~/srv/core ~/srv/projects
docker network create proxy_network     # red externa compartida

proxy_network es externa: la crea una vez y la reutilizan el proxy y todos los proyectos.

3.2 Almacenamiento de certificados ACME

mkdir -p ~/srv/core/data
touch ~/srv/core/data/acme.json
chmod 600 ~/srv/core/data/acme.json

acme.json contiene la clave privada de la cuenta de Let's Encrypt. Nunca se versiona (ya está en .gitignore).

3.3 Levantar el proxy

cd ~/srv/core
docker compose up -d
docker ps | grep core-proxy

Traefik queda escuchando en :80 (HTTP) y :443 (HTTPS).


4. Cómo funciona el proxy

Concepto Detalle
Provider Docker Traefik lee /var/run/docker.sock (solo lectura) y detecta contenedores con traefik.enable=true
exposedByDefault: false Solo se publican los contenedores etiquetados
Provider File Carga certificados estáticos desde dynamic_tls.yml con watch: true
proxy_network Red externa por la que Traefik alcanza a los servicios
Red default Red bridge aislada del proxy para bases de datos, colas, etc.
no-new-privileges Endurecimiento básico del contenedor del proxy
Dashboard Habilitado en api.dashboard (ver Seguridad)

Puertos expuestos por el proxy

ports:
  - "80:80"     # entrypoint: web      → HTTP
  - "443:443"   # entrypoint: websecure → HTTPS

5. Crear un proyecto base

cd ~/srv
./init_project.sh api-ventas api.midominio.com 80

Genera:

~/srv/projects/api-ventas/
├── app/                # Código fuente, repo o Dockerfile
├── data/
│   ├── db/             # Persistencia de base de datos
│   └── storage/        # Archivos del usuario
├── .env                # Variables (chmod 600)
└── docker-compose.yml  # Conectado a proxy_network

El directorio del proyecto queda con permisos 700.

Pasos siguientes

# 1. Colocar el código
git clone https://github.com/tu-usuario/api-ventas.git \
  ~/srv/projects/api-ventas/app/
# o bien:
cp -r /ruta/a/tus/archivos/* ~/srv/projects/api-ventas/app/

# 2. Ajustar docker-compose.yml
#    - Opción A: descomentar  build: { context: ./app, dockerfile: Dockerfile }
#    - Opción B: cambiar   image: api-ventas:latest  por tu imagen
#    - Servicios extra (Postgres, Redis) → red `default` (aislados del proxy)

# 3. Levantar
cd ~/srv/projects/api-ventas
docker compose up -d

Variables generadas en .env

APP_NAME=api-ventas
APP_DOMAIN=api.midominio.com
PORT=80

Agregá ahí tus credenciales. Nunca subas ese archivo a Git.


6. HTTP / HTTPS

Detalle completo en docs/http-https.md.

A) Dominio público + Let's Encrypt

labels:
  - "traefik.enable=true"
  - "traefik.docker.network=proxy_network"

  # Middleware de redirección HTTP → HTTPS
  - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
  - "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true"

  # Router HTTP (redirige)
  - "traefik.http.routers.miapp-http.rule=Host(`api.midominio.com`)"
  - "traefik.http.routers.miapp-http.entrypoints=web"
  - "traefik.http.routers.miapp-http.middlewares=redirect-to-https"

  # Router HTTPS (emite el certificado)
  - "traefik.http.routers.miapp-https.rule=Host(`api.midominio.com`)"
  - "traefik.http.routers.miapp-https.entrypoints=websecure"
  - "traefik.http.routers.miapp-https.tls=true"
  - "traefik.http.routers.miapp-https.tls.certresolver=letsencrypt"

  # Servicio interno
  - "traefik.http.services.miapp.loadbalancer.server.port=80"

B) Dominio local .local + certificado auto-firmado

  1. Generar el certificado:

    cd ~/srv/core/data/certs
    openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
      -keyout miapp.local.key -out miapp.local.crt \
      -subj "/CN=miapp.local" -addext "subjectAltName=DNS:miapp.local"
    
  2. Declararlo en core/data/dynamic_tls.yml:

    tls:
      certificates:
        - certFile: /etc/traefik/certs/miapp.local.crt
          keyFile:  /etc/traefik/certs/miapp.local.key
    
  3. Etiquetar el proyecto sin certresolver:

    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy_network"
      - "traefik.http.routers.miapp.rule=Host(`miapp.local`)"
      - "traefik.http.routers.miapp.entrypoints=websecure"
      - "traefik.http.routers.miapp.tls=true"
      - "traefik.http.services.miapp.loadbalancer.server.port=80"
    
  4. Reiniciar el proxy y resolver el nombre en el cliente:

    cd ~/srv/core && docker compose restart traefik
    echo "192.168.1.50  miapp.local" | sudo tee -a /etc/hosts
    

Instrucciones completas (SAN múltiple, instalación del CA en cada SO) en core/data/certs/README.md.

C) Sin redirección (solo HTTP)

Para entornos internos donde no interese HTTPS:

labels:
  - "traefik.enable=true"
  - "traefik.docker.network=proxy_network"
  - "traefik.http.routers.miapp.rule=Host(`miapp.local`)"
  - "traefik.http.routers.miapp.entrypoints=web"
  - "traefik.http.services.miapp.loadbalancer.server.port=80"

7. Comandos útiles

# Estado y logs del proxy
docker ps | grep core-proxy
docker logs -f core-proxy

# Recargar tras editar dynamic_tls.yml
cd ~/srv/core && docker compose restart traefik

# Proyecto: ciclo de vida
cd ~/srv/projects/miapp
docker compose up -d          # levantar
docker compose up -d --build  # reconstruir imagen
docker compose restart        # reiniciar
docker compose down           # detener (conserva datos)
docker compose logs -f web

# Entrar al contenedor
docker exec -it miapp-web bash

# Ver redes de un contenedor
docker network inspect proxy_network --format '{{.Containers}}'

8. Ejemplo real: proyecto Laravel + PostgreSQL

services:
  web:
    build:
      context: ./app
      dockerfile: Dockerfile
    container_name: optica-web
    restart: unless-stopped
    env_file: [ .env ]
    volumes:
      - ./app/src:/var/www/optica
      - ./data/storage:/var/www/optica/storage
    networks: [ default, proxy_network ]
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy_network"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      - "traefik.http.routers.optica-http.rule=Host(`optica.local`)"
      - "traefik.http.routers.optica-http.entrypoints=web"
      - "traefik.http.routers.optica-http.middlewares=redirect-to-https"
      - "traefik.http.routers.optica-https.rule=Host(`optica.local`)"
      - "traefik.http.routers.optica-https.entrypoints=websecure"
      - "traefik.http.routers.optica-https.tls=true"
      - "traefik.http.routers.optica-https.service=optica-svc"
      - "traefik.http.services.optica-svc.loadbalancer.server.port=80"

  pg:
    image: postgres:14
    container_name: optica-pg
    networks: [ default ]          # ← aislada: no accesible desde el proxy
    environment:
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: ${DB_NAME}
    volumes: [ ./data/db:/var/lib/postgresql ]

networks:
  default:
    driver: bridge
  proxy_network:
    external: true

⚠️ Mové las credenciales de environment: al .env del proyecto. Las contraseñas escritas directamente en el docker-compose.yml terminan en el historial de Git.


9. Solución de problemas

Síntoma Causa probable Solución
404 Not Found Contenedor sin traefik.enable=true o fuera de proxy_network docker network inspect proxy_network
El router no aparece en el dashboard Servicio sin traefik.enable o red incorrecta Verificar etiquetas y redes del compose
502 Bad Gateway El servicio interno no escucha en el puerto declarado docker exec -it <cont> sh -c 'netstat -tlnp'
Certificado no se emite (Let's Encrypt) DNS no apunta al server, o 80/443 bloqueados dig +short dominio, revisar firewall
too many certificates / rate limit Se emitted de más en staging/producción Revisar acme.json y esperar la ventana de rate limit
port is already allocated Otro servicio ocupa 80/443 docker ps --filter publish=80
permission denied en Docker socket Usuario fuera del grupo docker sudo usermod -aG docker $USER y re-loguear
El .crt local no se aplica Ruta incorrecta en dynamic_tls.yml Usar la ruta dentro del contenedor (/etc/traefik/certs/...) y reiniciar
no such file or directory: /acme.json Falta el archivo o permisos touch acme.json && chmod 600 acme.json
Cambios en traefik.yml sin efecto El archivo está montado :ro docker compose restart traefik

Diagnóstico rápido:

docker logs -f core-proxy | grep -iE "error|warn"
curl -sI http://miapp.local            # debería devolver 301 → https
curl -sk https://miapp.local -I        # -k ignora el certificado auto-firmado

10. Seguridad

  • Nunca subas: .env, acme.json, *.key, *.crt, contraseñas de bases de datos, dumps SQL ni respaldos. El .gitignore de este repo lo cubre.
  • Los .env por proyecto se crean con chmod 600.
  • docker.sock se monta solo lectura (ro) en Traefik.
  • El dashboard está habilitado. En producción:
    • agregá basicAuth / forwardAuth a las etiquetas del dashboard, o
    • deshabilitalo con api.dashboard: false.
  • Traefik corre con no-new-privileges:true.
  • acme.json debe quedar en 600 o el proxy no managea los certificados.
  • Restringí los puertos publicados de las bases de datos (ports:) a 127.0.0.1 si solo los necesitás desde el host:
    ports: ["127.0.0.1:5432:5432"]
    

11. Publicación

Este repositorio es la capa de infraestructura. Cada proyecto se mantiene en su propio repositorio y se despliega en ~/srv/projects/<proyecto>/ siguiendo los pasos de este README.

cd ~/srv/core && docker compose pull && docker compose up -d   # actualizar proxy

Licencia

MIT — ver el repositorio para detalles.