refactor(compose): protect services and centralize routing
This commit is contained in:
@@ -48,6 +48,10 @@ services:
|
|||||||
- "--experimental.plugins.crowdsec-bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
- "--experimental.plugins.crowdsec-bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||||
- "--experimental.plugins.crowdsec-bouncer.version=v1.6.0"
|
- "--experimental.plugins.crowdsec-bouncer.version=v1.6.0"
|
||||||
|
|
||||||
|
# Plugin Sablier (scale-to-zero on demand)
|
||||||
|
- "--experimental.plugins.sablier.modulename=github.com/acouvreur/sablier"
|
||||||
|
- "--experimental.plugins.sablier.version=v1.8.0"
|
||||||
|
|
||||||
# ACME / Let's Encrypt (resolver "letsencrypt")
|
# ACME / Let's Encrypt (resolver "letsencrypt")
|
||||||
- "--certificatesresolvers.letsencrypt.acme.email=${TRAEFIK_ACME_EMAIL}"
|
- "--certificatesresolvers.letsencrypt.acme.email=${TRAEFIK_ACME_EMAIL}"
|
||||||
- "--certificatesresolvers.letsencrypt.acme.storage=${TRAEFIK_ACME_STORAGE}"
|
- "--certificatesresolvers.letsencrypt.acme.storage=${TRAEFIK_ACME_STORAGE}"
|
||||||
@@ -63,6 +67,7 @@ services:
|
|||||||
- ${TRAEFIK_DYNAMIC_DIR}:/dynamic:Z
|
- ${TRAEFIK_DYNAMIC_DIR}:/dynamic:Z
|
||||||
- ${TRAEFIK_LETSENCRYPT_DIR}:/letsencrypt:Z
|
- ${TRAEFIK_LETSENCRYPT_DIR}:/letsencrypt:Z
|
||||||
- ${TRAEFIK_LOG_DIR}:/var/log/traefik:Z
|
- ${TRAEFIK_LOG_DIR}:/var/log/traefik:Z
|
||||||
|
- /opt/traefik/plugins-storage:/plugins-storage:Z
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ services:
|
|||||||
adguardhome:
|
adguardhome:
|
||||||
image: ${ADGUARD_IMAGE}
|
image: ${ADGUARD_IMAGE}
|
||||||
container_name: adguardhome
|
container_name: adguardhome
|
||||||
pull_policy: always
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ services:
|
|||||||
ths-authentik-server:
|
ths-authentik-server:
|
||||||
image: ${AUTHENTIK_IMAGE}
|
image: ${AUTHENTIK_IMAGE}
|
||||||
container_name: ths-authentik-server
|
container_name: ths-authentik-server
|
||||||
pull_policy: always
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: ["server"]
|
command: ["server"]
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
beszel:
|
beszel:
|
||||||
image: ${BESZEL_IMAGE}
|
image: ${BESZEL_IMAGE}
|
||||||
pull_policy: always
|
|
||||||
container_name: beszel
|
container_name: beszel
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
@@ -31,7 +30,6 @@ services:
|
|||||||
|
|
||||||
beszel-agent:
|
beszel-agent:
|
||||||
image: ${BESZEL_AGENT_IMAGE}
|
image: ${BESZEL_AGENT_IMAGE}
|
||||||
pull_policy: always
|
|
||||||
container_name: beszel-agent
|
container_name: beszel-agent
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
network_mode: host
|
network_mode: host
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ services:
|
|||||||
image: ${BOOKSTACK_DB_IMAGE}
|
image: ${BOOKSTACK_DB_IMAGE}
|
||||||
container_name: bookstack-db
|
container_name: bookstack-db
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: ${BOOKSTACK_DB_ROOT_PASSWORD}
|
MYSQL_ROOT_PASSWORD: ${BOOKSTACK_DB_ROOT_PASSWORD}
|
||||||
MYSQL_DATABASE: ${BOOKSTACK_DB_NAME}
|
MYSQL_DATABASE: ${BOOKSTACK_DB_NAME}
|
||||||
@@ -45,14 +47,9 @@ services:
|
|||||||
- proxy
|
- proxy
|
||||||
- mail_internal
|
- mail_internal
|
||||||
labels:
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
traefik.enable: "true"
|
traefik.enable: "true"
|
||||||
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
||||||
traefik.http.routers.bookstack.rule: "Host(`${BOOKSTACK_DOMAIN}`)"
|
|
||||||
traefik.http.routers.bookstack.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
|
|
||||||
traefik.http.routers.bookstack.tls: "true"
|
|
||||||
traefik.http.routers.bookstack.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
|
|
||||||
traefik.http.routers.bookstack.middlewares: "crowdsec-bouncer@file,${TRAEFIK_AUTH_MIDDLEWARE}"
|
|
||||||
traefik.http.services.bookstack.loadbalancer.server.port: "80"
|
|
||||||
homepage.group: "Productividad"
|
homepage.group: "Productividad"
|
||||||
homepage.name: "BookStack"
|
homepage.name: "BookStack"
|
||||||
homepage.icon: "bookstack.png"
|
homepage.icon: "bookstack.png"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
crowdsec:
|
crowdsec:
|
||||||
image: ${CROWDSEC_IMAGE}
|
image: ${CROWDSEC_IMAGE}
|
||||||
pull_policy: always
|
|
||||||
container_name: crowdsec
|
container_name: crowdsec
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
security_opt:
|
security_opt:
|
||||||
@@ -59,7 +58,7 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
crowdsec-prometheus:
|
crowdsec-prometheus:
|
||||||
image: prom/prometheus:latest
|
image: prom/prometheus@sha256:e4254400b85610324913f0dc4acf92603d9984e7519414c5a12811aa6146acc3
|
||||||
container_name: crowdsec-prometheus
|
container_name: crowdsec-prometheus
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command:
|
command:
|
||||||
|
|||||||
+1
-2
@@ -16,8 +16,7 @@ echo "=== CrowdSec Setup ==="
|
|||||||
# 1. Cargar .env
|
# 1. Cargar .env
|
||||||
if [ ! -f "${ENV_FILE}" ]; then
|
if [ ! -f "${ENV_FILE}" ]; then
|
||||||
echo "ERROR: No se encontro ${ENV_FILE}"
|
echo "ERROR: No se encontro ${ENV_FILE}"
|
||||||
echo "Copia stack.env a .env y editalo:"
|
echo "Crea ${ENV_FILE} con las variables reales y editalo:"
|
||||||
echo " cp ${SCRIPT_DIR}/stack.env ${ENV_FILE}"
|
|
||||||
echo " nano ${ENV_FILE}"
|
echo " nano ${ENV_FILE}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
dozzle:
|
dozzle:
|
||||||
image: ${DOZZLE_IMAGE}
|
image: ${DOZZLE_IMAGE}
|
||||||
pull_policy: always
|
|
||||||
container_name: dozzle
|
container_name: dozzle
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
@@ -15,14 +14,9 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
labels:
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
traefik.enable: "true"
|
traefik.enable: "true"
|
||||||
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
||||||
traefik.http.routers.dozzle.rule: "Host(`${DOZZLE_DOMAIN}`)"
|
|
||||||
traefik.http.routers.dozzle.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
|
|
||||||
traefik.http.routers.dozzle.tls: "true"
|
|
||||||
traefik.http.routers.dozzle.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
|
|
||||||
traefik.http.routers.dozzle.middlewares: "crowdsec-bouncer@file,${TRAEFIK_AUTH_MIDDLEWARE}"
|
|
||||||
traefik.http.services.dozzle.loadbalancer.server.port: "8080"
|
|
||||||
homepage.group: "Observabilidad"
|
homepage.group: "Observabilidad"
|
||||||
homepage.name: "Dozzle"
|
homepage.name: "Dozzle"
|
||||||
homepage.icon: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/dozzle.svg"
|
homepage.icon: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/dozzle.svg"
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ services:
|
|||||||
image: ${GITEA_POSTGRES_IMAGE}
|
image: ${GITEA_POSTGRES_IMAGE}
|
||||||
container_name: gitea-postgres
|
container_name: gitea-postgres
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: ${GITEA_DB_NAME}
|
POSTGRES_DB: ${GITEA_DB_NAME}
|
||||||
POSTGRES_USER: ${GITEA_DB_USER}
|
POSTGRES_USER: ${GITEA_DB_USER}
|
||||||
@@ -15,7 +17,6 @@ services:
|
|||||||
|
|
||||||
gitea:
|
gitea:
|
||||||
image: ${GITEA_IMAGE}
|
image: ${GITEA_IMAGE}
|
||||||
pull_policy: always
|
|
||||||
container_name: gitea
|
container_name: gitea
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -79,19 +80,16 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "${GITEA_SSH_PORT}:${GITEA_SSH_PORT}"
|
- "${GITEA_SSH_PORT}:${GITEA_SSH_PORT}"
|
||||||
labels:
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
traefik.enable: "true"
|
traefik.enable: "true"
|
||||||
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
||||||
traefik.http.routers.gitea.rule: "Host(`${GITEA_DOMAIN}`)"
|
|
||||||
traefik.http.routers.gitea.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
|
|
||||||
traefik.http.routers.gitea.tls: "true"
|
|
||||||
traefik.http.routers.gitea.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
|
|
||||||
traefik.http.routers.gitea.middlewares: "crowdsec-bouncer@file"
|
|
||||||
traefik.http.services.gitea.loadbalancer.server.port: "3000"
|
|
||||||
|
|
||||||
gitea-runner:
|
gitea-runner:
|
||||||
image: ${GITEA_RUNNER_IMAGE}
|
image: ${GITEA_RUNNER_IMAGE}
|
||||||
container_name: gitea-act-runner
|
container_name: gitea-act-runner
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
depends_on:
|
depends_on:
|
||||||
- gitea
|
- gitea
|
||||||
environment:
|
environment:
|
||||||
@@ -107,6 +105,8 @@ services:
|
|||||||
|
|
||||||
networks:
|
networks:
|
||||||
gitea:
|
gitea:
|
||||||
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
driver: bridge
|
driver: bridge
|
||||||
mail_internal:
|
mail_internal:
|
||||||
external: true
|
external: true
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
services:
|
services:
|
||||||
guacd:
|
guacd:
|
||||||
image: ${GUACD_IMAGE}
|
image: ${GUACD_IMAGE}
|
||||||
pull_policy: always
|
|
||||||
container_name: guacd
|
container_name: guacd
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:10.0.4.1"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "nc -z 127.0.0.1 4822 || exit 1"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 5s
|
||||||
networks:
|
networks:
|
||||||
- guacamole
|
- guacamole
|
||||||
|
|
||||||
@@ -13,6 +20,8 @@ services:
|
|||||||
image: ${GUACAMOLE_POSTGRES_IMAGE}
|
image: ${GUACAMOLE_POSTGRES_IMAGE}
|
||||||
container_name: guacamole-postgres
|
container_name: guacamole-postgres
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: ${GUACAMOLE_DB_NAME}
|
POSTGRES_DB: ${GUACAMOLE_DB_NAME}
|
||||||
POSTGRES_USER: ${GUACAMOLE_DB_USER}
|
POSTGRES_USER: ${GUACAMOLE_DB_USER}
|
||||||
@@ -26,7 +35,6 @@ services:
|
|||||||
|
|
||||||
guacamole:
|
guacamole:
|
||||||
image: ${GUACAMOLE_IMAGE}
|
image: ${GUACAMOLE_IMAGE}
|
||||||
pull_policy: always
|
|
||||||
container_name: guacamole
|
container_name: guacamole
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -41,19 +49,12 @@ services:
|
|||||||
POSTGRESQL_USERNAME: ${GUACAMOLE_DB_USER}
|
POSTGRESQL_USERNAME: ${GUACAMOLE_DB_USER}
|
||||||
POSTGRESQL_PASSWORD: ${GUACAMOLE_DB_PASSWORD}
|
POSTGRESQL_PASSWORD: ${GUACAMOLE_DB_PASSWORD}
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:10.0.4.1"
|
||||||
networks:
|
networks:
|
||||||
- guacamole
|
- guacamole
|
||||||
- proxy
|
- proxy
|
||||||
labels:
|
labels:
|
||||||
traefik.enable: "true"
|
prune.protect: "true"
|
||||||
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
|
||||||
traefik.http.routers.guacamole.rule: "Host(`${GUACAMOLE_DOMAIN}`)"
|
|
||||||
traefik.http.routers.guacamole.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
|
|
||||||
traefik.http.routers.guacamole.tls: "true"
|
|
||||||
traefik.http.routers.guacamole.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
|
|
||||||
traefik.http.routers.guacamole.middlewares: "crowdsec-bouncer@file,${TRAEFIK_AUTH_MIDDLEWARE}"
|
|
||||||
traefik.http.services.guacamole.loadbalancer.server.port: "8080"
|
|
||||||
homepage.group: "Infraestructura"
|
homepage.group: "Infraestructura"
|
||||||
homepage.name: "Remote Desktop"
|
homepage.name: "Remote Desktop"
|
||||||
homepage.icon: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/apache-guacamole.svg"
|
homepage.icon: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/apache-guacamole.svg"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
homepage:
|
homepage:
|
||||||
image: ${HOMEPAGE_IMAGE}
|
image: ${HOMEPAGE_IMAGE}
|
||||||
pull_policy: always
|
|
||||||
container_name: homepage
|
container_name: homepage
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
karakeep:
|
karakeep:
|
||||||
image: ghcr.io/karakeep-app/karakeep:${KARAKEEP_VERSION}
|
image: ghcr.io/karakeep-app/karakeep:${KARAKEEP_VERSION}
|
||||||
pull_policy: always
|
|
||||||
container_name: karakeep
|
container_name: karakeep
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
@@ -42,17 +41,16 @@ services:
|
|||||||
- karakeep_internal
|
- karakeep_internal
|
||||||
- proxy
|
- proxy
|
||||||
labels:
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
traefik.enable: "true"
|
traefik.enable: "true"
|
||||||
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
||||||
traefik.http.routers.karakeep.rule: "Host(`${KARAKEEP_DOMAIN}`)"
|
|
||||||
traefik.http.routers.karakeep.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
|
|
||||||
traefik.http.routers.karakeep.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
|
|
||||||
traefik.http.services.karakeep.loadbalancer.server.port: "3000"
|
|
||||||
|
|
||||||
karakeep-chrome:
|
karakeep-chrome:
|
||||||
image: gcr.io/zenika-hub/alpine-chrome:124
|
image: gcr.io/zenika-hub/alpine-chrome:124
|
||||||
container_name: karakeep-chrome
|
container_name: karakeep-chrome
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
command:
|
command:
|
||||||
- --no-sandbox
|
- --no-sandbox
|
||||||
- --disable-gpu
|
- --disable-gpu
|
||||||
@@ -67,6 +65,8 @@ services:
|
|||||||
image: getmeili/meilisearch:v1.37.0
|
image: getmeili/meilisearch:v1.37.0
|
||||||
container_name: karakeep-meilisearch
|
container_name: karakeep-meilisearch
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
environment:
|
environment:
|
||||||
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
|
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
|
||||||
MEILI_NO_ANALYTICS: "true"
|
MEILI_NO_ANALYTICS: "true"
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ services:
|
|||||||
mail-relay:
|
mail-relay:
|
||||||
image: ${MAIL_RELAY_IMAGE}
|
image: ${MAIL_RELAY_IMAGE}
|
||||||
container_name: mail-relay
|
container_name: mail-relay
|
||||||
pull_policy: always
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
TZ: ${TZ}
|
TZ: ${TZ}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ networks:
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
flaresolverr:
|
flaresolverr:
|
||||||
image: ghcr.io/flaresolverr/flaresolverr:latest
|
image: ghcr.io/flaresolverr/flaresolverr@sha256:b91dbd42a716ec5334a3e348ef835759f96d4082f29d6f2ab4542353d3a08aef
|
||||||
container_name: flaresolverr
|
container_name: flaresolverr
|
||||||
environment:
|
environment:
|
||||||
- LOG_LEVEL=${LOG_LEVEL:-info}
|
- LOG_LEVEL=${LOG_LEVEL:-info}
|
||||||
@@ -19,9 +19,8 @@ services:
|
|||||||
shm_size: "1gb"
|
shm_size: "1gb"
|
||||||
|
|
||||||
prowlarr:
|
prowlarr:
|
||||||
image: lscr.io/linuxserver/prowlarr:latest
|
image: lscr.io/linuxserver/prowlarr@sha256:df25cfc91278e78abdf33623f7ee341b3b476118af68a2434cd8f8df49e6f986
|
||||||
container_name: prowlarr
|
container_name: prowlarr
|
||||||
pull_policy: always
|
|
||||||
environment:
|
environment:
|
||||||
- PUID=0
|
- PUID=0
|
||||||
- PGID=0
|
- PGID=0
|
||||||
@@ -36,9 +35,8 @@ services:
|
|||||||
- traefik.http.services.prowlarr.loadbalancer.server.port=9696
|
- traefik.http.services.prowlarr.loadbalancer.server.port=9696
|
||||||
|
|
||||||
jackett:
|
jackett:
|
||||||
image: lscr.io/linuxserver/jackett:latest
|
image: lscr.io/linuxserver/jackett@sha256:520d1a04f7aba6c71eb607eea1ca806eb4e2e7f0861ee79624dc716c423e8573
|
||||||
container_name: jackett
|
container_name: jackett
|
||||||
pull_policy: always
|
|
||||||
environment:
|
environment:
|
||||||
- PUID=0
|
- PUID=0
|
||||||
- PGID=0
|
- PGID=0
|
||||||
@@ -53,9 +51,8 @@ services:
|
|||||||
- traefik.http.services.jackett.loadbalancer.server.port=9117
|
- traefik.http.services.jackett.loadbalancer.server.port=9117
|
||||||
|
|
||||||
sonarr:
|
sonarr:
|
||||||
image: lscr.io/linuxserver/sonarr:latest
|
image: lscr.io/linuxserver/sonarr@sha256:9272af4955c92cccc39bc75dcad2f0758bc0ab9353dc61a8efaf35501d9ad60d
|
||||||
container_name: sonarr
|
container_name: sonarr
|
||||||
pull_policy: always
|
|
||||||
environment:
|
environment:
|
||||||
- PUID=0
|
- PUID=0
|
||||||
- PGID=0
|
- PGID=0
|
||||||
@@ -72,9 +69,8 @@ services:
|
|||||||
- traefik.http.services.sonarr.loadbalancer.server.port=8989
|
- traefik.http.services.sonarr.loadbalancer.server.port=8989
|
||||||
|
|
||||||
radarr:
|
radarr:
|
||||||
image: lscr.io/linuxserver/radarr:latest
|
image: lscr.io/linuxserver/radarr@sha256:3d851f850263ac1cbb5b2c78b20123a224547d819c2f4ba5a0a8cacadbf97038
|
||||||
container_name: radarr
|
container_name: radarr
|
||||||
pull_policy: always
|
|
||||||
environment:
|
environment:
|
||||||
- PUID=0
|
- PUID=0
|
||||||
- PGID=0
|
- PGID=0
|
||||||
@@ -91,9 +87,8 @@ services:
|
|||||||
- traefik.http.services.radarr.loadbalancer.server.port=7878
|
- traefik.http.services.radarr.loadbalancer.server.port=7878
|
||||||
|
|
||||||
jellyseerr:
|
jellyseerr:
|
||||||
image: fallenbagel/jellyseerr:latest
|
image: fallenbagel/jellyseerr@sha256:499fbe958fa4a0c93172a231c14898986556bb28bd5cd569a0f41dbf887b04d7
|
||||||
container_name: jellyseerr
|
container_name: jellyseerr
|
||||||
pull_policy: always
|
|
||||||
environment:
|
environment:
|
||||||
- LOG_LEVEL=debug
|
- LOG_LEVEL=debug
|
||||||
- TZ=${TZ:-Europe/Madrid}
|
- TZ=${TZ:-Europe/Madrid}
|
||||||
@@ -108,9 +103,8 @@ services:
|
|||||||
|
|
||||||
# Opcional: Jellyfin en VPS (sin GPU)
|
# Opcional: Jellyfin en VPS (sin GPU)
|
||||||
jellyfin:
|
jellyfin:
|
||||||
image: lscr.io/linuxserver/jellyfin:latest
|
image: lscr.io/linuxserver/jellyfin@sha256:b46e291fb2a532eef9e2e1b7d3b927ab39558f0c58964a63f21013071f1ae278
|
||||||
container_name: jellyfin-vps
|
container_name: jellyfin-vps
|
||||||
pull_policy: always
|
|
||||||
environment:
|
environment:
|
||||||
- PUID=0
|
- PUID=0
|
||||||
- PGID=0
|
- PGID=0
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ services:
|
|||||||
nextcloud:
|
nextcloud:
|
||||||
image: nextcloud:33-apache
|
image: nextcloud:33-apache
|
||||||
container_name: nextcloud
|
container_name: nextcloud
|
||||||
pull_policy: always
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
- nextcloud-db
|
- nextcloud-db
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
services:
|
services:
|
||||||
opengist:
|
opengist:
|
||||||
image: ${OPENGIST_IMAGE}
|
image: ${OPENGIST_IMAGE}
|
||||||
pull_policy: always
|
|
||||||
container_name: opengist
|
container_name: opengist
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
TZ: ${TZ}
|
TZ: ${TZ}
|
||||||
OG_OPENGIST_HOME: /opengist
|
OG_OPENGIST_HOME: /opengist
|
||||||
|
OG_EXTERNAL_URL: "https://${OPENGIST_DOMAIN}"
|
||||||
OG_OIDC_PROVIDER_NAME: ${OPENGIST_OIDC_PROVIDER_NAME}
|
OG_OIDC_PROVIDER_NAME: ${OPENGIST_OIDC_PROVIDER_NAME}
|
||||||
OG_OIDC_CLIENT_KEY: ${OPENGIST_OIDC_CLIENT_ID}
|
OG_OIDC_CLIENT_KEY: ${OPENGIST_OIDC_CLIENT_ID}
|
||||||
OG_OIDC_SECRET: ${OPENGIST_OIDC_CLIENT_SECRET}
|
OG_OIDC_SECRET: ${OPENGIST_OIDC_CLIENT_SECRET}
|
||||||
@@ -16,15 +16,9 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
labels:
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
traefik.enable: "true"
|
traefik.enable: "true"
|
||||||
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
||||||
traefik.http.routers.opengist.rule: "Host(`${OPENGIST_DOMAIN}`)"
|
|
||||||
traefik.http.routers.opengist.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
|
|
||||||
traefik.http.routers.opengist.tls: "true"
|
|
||||||
traefik.http.routers.opengist.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
|
|
||||||
traefik.http.routers.opengist.middlewares: "crowdsec-bouncer@file"
|
|
||||||
traefik.http.routers.opengist.service: "opengist-svc"
|
|
||||||
traefik.http.services.opengist-svc.loadbalancer.server.port: "6157"
|
|
||||||
|
|
||||||
homepage.group: "Automatizacion y desarrollo"
|
homepage.group: "Automatizacion y desarrollo"
|
||||||
homepage.name: "OpenGist"
|
homepage.name: "OpenGist"
|
||||||
|
|||||||
@@ -11,28 +11,14 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
labels:
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
traefik.enable: "true"
|
traefik.enable: "true"
|
||||||
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
||||||
|
|
||||||
# Router publico — permite abrir enlaces compartidos sin login.
|
# Router publico — permite abrir enlaces compartidos sin login.
|
||||||
traefik.http.routers.pbin-public.rule: "Host(`${PRIVATEBIN_DOMAIN}`) && (Method(`GET`) || Method(`HEAD`))"
|
|
||||||
traefik.http.routers.pbin-public.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
|
|
||||||
traefik.http.routers.pbin-public.tls: "true"
|
|
||||||
traefik.http.routers.pbin-public.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
|
|
||||||
traefik.http.routers.pbin-public.middlewares: "crowdsec-bouncer@file"
|
|
||||||
traefik.http.routers.pbin-public.service: "pbin-svc"
|
|
||||||
traefik.http.routers.pbin-public.priority: "20"
|
|
||||||
|
|
||||||
# Router protegido — crear/borrar requiere login.
|
# Router protegido — crear/borrar requiere login.
|
||||||
traefik.http.routers.pbin.rule: "Host(`${PRIVATEBIN_DOMAIN}`)"
|
|
||||||
traefik.http.routers.pbin.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
|
|
||||||
traefik.http.routers.pbin.tls: "true"
|
|
||||||
traefik.http.routers.pbin.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
|
|
||||||
traefik.http.routers.pbin.middlewares: "crowdsec-bouncer@file,${TRAEFIK_AUTH_MIDDLEWARE}"
|
|
||||||
traefik.http.routers.pbin.service: "pbin-svc"
|
|
||||||
traefik.http.routers.pbin.priority: "10"
|
|
||||||
|
|
||||||
traefik.http.services.pbin-svc.loadbalancer.server.port: "8080"
|
|
||||||
|
|
||||||
homepage.group: "Automatizacion y desarrollo"
|
homepage.group: "Automatizacion y desarrollo"
|
||||||
homepage.name: "PrivateBin"
|
homepage.name: "PrivateBin"
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
services:
|
||||||
|
sablier:
|
||||||
|
image: acouvreur/sablier:${SABLIER_VERSION}
|
||||||
|
container_name: sablier
|
||||||
|
restart: unless-stopped
|
||||||
|
command:
|
||||||
|
- start
|
||||||
|
- --provider.name=docker
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro,Z
|
||||||
|
networks:
|
||||||
|
- proxy
|
||||||
|
|
||||||
|
networks:
|
||||||
|
proxy:
|
||||||
|
external: true
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
SABLIER_VERSION=1.8.0
|
||||||
Executable
+87
@@ -0,0 +1,87 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Recorre contenedores con restart-policy always/unless-stopped que estén
|
||||||
|
# caídos (Exited) o atascados (Restarting) y los recrea vía su propio
|
||||||
|
# proyecto docker compose, para curar referencias de red obsoletas tras
|
||||||
|
# un reinicio del daemon Docker (ej. update automático de docker-ce).
|
||||||
|
#
|
||||||
|
# Idempotente: si nada está roto, no hace nada. Pensado para correr
|
||||||
|
# periódicamente vía systemd timer (docker-selfheal.timer).
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
LOG_FILE="/var/log/docker-selfheal.log"
|
||||||
|
TELEGRAM_ENV="/home/felidae/f2b-telegram-bot/.env"
|
||||||
|
SABLIER_CONFIG="/opt/traefik/dynamic/sablier.yml"
|
||||||
|
|
||||||
|
# Contenedores que Sablier apaga a propósito (scale-to-zero) quedan Exited
|
||||||
|
# con restart-policy unless-stopped igual que un crash real -- no hay forma
|
||||||
|
# de distinguirlos por estado/policy. Se excluyen por nombre, leyendo los
|
||||||
|
# `names:` de cada middleware sablier-* en el dynamic config de Traefik.
|
||||||
|
declare -A SABLIER_MANAGED=()
|
||||||
|
if [ -r "$SABLIER_CONFIG" ]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
line="${line#*names:}"
|
||||||
|
line="${line//\"/}"
|
||||||
|
IFS=',' read -ra names <<< "$line"
|
||||||
|
for n in "${names[@]}"; do
|
||||||
|
n="$(echo "$n" | xargs)"
|
||||||
|
[ -n "$n" ] && SABLIER_MANAGED["$n"]=1
|
||||||
|
done
|
||||||
|
done < <(grep -E '^\s*names:' "$SABLIER_CONFIG")
|
||||||
|
fi
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "$(date -u +'%Y-%m-%dT%H:%M:%SZ') $*" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
notify() {
|
||||||
|
local msg="$1"
|
||||||
|
[ -f "$TELEGRAM_ENV" ] || return 0
|
||||||
|
local token chat_id
|
||||||
|
token=$(grep -E '^BOT_TOKEN=' "$TELEGRAM_ENV" | cut -d= -f2- | tr -d '"')
|
||||||
|
chat_id=$(grep -E '^PRIMARY_CHAT_ID=' "$TELEGRAM_ENV" | cut -d= -f2- | tr -d '"')
|
||||||
|
[ -n "$token" ] && [ -n "$chat_id" ] || return 0
|
||||||
|
curl -s -m 10 -X POST "https://api.telegram.org/bot${token}/sendMessage" \
|
||||||
|
-d "chat_id=${chat_id}" \
|
||||||
|
-d "text=${msg}" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed_any=0
|
||||||
|
|
||||||
|
# Contenedores parados o atascados que deberían estar corriendo
|
||||||
|
mapfile -t broken < <(docker ps -a --filter "status=exited" --filter "status=restarting" --filter "status=created" --format '{{.ID}}')
|
||||||
|
|
||||||
|
for cid in "${broken[@]}"; do
|
||||||
|
policy=$(docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' "$cid" 2>/dev/null)
|
||||||
|
name=$(docker inspect -f '{{.Name}}' "$cid" 2>/dev/null | sed 's#^/##')
|
||||||
|
|
||||||
|
case "$policy" in
|
||||||
|
always|unless-stopped) ;;
|
||||||
|
*) continue ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -n "${SABLIER_MANAGED[$name]:-}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
workdir=$(docker inspect -f '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}' "$cid" 2>/dev/null)
|
||||||
|
project=$(docker inspect -f '{{ index .Config.Labels "com.docker.compose.project" }}' "$cid" 2>/dev/null)
|
||||||
|
service=$(docker inspect -f '{{ index .Config.Labels "com.docker.compose.service" }}' "$cid" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -n "$workdir" ] && [ -d "$workdir" ]; then
|
||||||
|
log "Recreando '$name' (proyecto '$project', servicio '$service') vía compose en $workdir"
|
||||||
|
out=$(cd "$workdir" && docker compose up -d --force-recreate "$service" 2>&1)
|
||||||
|
log "$out"
|
||||||
|
fixed_any=1
|
||||||
|
notify "🔧 docker-selfheal: '$name' estaba caído (policy=$policy), recreado vía compose en ${project}."
|
||||||
|
else
|
||||||
|
log "Recreando '$name' vía docker start (sin metadata de compose)"
|
||||||
|
docker start "$cid" >/dev/null 2>&1
|
||||||
|
fixed_any=1
|
||||||
|
notify "🔧 docker-selfheal: '$name' estaba caído (policy=$policy), levantado con docker start."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$fixed_any" -eq 0 ]; then
|
||||||
|
log "Nada que arreglar."
|
||||||
|
fi
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
docker system prune -af --filter "label!=prune.protect=true"
|
||||||
@@ -2,7 +2,6 @@ services:
|
|||||||
trilium:
|
trilium:
|
||||||
image: ${TRILIUM_IMAGE}
|
image: ${TRILIUM_IMAGE}
|
||||||
container_name: trilium
|
container_name: trilium
|
||||||
pull_policy: always
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
hostname: ${TRILIUM_HOSTNAME}
|
hostname: ${TRILIUM_HOSTNAME}
|
||||||
|
|
||||||
@@ -19,19 +18,9 @@ services:
|
|||||||
- proxy
|
- proxy
|
||||||
|
|
||||||
labels:
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
traefik.enable: "true"
|
traefik.enable: "true"
|
||||||
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
||||||
traefik.http.routers.trilium.rule: "Host(`${TRILIUM_DOMAIN_1}`) || Host(`${TRILIUM_DOMAIN_2}`)"
|
|
||||||
traefik.http.routers.trilium.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
|
|
||||||
traefik.http.routers.trilium.middlewares: "trilium-sec"
|
|
||||||
traefik.http.routers.trilium.tls: "true"
|
|
||||||
traefik.http.routers.trilium.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
|
|
||||||
traefik.http.services.trilium.loadbalancer.server.port: "${TRILIUM_HTTP_PORT}"
|
|
||||||
traefik.http.middlewares.trilium-sec.headers.stsSeconds: "31536000"
|
|
||||||
traefik.http.middlewares.trilium-sec.headers.stsIncludeSubdomains: "true"
|
|
||||||
traefik.http.middlewares.trilium-sec.headers.stsPreload: "true"
|
|
||||||
traefik.http.middlewares.trilium-sec.headers.contentTypeNosniff: "true"
|
|
||||||
traefik.http.middlewares.trilium-sec.headers.frameDeny: "true"
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
proxy:
|
proxy:
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ services:
|
|||||||
image: postgres:17-alpine
|
image: postgres:17-alpine
|
||||||
container_name: vikunja-db
|
container_name: vikunja-db
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
environment:
|
environment:
|
||||||
TZ: ${TZ}
|
TZ: ${TZ}
|
||||||
POSTGRES_DB: ${VIKUNJA_DB_NAME}
|
POSTGRES_DB: ${VIKUNJA_DB_NAME}
|
||||||
@@ -20,8 +22,7 @@ services:
|
|||||||
- vikunja_internal
|
- vikunja_internal
|
||||||
|
|
||||||
vikunja:
|
vikunja:
|
||||||
image: vikunja/vikunja:latest
|
image: vikunja/vikunja@sha256:f6b80393c1998cd5cd0dc38d24762c59ab4c10000a6f1032ef5b554e262cab93
|
||||||
pull_policy: always
|
|
||||||
container_name: vikunja
|
container_name: vikunja
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
user: "1000:1000"
|
user: "1000:1000"
|
||||||
@@ -55,13 +56,9 @@ services:
|
|||||||
- mail_internal
|
- mail_internal
|
||||||
- proxy
|
- proxy
|
||||||
labels:
|
labels:
|
||||||
|
prune.protect: "true"
|
||||||
traefik.enable: "true"
|
traefik.enable: "true"
|
||||||
traefik.docker.network: "proxy"
|
traefik.docker.network: "proxy"
|
||||||
traefik.http.routers.vikunja.rule: "Host(`${VIKUNJA_DOMAIN}`)"
|
|
||||||
traefik.http.routers.vikunja.entrypoints: "websecure"
|
|
||||||
traefik.http.routers.vikunja.tls: "true"
|
|
||||||
traefik.http.routers.vikunja.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
|
|
||||||
traefik.http.services.vikunja.loadbalancer.server.port: "3456"
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
vikunja_internal:
|
vikunja_internal:
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ services:
|
|||||||
wg-easy:
|
wg-easy:
|
||||||
image: ${WG_EASY_IMAGE}
|
image: ${WG_EASY_IMAGE}
|
||||||
container_name: wg-easy
|
container_name: wg-easy
|
||||||
pull_policy: always
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
cap_add:
|
cap_add:
|
||||||
|
|||||||
Reference in New Issue
Block a user