compose.yaml
# Portal de actualizaciones de los nodos: Semaphore UI en el master-1, FUERA de k3s
# (si el clúster falla, el portal que lo arregla tiene que seguir funcionando).
# Se gestiona con scripts/portal.sh; no lanzar compose a mano.
name: portal-nodos
services:
semaphore:
build: .
image: portal-semaphore:2.19.16
restart: unless-stopped
# Red del host: los nodos solo aceptan la clave del usuario `ansible` desde
# 192.0.2.112. La web escucha SOLO en la IP de Tailscale del master-1.
network_mode: host
read_only: true
cap_drop: [ALL]
security_opt: [no-new-privileges:true]
tmpfs:
- /tmp:exec,mode=1777
env_file:
- /root/.config/semaphore/semaphore.env # secretos (0600), lo crea portal.sh
environment:
SEMAPHORE_DB_DIALECT: sqlite
SEMAPHORE_INTERFACE: ${PORTAL_IP:-203.0.113.121}
SEMAPHORE_PORT: ${PORTAL_PUERTO:-3010}
SEMAPHORE_TMP_PATH: /tmp/semaphore
SEMAPHORE_TOTP_ENABLED: "true"
SEMAPHORE_TOTP_ISSUER: Portal de nodos
SEMAPHORE_SCHEDULE_TIMEZONE: Europe/Madrid
SEMAPHORE_MAX_PARALLEL_TASKS: "1" # nunca dos actualizaciones a la vez
SEMAPHORE_NON_ADMIN_CAN_CREATE_PROJECT: "false"
SEMAPHORE_FORWARDED_ENV_VARS: '["TZ"]'
TZ: Europe/Madrid
volumes:
- datos:/var/lib/semaphore
- config:/etc/semaphore
- /opt/portal-nodos/repo:/portal/repo:ro # copia de main (portal.sh repo)
- /root/.config/semaphore/claves:/claves:ro # clave SSH, known_hosts, kubeconfig
volumes:
datos:
config: