feat: include and sync env files in gitops pipeline
Production GitOps Engine / execute-ansible (push) Failing after 50s Details

This commit is contained in:
Lucas Conrad 2026-06-04 01:17:01 +02:00
parent cdf7b2afb0
commit 3fdc2fbbfb
17 changed files with 730 additions and 7 deletions

View File

@ -21,7 +21,7 @@ jobs:
- name: Bootstrap Ansible Runtime
run: |
apt-get update && apt-get install -y python3-pip sshpass
apt-get update && apt-get install -y python3-pip sshpass rsync
# Pull upstream Ansible to ensure Python 3.12+ compatibility
pip3 install --break-system-packages ansible

View File

@ -0,0 +1,27 @@
services:
authelia:
image: authelia/authelia:latest
container_name: authelia
volumes:
- /opt/homelab/data/authelia:/config
networks:
- default
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.authelia.rule=Host(`auth.kotori-waifu`)"
- "traefik.http.services.authelia.loadbalancer.server.port=9091"
environment:
- TZ=Europe/Berlin
restart: unless-stopped
authelia-redis:
image: redis:alpine
container_name: authelia-redis
volumes:
- /opt/homelab/data/authelia-redis:/data
restart: unless-stopped
networks:
proxy_net:
external: true

View File

@ -0,0 +1,7 @@
# .env for Gitea stack — keep this file secure
# Database credentials
DB_PASS=Jd9uV3rTq8Pz2Lm6Qb5Xk1s
DB_ROOT_PASS=Rw7Nq2Zc8Hf4Yp1Lv3Sg6uT
# Runner registration token for gitea-runner
GITEA_RUNNER_TOKEN=K5EROdE8IE4EVcOoVnJX02k2WsaBvXNxEEkItzTg

View File

@ -0,0 +1,74 @@
services:
gitea:
image: gitea/gitea:1.21-rootless
container_name: gitea
environment:
- GITEA__database__DB_TYPE=mysql
- GITEA__database__HOST=gitea_db:3306
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=${DB_PASS}
- GITEA__server__ROOT_URL=https://git.kotori-waifu
- GITEA__server__SSH_PORT=2222
- GITEA__server__LFS_START_SERVER=true
- GITEA__actions__ENABLED=true
volumes:
# Absolute paths enforcing state segregation
- /opt/homelab/data/gitea_data:/var/lib/gitea
- /opt/homelab/data/gitea_config:/etc/gitea
networks:
- proxy_net
- db_net
ports:
- "2222:2222"
labels:
- "traefik.enable=true"
- "traefik.http.routers.gitea.rule=Host(`git.kotori-waifu`)"
- "traefik.http.routers.gitea.entrypoints=websecure"
- "traefik.http.routers.gitea.tls=true"
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
- "traefik.docker.network=proxy_net"
restart: unless-stopped
depends_on:
- gitea_db
gitea_db:
image: mariadb:10.11
container_name: gitea_db
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASS}
- MYSQL_DATABASE=gitea
- MYSQL_USER=gitea
- MYSQL_PASSWORD=${DB_PASS}
volumes:
- /opt/homelab/data/gitea_db:/var/lib/mysql
networks:
- db_net
restart: unless-stopped
gitea-runner:
image: gitea/act_runner:latest
container_name: gitea_runner
environment:
- GITEA_INSTANCE_URL=http://gitea:3000
- GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_TOKEN}
- GITEA_RUNNER_NAME=minipc-loopback-runner
- GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:20-bullseye
# Mandates routing ephemeral pipeline containers to proxy_net
- CONFIG_FILE=/config.yaml
volumes:
- /opt/homelab/data/runner_data:/data
- /var/run/docker.sock:/var/run/docker.sock
# Injected relative to execution path (/opt/homelab/stacks/00-core/gitea)
- /opt/homelab/data/gitea_config/runner-config.yaml:/config.yaml:ro
networks:
- proxy_net
restart: unless-stopped
depends_on:
- gitea
networks:
proxy_net:
external: true
db_net:
internal: true

View File

@ -0,0 +1,50 @@
log:
level: info
runner:
file: .runner
capacity: 1
timeout: 3h
shutdown_timeout: 0s
insecure: false
fetch_timeout: 5s
fetch_interval: 2s
fetch_interval_max: 5s
log_report_interval: 5s
log_report_max_latency: 3s
log_report_batch_size: 100
state_report_interval: 5s
github_mirror: ''
labels:
- "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest"
- "ubuntu-24.04:docker://docker.gitea.com/runner-images:ubuntu-24.04"
- "ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04"
cache:
enabled: true
dir: ""
host: ""
port: 0
external_server: ""
external_secret: ""
container:
network: "proxy_net"
privileged: false
options:
workdir_parent:
valid_volumes:
- '**'
docker_host: ""
force_pull: true
force_rebuild: false
require_docker: false
docker_timeout: 0s
bind_workdir: false
host:
workdir_parent:
metrics:
enabled: false
addr: "127.0.0.1:9101"

View File

@ -0,0 +1,28 @@
services:
traefik:
image: traefik:latest
container_name: traefik
user: root # Ensure access to docker.sock
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--log.level=DEBUG"
environment:
- DOCKER_API_VERSION=1.44
ports:
- "80:80"
- "443:443"
- "8085:8080" # Traefik dashboard (moved from 8080)
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxy_net
restart: unless-stopped
networks:
proxy_net:
external: true

View File

@ -0,0 +1,4 @@
APP_KEY=base64:IHjgcP5oTGLm3BwVZd8JpbU5gvU2nCrw4vq4HmrLSN4=
DB_PASSWORD=Kotowaifu13Database
MYSQL_ROOT_PASSWORD=Kotowaifu13DatabaseRoot
MYSQL_PASSWORD=Kotowaifu13Database

View File

@ -0,0 +1,51 @@
services:
bookstack:
image: lscr.io/linuxserver/bookstack:latest
container_name: bookstack
env_file:
- .env
environment:
- PUID=1000
- PGID=1000
- APP_URL=http://kotoribs.kotori-waifu
- APP_KEY=${APP_KEY}
- DB_HOST=bookstack_db
- DB_PORT=3306
- DB_USERNAME=bookstack
- DB_PASSWORD=${DB_PASSWORD}
- DB_DATABASE=bookstackapp
- TZ=Europe/Berlin
volumes:
- /opt/homelab/data/bookstack:/config
ports:
- 6875:80
networks:
- default
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.bookstack.rule=Host(`bookstack.kotori-waifu`) || Host(`kotoribs.kotori-waifu`) || Host(`bs.kotori-waifu.cc`)"
- "traefik.http.services.bookstack.loadbalancer.server.port=80"
- "traefik.docker.network=proxy_net"
restart: unless-stopped
depends_on:
- bookstack_db
bookstack_db:
image: mariadb:10.11
container_name: bookstack_db
env_file:
- .env
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=bookstackapp
- MYSQL_USER=bookstack
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- TZ=Europe/Berlin
volumes:
- /opt/homelab/data/bookstack_db:/var/lib/mysql
restart: unless-stopped
networks:
proxy_net:
external: true

View File

@ -0,0 +1,28 @@
services:
mealie:
image: ghcr.io/mealie-recipes/mealie:latest
container_name: mealie
environment:
- ALLOW_SIGNUP=true
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
- BASE_URL=https://mealie.kotori-waifu.cc
- API_DOCS=true
# Force HTTPS strictly for session cookie security
- NODE_ENV=production
volumes:
- /opt/homelab/data/mealie:/app/data
ports:
- "9925:9000"
networks:
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.mealie.rule=Host(`mealie.kotori-waifu`) || Host(`mealie.kotori-waifu.cc`)"
- "traefik.http.services.mealie.loadbalancer.server.port=9000"
restart: unless-stopped
networks:
proxy_net:
external: true

View File

@ -0,0 +1,24 @@
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
ports:
- "42088:80"
environment:
- WEBSOCKET_ENABLED=true
- SIGNUPS_ALLOWED=true
volumes:
- /opt/homelab/data/vaultwarden:/data
networks:
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.vaultwarden.rule=Host(`vault.kotori-waifu`)"
- "traefik.http.routers.vaultwarden.entrypoints=websecure"
- "traefik.http.routers.vaultwarden.tls=true"
- "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
networks:
proxy_net:
external: true

View File

@ -0,0 +1,7 @@
# Immich stack environment
# Set database credentials and storage locations
DB_PASSWORD=change_me_db_password
DB_USERNAME=immich
DB_DATABASE_NAME=immichdb
DB_DATA_LOCATION=/opt/homelab/data/immich/postgres
UPLOAD_LOCATION=/opt/homelab/data/immich/upload

View File

@ -0,0 +1,87 @@
#
# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose
#
# Make sure to use the docker-compose.yml of the current release:
#
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
#
# The compose file on main may not be compatible with the latest release.
services:
immich-server:
container_name: immich-server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
# extends:
# file: hwaccel.transcoding.yml
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
volumes:
# Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file
- ${UPLOAD_LOCATION}:/data
- /etc/localtime:/etc/localtime:ro
- /mnt/nas/gallery:/mnt/media/external:ro
env_file:
- .env
ports:
- "2283:2283"
depends_on:
- redis
- database
networks:
- default
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.immich.rule=Host(`immich.kotori-waifu`)"
- "traefik.http.services.immich.loadbalancer.server.port=2283"
- "traefik.docker.network=proxy_net"
restart: unless-stopped
healthcheck:
disable: false
immich-machine-learning:
container_name: immich-machine-learning
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
# Example tag: ${IMMICH_VERSION:-release}-cuda
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
# extends: # uncomment this section for hardware acceleration - see https://docs.immich.app/features/ml-hardware-acceleration
# file: hwaccel.ml.yml
# service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
volumes:
- model-cache:/cache
env_file:
- .env
restart: unless-stopped
healthcheck:
disable: false
redis:
container_name: redis
image: docker.io/valkey/valkey:9@sha256:546304417feac0874c3dd576e0952c6bb8f06bb4093ea0c9ca303c73cf458f63
healthcheck:
test: redis-cli ping || exit 1
restart: unless-stopped
database:
container_name: database
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
POSTGRES_INITDB_ARGS: "--data-checksums"
# Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs
# DB_STORAGE_TYPE: 'HDD'
volumes:
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
shm_size: 128mb
restart: unless-stopped
healthcheck:
disable: false
volumes:
model-cache:
networks:
proxy_net:
external: true

View File

@ -0,0 +1,2 @@
WIREGUARD_PRIVATE_KEY=AKS5sSyo7mtXDYEVBe8KAwoL2gJbXqV6UIU0SVoiElo=
CLOUDFLARED_TOKEN=eyJhIjoiOGEwOTNmZDg3NDk0OTU1ZDBkZGFhM2UyOTE2YTczYmUiLCJ0IjoiYzNjZDI1OGMtMTRjOC00OTk5LWFlNTAtZjQ4NTc2OTAwZWI0IiwicyI6IlpHSTVZbVEyTVdVdFptRTFNUzAwTXpSbUxXSTNNVEF0TldOaU0yRmtaV0prTXpsbSJ9

View File

@ -0,0 +1,229 @@
services:
gluetun:
image: qmcgaw/gluetun
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
ports:
- "8080:8080" # qBittorrent
- "9696:9696" # Prowlarr
- "6881:6881" # Torrent TCP
- "6881:6881/udp" # Torrent UDP
env_file:
- .env
environment:
- VPN_SERVICE_PROVIDER=protonvpn
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
- WIREGUARD_ADDRESSES=10.2.0.2/32
- SERVER_COUNTRIES=Netherlands
- SERVER_CITIES=Amsterdam
- FIREWALL_OUTBOUND_SUBNETS=172.16.0.0/12,192.168.0.0/16,10.0.0.0/8
networks:
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.torrent.rule=Host(`torrent.kotori-waifu`)"
- "traefik.http.routers.torrent.service=torrent"
- "traefik.http.services.torrent.loadbalancer.server.port=8080"
- "traefik.http.routers.prowlarr.rule=Host(`prowlarr.kotori-waifu`)"
- "traefik.http.routers.prowlarr.service=prowlarr"
- "traefik.http.services.prowlarr.loadbalancer.server.port=9696"
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: service:gluetun
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
- WEBUI_PORT=8080
- DOCKER_MODS=linuxserver/mods:universal-package-install
- INSTALL_PACKAGES=zip
volumes:
- /opt/homelab/data/qbittorrent:/config
- /mnt/nas:/data
restart: unless-stopped
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
container_name: prowlarr
network_mode: service:gluetun
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
volumes:
- /opt/homelab/data/prowlarr:/config
restart: unless-stopped
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
ports:
- "7878:7878"
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
volumes:
- /opt/homelab/data/radarr:/config
- /mnt/nas:/data
networks:
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.radarr.rule=Host(`radarr.kotori-waifu`)"
- "traefik.http.services.radarr.loadbalancer.server.port=7878"
restart: unless-stopped
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
ports:
- "8989:8989"
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
volumes:
- /opt/homelab/data/sonarr:/config
- /mnt/nas:/data
networks:
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.sonarr.rule=Host(`sonarr.kotori-waifu`)"
- "traefik.http.services.sonarr.loadbalancer.server.port=8989"
restart: unless-stopped
jellyfin:
image: lscr.io/linuxserver/jellyfin:latest
container_name: jellyfin
ports:
- "8096:8096"
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
volumes:
- /opt/homelab/data/jellyfin:/config
- /mnt/nas:/data
devices:
- /dev/dri:/dev/dri
networks:
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.jellyfin.rule=Host(`jellyfin.kotori-waifu`) || Host(`jellyfin.kotori-waifu.cc`)"
- "traefik.http.services.jellyfin.loadbalancer.server.port=8096"
restart: unless-stopped
seerr:
image: ghcr.io/seerr-team/seerr:latest
container_name: seerr
init: true
ports:
- "5055:5055"
environment:
- TZ=Etc/UTC
# Enforce strict execution namespace mapping to host UID/GID 1000
- PUID=1000
- PGID=1000
volumes:
- /opt/homelab/data/seerr:/app/config
networks:
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.seerr.rule=Host(`jellyseer.kotori-waifu`) || Host(`seer.kotori-waifu.cc`)"
- "traefik.http.services.seerr.loadbalancer.server.port=5055"
restart: unless-stopped
flaresolverr:
image: flaresolverr/flaresolverr:v3.4.3
container_name: flaresolverr
network_mode: service:gluetun
environment:
- LOG_LEVEL=info
- TZ=Etc/UTC
- HOST=0.0.0.0
- PORT=8191
security_opt:
- seccomp:unconfined
shm_size: 1gb
restart: unless-stopped
watchtower:
image: containrrr/watchtower:latest
container_name: watchtower
environment:
- TZ=Europe/Berlin
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_SCHEDULE=0 0 4 * * *
- DOCKER_API_VERSION=1.44
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
- proxy_net
restart: unless-stopped
homepage:
image: ghcr.io/gethomepage/homepage:latest
container_name: homepage
ports:
- "3000:3000"
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
- HOMEPAGE_ALLOWED_HOSTS=*
volumes:
- /opt/homelab/data/homepage:/app/config
- /var/run/docker.sock:/var/run/docker.sock:ro
- /mnt/nas:/mnt/nas:ro
networks:
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.homepage.rule=PathPrefix(`/`)"
- "traefik.http.routers.homepage.priority=1"
- "traefik.http.services.homepage.loadbalancer.server.port=3000"
restart: unless-stopped
komga:
image: gotson/komga:latest
container_name: komga
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
volumes:
- /opt/homelab/data/komga:/config
- /mnt/nas/manga:/data
ports:
- "25600:25600"
networks:
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.komga.rule=Host(`goon.kotori-waifu`)"
- "traefik.http.services.komga.loadbalancer.server.port=25600"
restart: unless-stopped
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
networks:
- proxy_net
restart: unless-stopped
env_file:
- .env
command: tunnel --no-autoupdate run --token ${CLOUDFLARED_TOKEN}
networks:
proxy_net:
external: true

View File

@ -0,0 +1,2 @@
GF_SECURITY_ADMIN_USER=admin
GF_SECURITY_ADMIN_PASSWORD=admin

View File

@ -0,0 +1,84 @@
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
volumes:
- /opt/homelab/data/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- /opt/homelab/data/prometheus:/prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--web.console.libraries=/etc/prometheus/console_libraries"
- "--web.console.templates=/etc/prometheus/consoles"
- "--web.enable-lifecycle"
ports:
- 9090:9090
networks:
- proxy_net
restart: unless-stopped
grafana:
image: grafana/grafana:latest
container_name: grafana
env_file:
- .env
environment:
- GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER}
- GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD}
volumes:
- /opt/homelab/data/grafana:/var/lib/grafana
ports:
- 3001:3000
depends_on:
- prometheus
networks:
- proxy_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.grafana.rule=Host(`grafana.kotori-waifu`)"
- "traefik.http.services.grafana.loadbalancer.server.port=3000"
restart: unless-stopped
cadvisor:
image: ghcr.io/google/cadvisor:v0.53.0
container_name: cadvisor
privileged: true
environment:
- DOCKER_API_VERSION=1.44
devices:
- /dev/kmsg:/dev/kmsg
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- /sys/fs/cgroup:/sys/fs/cgroup:ro
ports:
- 8081:8080
networks:
- proxy_net
restart: unless-stopped
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- "--path.procfs=/host/proc"
- "--path.rootfs=/rootfs"
- "--path.sysfs=/host/sys"
- "--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)"
ports:
- 9100:9100
networks:
- proxy_net
restart: unless-stopped
networks:
proxy_net:
external: true

View File

@ -3,10 +3,29 @@
hosts: minipc
gather_facts: false
tasks:
- name: Audit Target Operational State
ansible.builtin.command: uname -a
register: host_kernel
- name: Sync GitOps stacks to target machine
ansible.posix.synchronize:
src: "{{ playbook_dir }}/docker/stacks/"
dest: /opt/homelab/stacks/
archive: yes
delete: no
recursive: yes
rsync_opts:
- "--exclude=.git"
- "--exclude=.gitignore"
- name: Log Target Status
ansible.builtin.debug:
msg: "Deployment target operating system identified as: {{ host_kernel.stdout }}"
- name: Identify all docker-compose stack directories
ansible.builtin.find:
paths: /opt/homelab/stacks
file_type: file
patterns: "docker-compose.yml"
recurse: yes
register: compose_files
- name: Apply Docker Compose configurations
ansible.builtin.command: docker compose up -d --remove-orphans
args:
chdir: "{{ item.path | dirname }}"
loop: "{{ compose_files.files }}"
register: compose_results
changed_when: "'Started' in compose_results.stdout or 'Created' in compose_results.stdout or 'Recreated' in compose_results.stdout"