From 3bc9eab98402858b01319a66a9b592f9e90636c4 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Mon, 22 Jun 2026 17:19:58 +0100 Subject: [PATCH 01/30] chore(compose): remove compose stacks Stack templates will now move to a custom stacks repo. --- roles/karo-compose/defaults/main.yml | 225 +----------------- .../templates/core/pocketid/compose.yml.j2 | 63 ----- .../templates/core/traefik/compose.yml.j2 | 157 ------------ .../templates/core/traefik/traefik.yml.j2 | 79 ------ .../templates/extra/glance/compose.yml.j2 | 54 ----- .../templates/extra/glance/glance.yml.j2 | 1 - .../templates/extra/gluetun/compose.yml.j2 | 44 ---- .../templates/extra/godns/compose.yml.j2 | 33 --- .../templates/extra/godns/config.json.j2 | 21 -- .../templates/extra/jellyfin/compose.yml.j2 | 52 ---- .../templates/extra/ntfy/compose.yml.j2 | 64 ----- .../templates/extra/prowlarr/compose.yml.j2 | 37 --- .../templates/extra/proxy/compose.yml.j2 | 30 --- .../templates/extra/proxy/haproxy.cfg.j2 | 39 --- .../extra/qbittorrent/compose.yml.j2 | 80 ------- .../templates/extra/radarr/compose.yml.j2 | 40 ---- .../templates/extra/seerr/compose.yml.j2 | 47 ---- .../templates/extra/sonarr/compose.yml.j2 | 40 ---- .../templates/extra/whoami/compose.yml.j2 | 34 --- 19 files changed, 4 insertions(+), 1136 deletions(-) delete mode 100644 roles/karo-compose/templates/core/pocketid/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/core/traefik/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/core/traefik/traefik.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/glance/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/glance/glance.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/gluetun/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/godns/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/godns/config.json.j2 delete mode 100644 roles/karo-compose/templates/extra/jellyfin/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/ntfy/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/prowlarr/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/proxy/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/proxy/haproxy.cfg.j2 delete mode 100644 roles/karo-compose/templates/extra/qbittorrent/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/radarr/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/seerr/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/sonarr/compose.yml.j2 delete mode 100644 roles/karo-compose/templates/extra/whoami/compose.yml.j2 diff --git a/roles/karo-compose/defaults/main.yml b/roles/karo-compose/defaults/main.yml index e4ece33..bfe1c81 100644 --- a/roles/karo-compose/defaults/main.yml +++ b/roles/karo-compose/defaults/main.yml @@ -8,25 +8,12 @@ karo_compose_justfile_stack: all # docker will start stacks in this order # docker will stop stacks in reverse order -karo_compose_stacks: - core: - - traefik - - pocketid - extra: - - proxy - - whoami - - godns - - gluetun - - qbittorrent - - prowlarr - - radarr - - sonarr - - seerr - - jellyfin - - ntfy - - glance +# karo_compose_stacks: +# core: +# extra: karo_compose_restart_policy: unless-stopped + karo_compose_timezone: "Europe/London" karo_compose_root_domain: example.com @@ -58,207 +45,3 @@ karo_compose_oidc_certificate_url: "{{ karo_compose_oidc_url }}{{ karo_compose_o karo_compose_oidc_provider_name: "PocketID" karo_compose_oidc_admin_group: "admin" - -# core stacks -# --- - -# pocketid - -karo_compose_pocketid_enabled: true -karo_compose_pocketid_image: ghcr.io/pocket-id/pocket-id -karo_compose_pocketid_version: v2.6.2@sha256:1cc51e5b36fa7ec31368d3efb24ba362be6dce20f3cc0a2852843527bd5110f0 -karo_compose_pocketid_log_level: info # debug, info, warn, error - -karo_compose_pocketid_secrets: - pocketid_encryption_key: "" # `openssl rand -base64 32` - pocketid_maxmind_license_key: "" - -# traefik - -karo_compose_traefik_enabled: true -karo_compose_traefik_image: docker.io/traefik -karo_compose_traefik_version: v3.6.15@sha256:8cb20d16e01a53d8d7f7696ac2f1af7d200d5c9984d226ce2299731d9eab6d6c -karo_compose_traefik_dashboard_domain: "traefik.{{ karo_compose_root_domain }}" -karo_compose_traefik_dashboard_forward_auth_enabled: true -karo_compose_traefik_log_level: debug # trace, debug, info, warn, error, fatal, panic - -karo_compose_traefik_dashboard_enabled: true - -karo_compose_traefik_custom_domains_raw: | - # - main: "*.int.{{ karo_compose_root_domain }}" - -karo_compose_traefik_acme_staging_enabled: true - -karo_compose_traefik_acme_ca_server_url: "https://acme-v02.api.letsencrypt.org/directory" # "https://acme.zerossl.com/v2/DV90" -karo_compose_traefik_acme_staging_ca_server_url: "https://acme-staging-v02.api.letsencrypt.org/directory" - -karo_compose_traefik_acme_email: cert@example.com - -karo_compose_traefik_secrets: - traefik_acme_zone_api_token: "" - traefik_acme_dns_api_token: "" - tinyauth_oidc_client_secret: "" - -# traefik tinyauth - -karo_compose_traefik_tinyauth_image: ghcr.io/steveiliop56/tinyauth -karo_compose_traefik_tinyauth_version: v5.0.7-distroless@sha256:e31ae2bac566cfe4687a8a54121a884ce25c4869995075f8e7e3d352bcd59241 -karo_compose_traefik_tinyauth_domain: "tinyauth.{{ karo_compose_root_domain }}" -karo_compose_traefik_tinyauth_log_level: info # trace, debug, info, warn, error, fatal, panic - -karo_compose_traefik_tinyauth_oidc_client_id: "" - -# traefik cetusguard - -karo_compose_traefik_cetusguard_image: docker.io/hectorm/cetusguard -karo_compose_traefik_cetusguard_version: v1.1.3@sha256:b3dc5d2042869dd714c75985d94bb8c259df53b325ec6b1a2b57bcc3eac17689 -karo_compose_traefik_cetusguard_log_level: 7 # 0-7 (min to max verbosity) - -# extra stacks -# --- - -# glance - -karo_compose_glance_enabled: false -karo_compose_glance_image: docker.io/glanceapp/glance -karo_compose_glance_version: v0.8.4@sha256:6df86a7e8868d1eda21f35205134b1962c422957e42a0c44d4717c8e8f741b1a -karo_compose_glance_domain: "glance.{{ karo_compose_root_domain }}" -karo_compose_glance_forward_auth_enabled: true - -karo_compose_glance_config_raw: | - # glance config - -# gluetun - -karo_compose_gluetun_enabled: false -karo_compose_gluetun_image: docker.io/qmcgaw/gluetun -karo_compose_gluetun_version: v3.41.1@sha256:1a5bf4b4820a879cdf8d93d7ef0d2d963af56670c9ebff8981860b6804ebc8ab -karo_compose_gluetun_log_level: info # debug, info, warning, error - -karo_compose_gluetun_firewall_vpn_input_ports: "" - -karo_compose_gluetun_secrets: - gluetun_wireguard_config_raw: | - # wireguard config - -# godns - -karo_compose_godns_enabled: false -karo_compose_godns_image: docker.io/timothyye/godns -karo_compose_godns_version: v3.4.1@sha256:d9a043164b256ab9d7ab03a7619bfe2292a2ab2b6e5e3151e818edb63249b1de -karo_compose_godns_debug_info_enabled: false - -karo_compose_godns_subdomain: home - -karo_compose_godns_secrets: - godns_dns_api_token: "" - -# jellyfin - -karo_compose_jellyfin_enabled: false -karo_compose_jellyfin_image: docker.io/jellyfin/jellyfin -karo_compose_jellyfin_version: 10.11.8@sha256:1694ff069f0c9dafb283c36765175606866769f5d72f2ed56b6a0f1be922fc37 -karo_compose_jellyfin_domain: "jellyfin.{{ karo_compose_root_domain }}" - -karo_compose_jellyfin_movies_media_path: "" # e.g. /media/drive1/data/media/movies -karo_compose_jellyfin_series_media_path: "" # e.g. /media/drive1/data/media/series - -# ntfy - -karo_compose_ntfy_enabled: false -karo_compose_ntfy_image: docker.io/binwiederhier/ntfy -karo_compose_ntfy_version: v2.22.0@sha256:d8b3408c75d5ae78e03339af6103e7e6195e56573eea6f1ad34bb4dc4f44f340 -karo_compose_ntfy_domain: "ntfy.{{ karo_compose_root_domain }}" -karo_compose_ntfy_log_level: info # trace, debug, info, warn, error - -karo_compose_ntfy_auth_users: "" # https://docs.ntfy.sh/config/#users-via-the-config -karo_compose_ntfy_auth_access: "" # https://docs.ntfy.sh/config/#acl-entries-via-the-config -karo_compose_ntfy_auth_tokens: "" # https://docs.ntfy.sh/config/#tokens-via-the-config - -# prowlarr - -karo_compose_prowlarr_enabled: false -karo_compose_prowlarr_image: docker.io/linuxserver/prowlarr -karo_compose_prowlarr_version: 2.3.5.5327-ls144@sha256:b4204e18666179472225935b443a99cf6c66dcb7bbc2d35034427a3851f13135 -karo_compose_prowlarr_domain: "prowlarr.{{ karo_compose_root_domain }}" -karo_compose_prowlarr_forward_auth_enabled: true - -# proxy - -karo_compose_proxy_client_enabled: false -karo_compose_proxy_server_enabled: false - -karo_compose_proxy_enabled: "{{ karo_compose_proxy_server_enabled }}" - -karo_compose_proxy_client_wireguard_ipv4: 10.25.0.2 -karo_compose_proxy_server_wireguard_ipv4: 10.25.0.1 - -karo_compose_proxy_server_wireguard_port: 52525 - -# proxy haproxy - -karo_compose_proxy_haproxy_image: docker.io/haproxy -karo_compose_proxy_haproxy_version: 3.2.17-alpine@sha256:0c32ec9e64a3bbacb2a1a28afbc3d46032d905bf74ccb745b45488ff41ee9a6b - -karo_compose_proxy_haproxy_config_raw: | - # haproxy config - -# qbittorrent - -karo_compose_qbittorrent_enabled: false -karo_compose_qbittorrent_image: docker.io/qbittorrentofficial/qbittorrent-nox -karo_compose_qbittorrent_version: 5.1.4-1@sha256:41888f4e05646ace98aeaff01a7bbec714e2e6082ab273fd4af0d1210352ea2c -karo_compose_qbittorrent_domain: "qbittorrent.{{ karo_compose_root_domain }}" - -karo_compose_qbittorrent_downloads_path: "" # e.g. /media/drive1/data/torrents - -karo_compose_qbittorrent_webui_enabled: true - -karo_compose_qbittorrent_secrets: - qui_session_secret: "" # `openssl rand -hex 16` - qui_oidc_client_secret: "" - -# qbittorrent qui - -karo_compose_qbittorrent_qui_image: ghcr.io/autobrr/qui -karo_compose_qbittorrent_qui_version: v1.17.0@sha256:fb3832e68f66b056e1b049d16c40732661e7b73999bc642d4b11469a3ebbabd3 -karo_compose_qbittorrent_qui_domain: "qui.{{ karo_compose_root_domain }}" -karo_compose_qbittorrent_qui_log_level: info # trace, debug, info, warn, error - -karo_compose_qbittorrent_qui_oidc_client_id: "" - -# radarr - -karo_compose_radarr_enabled: false -karo_compose_radarr_image: docker.io/linuxserver/radarr -karo_compose_radarr_version: 6.1.1.10360-ls300@sha256:b01097ad2d948c9f5eca39eb60bb529e2e55b0738c4bf7db09383bef0abab59d -karo_compose_radarr_domain: "radarr.{{ karo_compose_root_domain }}" -karo_compose_radarr_forward_auth_enabled: true - -karo_compose_radarr_data_path: "" # e.g. /media/drive1/data - -# seerr - -karo_compose_seerr_enabled: false -karo_compose_seerr_image: ghcr.io/seerr-team/seerr -karo_compose_seerr_version: preview-new-oidc@sha256:a14a9c326881fdce70b00df3fc095f5b5b128f5552677f677cd3788e757fb4e6 -karo_compose_seerr_domain: "seerr.{{ karo_compose_root_domain }}" -karo_compose_seerr_log_level: info # debug, info, warn, error - -# sonarr - -karo_compose_sonarr_enabled: false -karo_compose_sonarr_image: docker.io/linuxserver/sonarr -karo_compose_sonarr_version: 4.0.17.2952-ls309@sha256:3580aec3802c915f0f819a88d5099abce61734b925732b8393d176b5dc561020 -karo_compose_sonarr_domain: "sonarr.{{ karo_compose_root_domain }}" -karo_compose_sonarr_forward_auth_enabled: true - -karo_compose_sonarr_data_path: "" # e.g. /media/drive1/data - -# whoami - -karo_compose_whoami_enabled: false -karo_compose_whoami_image: docker.io/traefik/whoami -karo_compose_whoami_version: v1.11.0@sha256:200689790a0a0ea48ca45992e0450bc26ccab5307375b41c84dfc4f2475937ab -karo_compose_whoami_domain: "whoami.{{ karo_compose_root_domain }}" -karo_compose_whoami_forward_auth_enabled: false diff --git a/roles/karo-compose/templates/core/pocketid/compose.yml.j2 b/roles/karo-compose/templates/core/pocketid/compose.yml.j2 deleted file mode 100644 index 8f498dd..0000000 --- a/roles/karo-compose/templates/core/pocketid/compose.yml.j2 +++ /dev/null @@ -1,63 +0,0 @@ -# SPDX-FileCopyrightText: 2025 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/core/pocket-id - ---- -name: pocketid -services: - - pocketid: - image: {{ karo_compose_pocketid_image }}:{{ karo_compose_pocketid_version }} - container_name: pocketid - restart: {{ karo_compose_restart_policy }} - read_only: false - networks: - - egress_pocketid - - frontend - volumes: - - type: volume - source: pocketid_data - target: /app/data - labels: - - traefik.enable=true - - traefik.http.routers.pocketid.rule=Host(`{{ karo_compose_oidc_domain }}`) - - traefik.http.services.pocketid.loadbalancer.server.port=1411 - environment: - - LOG_LEVEL={{ karo_compose_pocketid_log_level }} - - APP_URL={{ karo_compose_oidc_url }} - - ENCRYPTION_KEY_FILE=/run/secrets/pocketid_encryption_key - - MAXMIND_LICENSE_KEY_FILE=/run/secrets/pocketid_maxmind_license_key - - ANALYTICS_DISABLED=true - - VERSION_CHECK_DISABLED=true - - TRUST_PROXY=true - - PUID=1000 - - PGID=1000 - secrets: - - pocketid_encryption_key - - pocketid_maxmind_license_key - healthcheck: - test: [ "CMD", "/app/pocket-id", "healthcheck" ] - interval: 1m30s - timeout: 5s - retries: 2 - start_period: 10s - -networks: - egress_pocketid: - name: egress_pocketid - driver: bridge - internal: false - frontend: - external: true - -volumes: - pocketid_data: - name: pocketid_data - -secrets: - pocketid_encryption_key: - file: /run/user/1001/karo/compose/pocketid_encryption_key - pocketid_maxmind_license_key: - file: /run/user/1001/karo/compose/pocketid_maxmind_license_key diff --git a/roles/karo-compose/templates/core/traefik/compose.yml.j2 b/roles/karo-compose/templates/core/traefik/compose.yml.j2 deleted file mode 100644 index f6e4492..0000000 --- a/roles/karo-compose/templates/core/traefik/compose.yml.j2 +++ /dev/null @@ -1,157 +0,0 @@ -# SPDX-FileCopyrightText: 2025 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/core/traefik - -{% set traefik_acme_volume = "traefik_acme_staging" if karo_compose_traefik_acme_staging_enabled else "traefik_acme" %} ---- -name: traefik -services: - - traefik: - image: {{ karo_compose_traefik_image }}:{{ karo_compose_traefik_version }} - container_name: traefik - restart: {{ karo_compose_restart_policy }} - security_opt: - - no-new-privileges:true - ports: - - "0.0.0.0:80:80/tcp" - - "0.0.0.0:443:443/tcp" - - "0.0.0.0:443:443/udp" - networks: - socketproxy_traefik: - egress_traefik: - frontend: - ipv4_address: 172.18.0.254 - volumes: - - type: bind - source: /srv/docker/core/traefik/traefik.yml - target: /etc/traefik/traefik.yml - read_only: true - - type: volume - source: {{ traefik_acme_volume }} - target: /etc/traefik/acme - labels: - - traefik.enable=true - - traefik.http.routers.dashboard.rule=Host(`{{ karo_compose_traefik_dashboard_domain }}`) - - traefik.http.routers.dashboard.service=api@internal - - traefik.http.services.dashboard.loadbalancer.server.port=8080 -{% if karo_compose_traefik_dashboard_forward_auth_enabled %} - # forward auth - - traefik.http.routers.dashboard.middlewares=tinyauth - - tinyauth.apps.dashboard.config.domain={{ karo_compose_traefik_dashboard_domain }} - - tinyauth.apps.dashboard.oauth.groups={{ karo_compose_oidc_admin_group }} -{% endif %} - environment: - - CF_ZONE_API_TOKEN_FILE=/run/secrets/traefik_acme_zone_api_token - - CF_DNS_API_TOKEN_FILE=/run/secrets/traefik_acme_dns_api_token - - CLOUDFLARE_POLLING_INTERVAL=10 - - TZ={{ karo_compose_timezone }} - secrets: - - traefik_acme_zone_api_token - - traefik_acme_dns_api_token - depends_on: - tinyauth: - condition: service_healthy - - tinyauth: - image: {{ karo_compose_traefik_tinyauth_image }}:{{ karo_compose_traefik_tinyauth_version }} - container_name: tinyauth - restart: {{ karo_compose_restart_policy }} - # user: 1000:1000 # unsupported - # read_only: true # write secrets - security_opt: - - no-new-privileges:true - networks: - socketproxy_traefik: - frontend: - ipv4_address: 172.18.0.253 - extra_hosts: - - "{{ karo_compose_oidc_domain }}:172.18.0.254" - labels: - - traefik.enable=true - - traefik.http.routers.tinyauth.rule=Host(`{{ karo_compose_traefik_tinyauth_domain }}`) - - traefik.http.services.tinyauth.loadbalancer.server.port=3000 - - traefik.http.middlewares.tinyauth.forwardauth.address=http://tinyauth:3000/api/auth/traefik - - traefik.http.middlewares.tinyauth.forwardauth.maxResponseBodySize=8192 - - traefik.http.middlewares.tinyauth.forwardauth.trustForwardHeader=true - environment: - - TINYAUTH_LOG_LEVEL={{ karo_compose_traefik_tinyauth_log_level }} - - TINYAUTH_ANALYTICS_ENABLED=false - - TINYAUTH_RESOURCES_ENABLED=false - - TINYAUTH_UI_BACKGROUNDIMAGE="" - - TINYAUTH_APPURL=https://{{ karo_compose_traefik_tinyauth_domain }} - - TINYAUTH_OAUTH_AUTOREDIRECT=pocketid - - TINYAUTH_OAUTH_PROVIDERS_POCKETID_CLIENTID={{ karo_compose_traefik_tinyauth_oidc_client_id }} - - TINYAUTH_OAUTH_PROVIDERS_POCKETID_CLIENTSECRETFILE=/run/secrets/tinyauth_oidc_client_secret - - TINYAUTH_OAUTH_PROVIDERS_POCKETID_AUTHURL={{ karo_compose_oidc_authorization_url }} - - TINYAUTH_OAUTH_PROVIDERS_POCKETID_TOKENURL={{ karo_compose_oidc_token_url }} - - TINYAUTH_OAUTH_PROVIDERS_POCKETID_USERINFOURL={{ karo_compose_oidc_userinfo_url }} - - TINYAUTH_OAUTH_PROVIDERS_POCKETID_REDIRECTURL=https://{{ karo_compose_traefik_tinyauth_domain }}/api/oauth/callback/pocketid - - TINYAUTH_OAUTH_PROVIDERS_POCKETID_SCOPES=openid email profile groups - - TINYAUTH_OAUTH_PROVIDERS_POCKETID_NAME={{ karo_compose_oidc_provider_name }} - - DOCKER_HOST=tcp://cetusguard:2375 - secrets: - - tinyauth_oidc_client_secret - depends_on: - - cetusguard - - cetusguard: - image: {{ karo_compose_traefik_cetusguard_image }}:{{ karo_compose_traefik_cetusguard_version }} - container_name: cetusguard_traefik - restart: {{ karo_compose_restart_policy }} - read_only: true - networks: - - socketproxy_traefik - volumes: - - type: bind - source: /run/user/1001/docker.sock - target: /var/run/docker.sock - read_only: true - environment: - CETUSGUARD_BACKEND_ADDR: "unix:///var/run/docker.sock" - CETUSGUARD_FRONTEND_ADDR: "tcp://:2375" - CETUSGUARD_RULES: | - ! monitor events - GET %API_PREFIX_EVENTS% - ! list containers - GET %API_PREFIX_CONTAINERS%/json - ! inspect a container - GET %API_PREFIX_CONTAINERS%/%CONTAINER_ID_OR_NAME%/json - CETUSGUARD_LOG_LEVEL: "{{ karo_compose_traefik_cetusguard_log_level }}" - -networks: - socketproxy_traefik: - name: socketproxy_traefik - driver: bridge - internal: true - egress_traefik: - name: egress_traefik - driver: bridge - internal: false - ipam: - config: - - subnet: 172.19.0.0/24 - gateway: 172.19.0.1 - ip_range: 172.19.0.253/32 - frontend: - name: frontend - driver: bridge - internal: true - ipam: - config: - - subnet: 172.18.0.0/24 - gateway: 172.18.0.1 - -volumes: - {{ traefik_acme_volume }}: - name: {{ traefik_acme_volume }} - -secrets: - traefik_acme_zone_api_token: - file: /run/user/1001/karo/compose/traefik_acme_zone_api_token - traefik_acme_dns_api_token: - file: /run/user/1001/karo/compose/traefik_acme_dns_api_token - tinyauth_oidc_client_secret: - file: /run/user/1001/karo/compose/tinyauth_oidc_client_secret diff --git a/roles/karo-compose/templates/core/traefik/traefik.yml.j2 b/roles/karo-compose/templates/core/traefik/traefik.yml.j2 deleted file mode 100644 index 69ad62d..0000000 --- a/roles/karo-compose/templates/core/traefik/traefik.yml.j2 +++ /dev/null @@ -1,79 +0,0 @@ -# SPDX-FileCopyrightText: 2025 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - ---- - -global: - checkNewVersion: false - sendAnonymousUsage: false - -# https://doc.traefik.io/traefik/reference/install-configuration/observability/logs-and-accesslogs/ -log: - level: {{ karo_compose_traefik_log_level | upper }} - -# https://doc.traefik.io/traefik/reference/install-configuration/api-dashboard/ -api: - dashboard: {{ karo_compose_traefik_dashboard_enabled | lower }} - disabledashboardad: true - -# https://doc.traefik.io/traefik/reference/install-configuration/entrypoints/ -entryPoints: - web: - address: :80 - http: - redirections: - entryPoint: - to: websecure - scheme: https - permanent: true - websecure: - address: :443 - http3: true - asDefault: true - forwardedHeaders: - trustedIPs: - - "172.18.0.253" -{% if karo_compose_proxy_client_enabled %} - proxyProtocol: - trustedIPs: - - "{{ karo_compose_proxy_server_wireguard_ipv4 }}" -{% endif %} - http: - tls: - certResolver: cloudflare - domains: - - main: "*.{{ karo_compose_root_domain }}" - {{ karo_compose_traefik_custom_domains_raw }} - -# https://doc.traefik.io/traefik/reference/install-configuration/tls/certificate-resolvers/acme/ -certificatesResolvers: - cloudflare: - acme: - storage: /etc/traefik/acme/acme.json - email: {{ karo_compose_traefik_acme_email }} - keyType: EC384 -{% if karo_compose_traefik_acme_staging_enabled %} - caServer: {{ karo_compose_traefik_acme_staging_ca_server_url }} -{% else %} - caServer: {{ karo_compose_traefik_acme_ca_server_url }} -{% endif %} - dnsChallenge: - provider: cloudflare - resolvers: - - "1.1.1.1:53" - - "1.0.0.1:53" - -# https://doc.traefik.io/traefik/reference/routing-configuration/http/tls/tls-options/ -tls: - options: - default: - minVersion: VersionTLS13 - sniStrict: true - -# https://doc.traefik.io/traefik/reference/install-configuration/providers/docker/ -providers: - docker: - endpoint: "tcp://cetusguard:2375" - exposedByDefault: false - network: frontend # container connections diff --git a/roles/karo-compose/templates/extra/glance/compose.yml.j2 b/roles/karo-compose/templates/extra/glance/compose.yml.j2 deleted file mode 100644 index 9c830f6..0000000 --- a/roles/karo-compose/templates/extra/glance/compose.yml.j2 +++ /dev/null @@ -1,54 +0,0 @@ -# SPDX-FileCopyrightText: 2026 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/extra/glance - ---- -name: glance -services: - - glance: - image: {{ karo_compose_glance_image }}:{{ karo_compose_glance_version }} - container_name: glance - restart: {{ karo_compose_restart_policy }} - user: 1000:1000 - tty: false - stdin_open: false - read_only: true - security_opt: - - no-new-privileges:true - tmpfs: - - /tmp:rw,noexec,nosuid,nodev - cap_drop: - - ALL - networks: - - egress_glance - - frontend - volumes: - - type: bind - source: /etc/localtime - target: /etc/localtime - read_only: true - - type: bind - source: /srv/docker/extra/glance/glance.yml - target: /app/config/glance.yml - read_only: true - labels: - - traefik.enable=true - - traefik.http.routers.glance.rule=Host(`{{ karo_compose_glance_domain }}`) - - traefik.http.services.glance.loadbalancer.server.port=8080 -{% if karo_compose_glance_forward_auth_enabled %} - # forward auth - - traefik.http.routers.glance.middlewares=tinyauth - - tinyauth.apps.glance.config.domain={{ karo_compose_glance_domain }} - - tinyauth.apps.glance.oauth.groups={{ karo_compose_oidc_admin_group }} -{% endif %} - -networks: - egress_glance: - name: egress_glance - driver: bridge - internal: false - frontend: - external: true diff --git a/roles/karo-compose/templates/extra/glance/glance.yml.j2 b/roles/karo-compose/templates/extra/glance/glance.yml.j2 deleted file mode 100644 index a1fbd39..0000000 --- a/roles/karo-compose/templates/extra/glance/glance.yml.j2 +++ /dev/null @@ -1 +0,0 @@ -{{ karo_compose_glance_config_raw }} diff --git a/roles/karo-compose/templates/extra/gluetun/compose.yml.j2 b/roles/karo-compose/templates/extra/gluetun/compose.yml.j2 deleted file mode 100644 index 7efee8e..0000000 --- a/roles/karo-compose/templates/extra/gluetun/compose.yml.j2 +++ /dev/null @@ -1,44 +0,0 @@ -# SPDX-FileCopyrightText: 2025 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/extra/gluetun - ---- -name: gluetun -services: - - gluetun: - image: {{ karo_compose_gluetun_image }}:{{ karo_compose_gluetun_version }} - container_name: gluetun - restart: {{ karo_compose_restart_policy }} - cap_add: - - NET_ADMIN - devices: - - /dev/net/tun:/dev/net/tun - networks: - - egress_gluetun - - frontend - extra_hosts: - - "{{ karo_compose_oidc_domain }}:172.18.0.254" - environment: - - TZ={{ karo_compose_timezone }} - - LOG_LEVEL={{ karo_compose_gluetun_log_level }} - - VPN_SERVICE_PROVIDER=custom - - VPN_TYPE=wireguard - - FIREWALL_VPN_INPUT_PORTS={{ karo_compose_gluetun_firewall_vpn_input_ports }} - - WIREGUARD_CONF_SECRETFILE=/run/secrets/gluetun_wireguard_config_raw - secrets: - - gluetun_wireguard_config_raw - -networks: - egress_gluetun: - name: egress_gluetun - driver: bridge - internal: false - frontend: - external: true - -secrets: - gluetun_wireguard_config_raw: - file: /run/user/1001/karo/compose/gluetun_wireguard_config_raw diff --git a/roles/karo-compose/templates/extra/godns/compose.yml.j2 b/roles/karo-compose/templates/extra/godns/compose.yml.j2 deleted file mode 100644 index 2a3d4c9..0000000 --- a/roles/karo-compose/templates/extra/godns/compose.yml.j2 +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-FileCopyrightText: 2025 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/extra/godns - ---- -name: godns -services: - - godns: - image: {{ karo_compose_godns_image }}:{{ karo_compose_godns_version }} - container_name: godns - restart: {{ karo_compose_restart_policy }} - networks: - - egress_godns - volumes: - - type: bind - source: /srv/docker/extra/godns/config.json - target: /config.json - read_only: true - secrets: - - godns_dns_api_token - -networks: - egress_godns: - name: egress_godns - driver: bridge - internal: false - -secrets: - godns_dns_api_token: - file: /run/user/1001/karo/compose/godns_dns_api_token diff --git a/roles/karo-compose/templates/extra/godns/config.json.j2 b/roles/karo-compose/templates/extra/godns/config.json.j2 deleted file mode 100644 index 9127761..0000000 --- a/roles/karo-compose/templates/extra/godns/config.json.j2 +++ /dev/null @@ -1,21 +0,0 @@ -{ - "debug_info": {{ karo_compose_godns_debug_info_enabled | lower }}, - "provider": "Cloudflare", - "login_token_file": "/run/secrets/godns_dns_api_token", - "domains": [ - { - "domain_name": "{{ karo_compose_root_domain }}", - "sub_domains": ["{{ karo_compose_godns_subdomain }}"] - } - ], - "resolver": "1.1.1.1", - "ip_urls": [ - "https://api.ipify.org", - "https://myip.biturl.top", - "https://api-ipv4.ip.sb/ip" - ], - "ip_type": "IPv4", - "interval": 300, - "socks5_proxy": "", - "proxied": false -} diff --git a/roles/karo-compose/templates/extra/jellyfin/compose.yml.j2 b/roles/karo-compose/templates/extra/jellyfin/compose.yml.j2 deleted file mode 100644 index 357a0b5..0000000 --- a/roles/karo-compose/templates/extra/jellyfin/compose.yml.j2 +++ /dev/null @@ -1,52 +0,0 @@ -# SPDX-FileCopyrightText: 2025 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/extra/jellyfin - ---- -name: jellyfin -services: - - jellyfin: - image: {{ karo_compose_jellyfin_image }}:{{ karo_compose_jellyfin_version }} - container_name: jellyfin - restart: {{ karo_compose_restart_policy }} - networks: - - egress_jellyfin - - frontend - volumes: - - type: bind - source: {{ karo_compose_jellyfin_movies_media_path }} - target: /data/media/movies - read_only: true - - type: bind - source: {{ karo_compose_jellyfin_series_media_path }} - target: /data/media/series - read_only: true - - type: volume - source: jellyfin_data - target: /config - - type: volume - source: jellyfin_cache - target: /cache - labels: - - traefik.enable=true - - traefik.http.routers.jellyfin.rule=Host(`{{ karo_compose_jellyfin_domain }}`) - - traefik.http.services.jellyfin.loadbalancer.server.port=8096 - environment: - - TZ={{ karo_compose_timezone }} - -networks: - egress_jellyfin: - name: egress_jellyfin - driver: bridge - internal: false - frontend: - external: true - -volumes: - jellyfin_data: - name: jellyfin_data - jellyfin_cache: - name: jellyfin_cache diff --git a/roles/karo-compose/templates/extra/ntfy/compose.yml.j2 b/roles/karo-compose/templates/extra/ntfy/compose.yml.j2 deleted file mode 100644 index a1c3269..0000000 --- a/roles/karo-compose/templates/extra/ntfy/compose.yml.j2 +++ /dev/null @@ -1,64 +0,0 @@ -# SPDX-FileCopyrightText: 2026 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/extra/ntfy - ---- -name: ntfy -services: - - ntfy: - image: {{ karo_compose_ntfy_image }}:{{ karo_compose_ntfy_version }} - container_name: ntfy - restart: {{ karo_compose_restart_policy }} - # user: 1000:1000 # issue 1276 - read_only: true - security_opt: - - no-new-privileges:true - init: true # for healthcheck - command: - - serve - networks: - - egress_ntfy - - frontend - volumes: - - type: volume - source: ntfy_data - target: /var/lib/ntfy - labels: - - traefik.enable=true - - traefik.http.routers.ntfy.rule=Host(`{{ karo_compose_ntfy_domain }}`) - - traefik.http.services.ntfy.loadbalancer.server.port=80 - environment: - - TZ={{ karo_compose_timezone }} - - NTFY_BASE_URL=https://{{ karo_compose_ntfy_domain }} - - NTFY_CACHE_FILE=/var/lib/ntfy/cache.db - - NTFY_AUTH_FILE=/var/lib/ntfy/auth.db - - NTFY_AUTH_DEFAULT_ACCESS=deny-all - - NTFY_BEHIND_PROXY=true - - NTFY_ATTACHMENT_CACHE_DIR=/var/lib/ntfy/attachments - - NTFY_UPSTREAM_BASE_URL=https://ntfy.sh - - NTFY_WEB_ROOT=disable - - NTFY_LOG_LEVEL={{ karo_compose_ntfy_log_level }} - - NTFY_AUTH_USERS={{ karo_compose_ntfy_auth_users }} - - NTFY_AUTH_ACCESS={{ karo_compose_ntfy_auth_access }} - - NTFY_AUTH_TOKENS={{ karo_compose_ntfy_auth_tokens }} - healthcheck: - test: ["CMD-SHELL", "wget -q --tries=1 http://localhost:80/v1/health -O - | grep -Eo '\"healthy\"\\s*:\\s*true' || exit 1"] - interval: 60s - timeout: 10s - retries: 3 - start_period: 40s - -networks: - egress_ntfy: - name: egress_ntfy - driver: bridge - internal: false - frontend: - external: true - -volumes: - ntfy_data: - name: ntfy_data diff --git a/roles/karo-compose/templates/extra/prowlarr/compose.yml.j2 b/roles/karo-compose/templates/extra/prowlarr/compose.yml.j2 deleted file mode 100644 index 5d6bfb9..0000000 --- a/roles/karo-compose/templates/extra/prowlarr/compose.yml.j2 +++ /dev/null @@ -1,37 +0,0 @@ -# SPDX-FileCopyrightText: 2025 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/extra/prowlarr - ---- -name: prowlarr -services: - - prowlarr: - image: {{ karo_compose_prowlarr_image }}:{{ karo_compose_prowlarr_version }} - container_name: prowlarr - restart: {{ karo_compose_restart_policy }} - network_mode: "container:gluetun" - volumes: - - type: volume - source: prowlarr_data - target: /config - labels: - - traefik.enable=true - - traefik.http.routers.prowlarr.rule=Host(`{{ karo_compose_prowlarr_domain }}`) - - traefik.http.services.prowlarr.loadbalancer.server.port=9696 -{% if karo_compose_prowlarr_forward_auth_enabled %} - # forward auth - - traefik.http.routers.prowlarr.middlewares=tinyauth - - tinyauth.apps.prowlarr.config.domain={{ karo_compose_prowlarr_domain }} - - tinyauth.apps.prowlarr.oauth.groups={{ karo_compose_oidc_admin_group }} -{% endif %} - environment: - - TZ={{ karo_compose_timezone }} - - PUID=1000 - - PGID=1000 - -volumes: - prowlarr_data: - name: prowlarr_data diff --git a/roles/karo-compose/templates/extra/proxy/compose.yml.j2 b/roles/karo-compose/templates/extra/proxy/compose.yml.j2 deleted file mode 100644 index 0f0ba7a..0000000 --- a/roles/karo-compose/templates/extra/proxy/compose.yml.j2 +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-FileCopyrightText: 2026 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/extra/proxy - ---- -name: proxy -services: - - haproxy: - image: {{ karo_compose_proxy_haproxy_image }}:{{ karo_compose_proxy_haproxy_version }} - container_name: haproxy - restart: {{ karo_compose_restart_policy }} - ports: - - "0.0.0.0:80:80/tcp" - - "0.0.0.0:443:443/tcp" - networks: - - egress_haproxy - volumes: - - type: bind - source: /srv/docker/extra/proxy/haproxy.cfg - target: /usr/local/etc/haproxy/haproxy.cfg - read_only: true - -networks: - egress_haproxy: - name: egress_haproxy - driver: bridge - internal: false diff --git a/roles/karo-compose/templates/extra/proxy/haproxy.cfg.j2 b/roles/karo-compose/templates/extra/proxy/haproxy.cfg.j2 deleted file mode 100644 index aaae45a..0000000 --- a/roles/karo-compose/templates/extra/proxy/haproxy.cfg.j2 +++ /dev/null @@ -1,39 +0,0 @@ -# SPDX-FileCopyrightText: 2026 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - - -global - log stdout format raw local0 - stats timeout 30s - -defaults - # logging - log global - option dontlognull - option tcplog - - # connections - option tcpka - option tcp-smart-accept - option tcp-smart-connect - - # timeouts - timeout connect 10s - timeout check 10s - timeout client 30s - timeout server 30s - timeout tunnel 1h - -frontend http - bind *:80 - mode http - - # https redirect - http-request redirect scheme https code 301 if !{ ssl_fc } - -frontend https - bind *:443 - mode tcp - - {{ karo_compose_proxy_haproxy_config_raw }} diff --git a/roles/karo-compose/templates/extra/qbittorrent/compose.yml.j2 b/roles/karo-compose/templates/extra/qbittorrent/compose.yml.j2 deleted file mode 100644 index 73080b6..0000000 --- a/roles/karo-compose/templates/extra/qbittorrent/compose.yml.j2 +++ /dev/null @@ -1,80 +0,0 @@ -# SPDX-FileCopyrightText: 2025 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/extra/qbittorrent - ---- -name: qbittorrent -services: - - qbittorrent: - image: {{ karo_compose_qbittorrent_image }}:{{ karo_compose_qbittorrent_version }} - container_name: qbittorrent - restart: {{ karo_compose_restart_policy }} - stop_grace_period: 30m - tty: false - stdin_open: false - read_only: false - security_opt: - - no-new-privileges:true - tmpfs: - - /tmp:rw,noexec,nosuid,nodev - network_mode: "container:gluetun" - volumes: - - type: bind - source: {{ karo_compose_qbittorrent_downloads_path }} - target: /data/torrents - - type: volume - source: qbittorrent_data - target: /config - labels: - - traefik.enable={{ karo_compose_qbittorrent_webui_enabled | lower }} - - traefik.http.routers.qbittorrent.rule=Host(`{{ karo_compose_qbittorrent_domain }}`) - - traefik.http.services.qbittorrent.loadbalancer.server.port=9814 - environment: - - TZ={{ karo_compose_timezone }} - - PUID=0 - - PGID=0 - - QBT_LEGAL_NOTICE=confirm - - QBT_WEBUI_PORT=9814 - - qui: - image: {{ karo_compose_qbittorrent_qui_image }}:{{ karo_compose_qbittorrent_qui_version }} - container_name: qui - restart: {{ karo_compose_restart_policy }} - network_mode: "container:gluetun" - volumes: - - type: volume - source: qui_data - target: /config - labels: - - traefik.enable=true - - traefik.http.routers.qui.rule=Host(`{{ karo_compose_qbittorrent_qui_domain }}`) - - traefik.http.services.qui.loadbalancer.server.port=7476 - environment: - - QUI__LOG_LEVEL={{ karo_compose_qbittorrent_qui_log_level | upper }} - - QUI__SESSION_SECRET_FILE=/run/secrets/qui_session_secret - - QUI__OIDC_ENABLED=true - - QUI__OIDC_ISSUER={{ karo_compose_oidc_url }} - - QUI__OIDC_CLIENT_ID={{ karo_compose_qbittorrent_qui_oidc_client_id }} - - QUI__OIDC_CLIENT_SECRET_FILE=/run/secrets/qui_oidc_client_secret - - QUI__OIDC_REDIRECT_URL=https://{{ karo_compose_qbittorrent_qui_domain }}/api/auth/oidc/callback - - QUI__OIDC_DISABLE_BUILT_IN_LOGIN=true - secrets: - - qui_session_secret - - qui_oidc_client_secret - depends_on: - - qbittorrent - -volumes: - qbittorrent_data: - name: qbittorrent_data - qui_data: - name: qui_data - -secrets: - qui_session_secret: - file: /run/user/1001/karo/compose/qui_session_secret - qui_oidc_client_secret: - file: /run/user/1001/karo/compose/qui_oidc_client_secret diff --git a/roles/karo-compose/templates/extra/radarr/compose.yml.j2 b/roles/karo-compose/templates/extra/radarr/compose.yml.j2 deleted file mode 100644 index fed38bb..0000000 --- a/roles/karo-compose/templates/extra/radarr/compose.yml.j2 +++ /dev/null @@ -1,40 +0,0 @@ -# SPDX-FileCopyrightText: 2026 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/extra/radarr - ---- -name: radarr -services: - - radarr: - image: {{ karo_compose_radarr_image }}:{{ karo_compose_radarr_version }} - container_name: radarr - restart: {{ karo_compose_restart_policy }} - network_mode: "container:gluetun" - volumes: - - type: bind - source: {{ karo_compose_radarr_data_path }} - target: /data - - type: volume - source: radarr_data - target: /config - labels: - - traefik.enable=true - - traefik.http.routers.radarr.rule=Host(`{{ karo_compose_radarr_domain }}`) - - traefik.http.services.radarr.loadbalancer.server.port=7878 -{% if karo_compose_radarr_forward_auth_enabled %} - # forward auth - - traefik.http.routers.radarr.middlewares=tinyauth - - tinyauth.apps.radarr.config.domain={{ karo_compose_radarr_domain }} - - tinyauth.apps.radarr.oauth.groups={{ karo_compose_oidc_admin_group }} -{% endif %} - environment: - - TZ={{ karo_compose_timezone }} - - PUID=0 - - PGID=0 - -volumes: - radarr_data: - name: radarr_data diff --git a/roles/karo-compose/templates/extra/seerr/compose.yml.j2 b/roles/karo-compose/templates/extra/seerr/compose.yml.j2 deleted file mode 100644 index 5544f1c..0000000 --- a/roles/karo-compose/templates/extra/seerr/compose.yml.j2 +++ /dev/null @@ -1,47 +0,0 @@ -# SPDX-FileCopyrightText: 2025 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/extra/seerr - ---- -name: seerr -services: - - seerr: - image: {{ karo_compose_seerr_image }}:{{ karo_compose_seerr_version }} - container_name: seerr - restart: {{ karo_compose_restart_policy }} - init: true - networks: - - egress_seerr - - frontend - volumes: - - type: volume - source: seerr_data - target: /app/config - labels: - - traefik.enable=true - - traefik.http.routers.seerr.rule=Host(`{{ karo_compose_seerr_domain }}`) - - traefik.http.services.seerr.loadbalancer.server.port=5055 - environment: - - LOG_LEVEL={{ karo_compose_seerr_log_level }} - - TZ={{ karo_compose_timezone }} - healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:5055/api/v1/settings/public || exit 1"] - interval: 15s - timeout: 3s - retries: 3 - start_period: 20s - -networks: - egress_seerr: - name: egress_seerr - driver: bridge - internal: false - frontend: - external: true - -volumes: - seerr_data: - name: seerr_data diff --git a/roles/karo-compose/templates/extra/sonarr/compose.yml.j2 b/roles/karo-compose/templates/extra/sonarr/compose.yml.j2 deleted file mode 100644 index 1e4e72b..0000000 --- a/roles/karo-compose/templates/extra/sonarr/compose.yml.j2 +++ /dev/null @@ -1,40 +0,0 @@ -# SPDX-FileCopyrightText: 2026 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/extra/sonarr - ---- -name: sonarr -services: - - sonarr: - image: {{ karo_compose_sonarr_image }}:{{ karo_compose_sonarr_version }} - container_name: sonarr - restart: {{ karo_compose_restart_policy }} - network_mode: "container:gluetun" - volumes: - - type: bind - source: {{ karo_compose_sonarr_data_path }} - target: /data - - type: volume - source: sonarr_data - target: /config - labels: - - traefik.enable=true - - traefik.http.routers.sonarr.rule=Host(`{{ karo_compose_sonarr_domain }}`) - - traefik.http.services.sonarr.loadbalancer.server.port=8989 -{% if karo_compose_sonarr_forward_auth_enabled %} - # forward auth - - traefik.http.routers.sonarr.middlewares=tinyauth - - tinyauth.apps.sonarr.config.domain={{ karo_compose_sonarr_domain }} - - tinyauth.apps.sonarr.oauth.groups={{ karo_compose_oidc_admin_group }} -{% endif %} - environment: - - TZ={{ karo_compose_timezone }} - - PUID=0 - - PGID=0 - -volumes: - sonarr_data: - name: sonarr_data diff --git a/roles/karo-compose/templates/extra/whoami/compose.yml.j2 b/roles/karo-compose/templates/extra/whoami/compose.yml.j2 deleted file mode 100644 index 7cecc4a..0000000 --- a/roles/karo-compose/templates/extra/whoami/compose.yml.j2 +++ /dev/null @@ -1,34 +0,0 @@ -# SPDX-FileCopyrightText: 2025 hazzuk -# -# SPDX-License-Identifier: AGPL-3.0-only - -# https://docs.karolabs.dev/stacks/extra/whoami - ---- -name: whoami -services: - - whoami: - image: {{ karo_compose_whoami_image }}:{{ karo_compose_whoami_version }} - container_name: whoami - restart: {{ karo_compose_restart_policy }} - networks: - - frontend - labels: - - traefik.enable=true - - traefik.http.routers.whoami.rule=Host(`{{ karo_compose_whoami_domain }}`) - # - traefik.http.routers.whoami.tls=true - # - traefik.http.routers.whoami.tls.certresolver=cloudflare - # - traefik.http.routers.dashboard.entrypoints=websecure - - traefik.http.services.whoami.loadbalancer.server.port=80 -{% if karo_compose_whoami_forward_auth_enabled %} - # forward auth - - traefik.http.routers.whoami.middlewares=tinyauth - - tinyauth.apps.whoami.config.domain={{ karo_compose_whoami_domain }} - - tinyauth.apps.whoami.oauth.groups={{ karo_compose_oidc_admin_group }} - - tinyauth.apps.whoami.path.allow=^\/api -{% endif %} - -networks: - frontend: - external: true From 0336bd7179a014a6a7101fdcc04cbb500663857d Mon Sep 17 00:00:00 2001 From: hazzuk Date: Tue, 23 Jun 2026 17:38:46 +0100 Subject: [PATCH 02/30] feat(justfile): add recipe to symlink custom stacks --- justfile | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/justfile b/justfile index 9fd8797..2820cd9 100644 --- a/justfile +++ b/justfile @@ -103,3 +103,22 @@ wireguard: pub="$(wg pubkey <<<"$priv")"; \ printf 'Private key: %s\n' "$priv"; \ printf 'Public key: %s\n' "$pub" + + +# custom stacks + +@_stack-remove: + # remove role defaults + find roles/karo-compose/defaults/ -mindepth 1 ! -name "main.yml" -delete + # remove role templates + find roles/karo-compose/templates/ -mindepth 1 -delete + +@_stack-add: + # symlink custom defaults + ln -sr custom/*/defaults/main/*.yml roles/karo-compose/defaults/ + # symlink custom templates + ln -sr custom/*/templates/*/ roles/karo-compose/templates/ + +@stack: + just _stack-remove + just _stack-add From d597a566df7e89195e7bdbbeff260892672afe5f Mon Sep 17 00:00:00 2001 From: hazzuk Date: Tue, 23 Jun 2026 17:41:14 +0100 Subject: [PATCH 03/30] refactor(compose): create oidc defaults dictionary --- roles/karo-compose/defaults/main.yml | 53 ++++++++++++++-------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/roles/karo-compose/defaults/main.yml b/roles/karo-compose/defaults/main.yml index bfe1c81..3a6f9a8 100644 --- a/roles/karo-compose/defaults/main.yml +++ b/roles/karo-compose/defaults/main.yml @@ -18,30 +18,29 @@ karo_compose_timezone: "Europe/London" karo_compose_root_domain: example.com -# oidc - -karo_compose_oidc_subdomain: auth -karo_compose_oidc_domain: "{{ karo_compose_oidc_subdomain }}.{{ karo_compose_root_domain }}" # e.g. auth.example.com -karo_compose_oidc_url: "https://{{ karo_compose_oidc_domain }}" # e.g. https://auth.example.com - -karo_compose_oidc_authorization_path: "/authorize" -karo_compose_oidc_authorization_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_authorization_path }}" - -karo_compose_oidc_discovery_path: "/.well-known/openid-configuration" -karo_compose_oidc_discovery_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_discovery_path }}" - -karo_compose_oidc_token_path: "/api/oidc/token" -karo_compose_oidc_token_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_token_path }}" - -karo_compose_oidc_userinfo_path: "/api/oidc/userinfo" -karo_compose_oidc_userinfo_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_userinfo_path }}" - -karo_compose_oidc_logout_path: "/api/oidc/end-session" -karo_compose_oidc_logout_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_logout_path }}" - -karo_compose_oidc_certificate_path: "/.well-known/jwks.json" -karo_compose_oidc_certificate_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_certificate_path }}" - -karo_compose_oidc_provider_name: "PocketID" - -karo_compose_oidc_admin_group: "admin" +karo_compose_auth: + provider_name: PocketID + admin_group: admin + subdomain: auth + +karo_compose_oidc: + domain: "{{ karo_compose_auth.subdomain }}.{{ karo_compose_root_domain }}" # e.g. auth.example.com + url: "https://{{ karo_compose_oidc.domain }}" # e.g. https://auth.example.com + authorization: + path: "/authorize" + url: "{{ karo_compose_oidc.url }}{{ karo_compose_oidc.authorization.path }}" + discovery: + path: "/.well-known/openid-configuration" + url: "{{ karo_compose_oidc.url }}{{ karo_compose_oidc.discovery.path }}" + token: + path: "/api/oidc/token" + url: "{{ karo_compose_oidc.url }}{{ karo_compose_oidc.token.path }}" + userinfo: + path: "/api/oidc/userinfo" + url: "{{ karo_compose_oidc.url }}{{ karo_compose_oidc.userinfo.path }}" + logout: + path: "/api/oidc/end-session" + url: "{{ karo_compose_oidc.url }}{{ karo_compose_oidc.logout.path }}" + certificate: + path: "/.well-known/jwks.json" + url: "{{ karo_compose_oidc.url }}{{ karo_compose_oidc.certificate.path }}" From ecc3f9869c1a23661444fa2bacea4091d19fa40e Mon Sep 17 00:00:00 2001 From: hazzuk Date: Tue, 23 Jun 2026 17:56:25 +0100 Subject: [PATCH 04/30] refactor(compose): create role defaults sub-dir Required by Ansible to use multiple defaults files. --- justfile | 4 ++-- roles/karo-compose/defaults/{ => main}/main.yml | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename roles/karo-compose/defaults/{ => main}/main.yml (100%) diff --git a/justfile b/justfile index 2820cd9..b943fd0 100644 --- a/justfile +++ b/justfile @@ -109,13 +109,13 @@ wireguard: @_stack-remove: # remove role defaults - find roles/karo-compose/defaults/ -mindepth 1 ! -name "main.yml" -delete + find roles/karo-compose/defaults/main/ -mindepth 1 ! -name "main.yml" -delete # remove role templates find roles/karo-compose/templates/ -mindepth 1 -delete @_stack-add: # symlink custom defaults - ln -sr custom/*/defaults/main/*.yml roles/karo-compose/defaults/ + ln -sr custom/*/defaults/main/*.yml roles/karo-compose/defaults/main/ # symlink custom templates ln -sr custom/*/templates/*/ roles/karo-compose/templates/ diff --git a/roles/karo-compose/defaults/main.yml b/roles/karo-compose/defaults/main/main.yml similarity index 100% rename from roles/karo-compose/defaults/main.yml rename to roles/karo-compose/defaults/main/main.yml From c4ac087339c829dabf68237b05a4c22e803c8b68 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Wed, 24 Jun 2026 01:21:09 +0100 Subject: [PATCH 05/30] refactor(compose): switch to a stack_groups list Stack groups and stack names are now defined separately, allowing for custom stacks. --- roles/karo-compose/defaults/main/main.yml | 6 +++--- roles/karo-compose/tasks/main.yml | 21 +++++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/roles/karo-compose/defaults/main/main.yml b/roles/karo-compose/defaults/main/main.yml index 3a6f9a8..5493cb5 100644 --- a/roles/karo-compose/defaults/main/main.yml +++ b/roles/karo-compose/defaults/main/main.yml @@ -8,9 +8,9 @@ karo_compose_justfile_stack: all # docker will start stacks in this order # docker will stop stacks in reverse order -# karo_compose_stacks: -# core: -# extra: +# karo_compose_stack_groups: +# - hazzuk_core +# - hazzuk_extra karo_compose_restart_policy: unless-stopped diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index 672e822..8230306 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -19,16 +19,17 @@ - name: Define expanded stack list ansible.builtin.set_fact: - karo_compose_stacks_expanded: >- - {{ karo_compose_stacks_expanded | default([]) + [ - { - 'group': item.0.key, - 'name': item.1 - } - ] }} - loop: "{{ karo_compose_stacks | dict2items | subelements('value') }}" - loop_control: - label: "{{ item.0.key }}/{{ item.1 }}" + karo_compose_stacks_expanded: | + {%- set expanded_stacks = [] -%} + {%- for group in karo_compose_stack_groups -%} + {%- for stack in lookup('vars', group) -%} + {%- set _ = expanded_stacks.append({ + 'group': group, + 'name': stack + }) -%} + {%- endfor -%} + {%- endfor -%} + {{ expanded_stacks }} - name: Assert stack name ansible.builtin.assert: From d3121e8bb89b9b6255edda860749303453ab54a5 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Wed, 24 Jun 2026 01:38:28 +0100 Subject: [PATCH 06/30] refactor(compose): rename expanded stacks variable --- roles/karo-compose/tasks/main.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index 8230306..e3a049e 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -6,6 +6,8 @@ --- +# Setup + - name: Check docker directory status ansible.builtin.stat: path: /srv/docker @@ -19,7 +21,7 @@ - name: Define expanded stack list ansible.builtin.set_fact: - karo_compose_stacks_expanded: | + karo_compose_expanded_stacks: | {%- set expanded_stacks = [] -%} {%- for group in karo_compose_stack_groups -%} {%- for stack in lookup('vars', group) -%} @@ -34,12 +36,14 @@ - name: Assert stack name ansible.builtin.assert: that: karo_compose_justfile_stack in ( - karo_compose_stacks_expanded | map(attribute='name') | list + karo_compose_expanded_stacks | map(attribute='name') | list + ['all'] ) fail_msg: "Invalid stack name '{{ karo_compose_justfile_stack }}'." quiet: true +# Actions + - name: Down stacks become: true become_user: dockeruser @@ -47,7 +51,7 @@ block: - name: Down docker compose stacks ansible.builtin.include_tasks: down.yml - loop: "{{ karo_compose_stacks_expanded | reverse }}" + loop: "{{ karo_compose_expanded_stacks | reverse }}" loop_control: label: "{{ item.name }}" vars: @@ -64,7 +68,7 @@ block: - name: Deploy docker compose stacks ansible.builtin.include_tasks: deploy.yml - loop: "{{ karo_compose_stacks_expanded }}" + loop: "{{ karo_compose_expanded_stacks }}" loop_control: label: "{{ item.name }}" vars: @@ -81,7 +85,7 @@ block: - name: Up docker compose stacks ansible.builtin.include_tasks: up.yml - loop: "{{ karo_compose_stacks_expanded }}" + loop: "{{ karo_compose_expanded_stacks }}" loop_control: label: "{{ item.name }}" vars: From 2253fa89ffd41e8b86fe121d51607ea8359872af Mon Sep 17 00:00:00 2001 From: hazzuk Date: Wed, 24 Jun 2026 03:35:17 +0100 Subject: [PATCH 07/30] refactor(compose): reword setup task names --- roles/karo-compose/tasks/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index e3a049e..3fc6e2a 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -8,7 +8,7 @@ # Setup -- name: Check docker directory status +- name: Register docker directory status ansible.builtin.stat: path: /srv/docker register: docker_dir @@ -19,7 +19,7 @@ fail_msg: "Docker directory not present, run `just install` to setup Docker." quiet: true -- name: Define expanded stack list +- name: Set expanded stack list ansible.builtin.set_fact: karo_compose_expanded_stacks: | {%- set expanded_stacks = [] -%} @@ -33,7 +33,7 @@ {%- endfor -%} {{ expanded_stacks }} -- name: Assert stack name +- name: Assert provided stack name ansible.builtin.assert: that: karo_compose_justfile_stack in ( karo_compose_expanded_stacks | map(attribute='name') | list From 03c15de0c6368f9284f7b57a17c42fd70c6965d7 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Wed, 24 Jun 2026 15:42:47 +0100 Subject: [PATCH 08/30] feat(compose): add stack group asserts --- roles/karo-compose/tasks/deploy.yml | 2 +- roles/karo-compose/tasks/main.yml | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/roles/karo-compose/tasks/deploy.yml b/roles/karo-compose/tasks/deploy.yml index e608884..1fce412 100644 --- a/roles/karo-compose/tasks/deploy.yml +++ b/roles/karo-compose/tasks/deploy.yml @@ -15,7 +15,7 @@ src: "{{ template.src }}" dest: "/srv/docker/{{ group }}/{{ stack }}/{{ template.path | splitext | first }}" mode: "0644" + with_community.general.filetree: ../templates/{{ group }}/{{ stack }}/ loop_control: loop_var: template label: "{{ template.path }}" - with_community.general.filetree: ../templates/{{ group }}/{{ stack }}/ diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index 3fc6e2a..ab502cb 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -19,6 +19,29 @@ fail_msg: "Docker directory not present, run `just install` to setup Docker." quiet: true +- name: Assert stack group names + ansible.builtin.assert: + that: + - stack_group_lookup is not false + - stack_group_lookup | type_debug == "list" + fail_msg: "Invalid stack group '{{ stack_group }}' in 'karo_compose_stack_groups'." + quiet: true + loop: "{{ karo_compose_stack_groups }}" + loop_control: + loop_var: stack_group + vars: + stack_group_lookup: "{{ lookup('vars', stack_group, default=false) }}" + +- name: Assert imported stack groups + ansible.builtin.assert: + that: stack_group_dir.path in karo_compose_stack_groups + fail_msg: "Stack group '{{ stack_group_dir.path }}' missing from 'karo_compose_stack_groups'." + quiet: true + with_community.general.filetree: ../templates/ + loop_control: + loop_var: stack_group_dir + label: "{{ stack_group_dir.path }}" + - name: Set expanded stack list ansible.builtin.set_fact: karo_compose_expanded_stacks: | From 07fae53d37c46612dffaf87450cbbc029a46069e Mon Sep 17 00:00:00 2001 From: hazzuk Date: Wed, 24 Jun 2026 16:41:38 +0100 Subject: [PATCH 09/30] feat(compose): improve asserts logging clarity --- roles/karo-compose/tasks/main.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index ab502cb..1aa283b 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -29,6 +29,7 @@ loop: "{{ karo_compose_stack_groups }}" loop_control: loop_var: stack_group + label: "karo_compose_stack_groups -> {{ stack_group }}" vars: stack_group_lookup: "{{ lookup('vars', stack_group, default=false) }}" @@ -40,7 +41,7 @@ with_community.general.filetree: ../templates/ loop_control: loop_var: stack_group_dir - label: "{{ stack_group_dir.path }}" + label: "templates/{{ stack_group_dir.path }}/" - name: Set expanded stack list ansible.builtin.set_fact: From d3d6eb162fb023f6f449b276e37af9ab23cc149c Mon Sep 17 00:00:00 2001 From: hazzuk Date: Wed, 24 Jun 2026 16:45:24 +0100 Subject: [PATCH 10/30] refactor(compose): use new variables for lookups --- roles/karo-compose/tasks/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index 1aa283b..1ca1e27 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -82,7 +82,7 @@ stack: "{{ item.name }}" group: "{{ item.group }}" when: - - lookup('vars', 'karo_compose_' ~ stack ~ '_enabled') | default(false) + - lookup('vars', group ~ '_' ~ stack ~ '_enabled') | default(false) - karo_compose_justfile_stack in [stack, 'all'] - name: Deploy stacks @@ -99,7 +99,7 @@ stack: "{{ item.name }}" group: "{{ item.group }}" when: - - lookup('vars', 'karo_compose_' ~ stack ~ '_enabled') | default(false) + - lookup('vars', group ~ '_' ~ stack ~ '_enabled') | default(false) - karo_compose_justfile_stack in [stack, 'all'] - name: Create stacks @@ -115,9 +115,9 @@ vars: stack: "{{ item.name }}" group: "{{ item.group }}" - secrets: "{{ lookup('vars', 'karo_compose_' ~ stack ~ '_secrets', default={}) | dict2items }}" + secrets: "{{ lookup('vars', group ~ '_' ~ stack ~ '_secrets', default={}) | dict2items }}" when: - - lookup('vars', 'karo_compose_' ~ stack ~ '_enabled') | default(false) + - lookup('vars', group ~ '_' ~ stack ~ '_enabled') | default(false) - karo_compose_justfile_stack in [stack, 'all'] always: From f2b1ff3843bc70148a93ffbb2b2e036e9c1408ae Mon Sep 17 00:00:00 2001 From: hazzuk Date: Wed, 24 Jun 2026 18:08:35 +0100 Subject: [PATCH 11/30] refactor(compose): move lookups to expanded_stacks --- roles/karo-compose/tasks/main.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index 1ca1e27..c024e09 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -50,8 +50,10 @@ {%- for group in karo_compose_stack_groups -%} {%- for stack in lookup('vars', group) -%} {%- set _ = expanded_stacks.append({ + 'enabled': lookup('vars', group ~ '_' ~ stack ~ '_enabled'), 'group': group, - 'name': stack + 'name': stack, + 'secrets': lookup('vars', group ~ '_' ~ stack ~ '_secrets', default={}) | dict2items }) -%} {%- endfor -%} {%- endfor -%} @@ -82,7 +84,7 @@ stack: "{{ item.name }}" group: "{{ item.group }}" when: - - lookup('vars', group ~ '_' ~ stack ~ '_enabled') | default(false) + - item.enabled - karo_compose_justfile_stack in [stack, 'all'] - name: Deploy stacks @@ -99,7 +101,7 @@ stack: "{{ item.name }}" group: "{{ item.group }}" when: - - lookup('vars', group ~ '_' ~ stack ~ '_enabled') | default(false) + - item.enabled - karo_compose_justfile_stack in [stack, 'all'] - name: Create stacks @@ -115,9 +117,9 @@ vars: stack: "{{ item.name }}" group: "{{ item.group }}" - secrets: "{{ lookup('vars', group ~ '_' ~ stack ~ '_secrets', default={}) | dict2items }}" + secrets: "{{ item.secrets }}" when: - - lookup('vars', group ~ '_' ~ stack ~ '_enabled') | default(false) + - item.enabled - karo_compose_justfile_stack in [stack, 'all'] always: From 5f612db5246e341164d267b64c5013dd2addac1d Mon Sep 17 00:00:00 2001 From: hazzuk Date: Wed, 24 Jun 2026 19:24:13 +0100 Subject: [PATCH 12/30] refactor(compose): specify loop var for main tasks --- roles/karo-compose/tasks/main.yml | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index c024e09..38c81b9 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -79,12 +79,13 @@ ansible.builtin.include_tasks: down.yml loop: "{{ karo_compose_expanded_stacks | reverse }}" loop_control: - label: "{{ item.name }}" + loop_var: expanded_stack + label: "{{ stack }}" vars: - stack: "{{ item.name }}" - group: "{{ item.group }}" + stack: "{{ expanded_stack.name }}" + group: "{{ expanded_stack.group }}" when: - - item.enabled + - expanded_stack.enabled - karo_compose_justfile_stack in [stack, 'all'] - name: Deploy stacks @@ -96,12 +97,13 @@ ansible.builtin.include_tasks: deploy.yml loop: "{{ karo_compose_expanded_stacks }}" loop_control: - label: "{{ item.name }}" + loop_var: expanded_stack + label: "{{ stack }}" vars: - stack: "{{ item.name }}" - group: "{{ item.group }}" + stack: "{{ expanded_stack.name }}" + group: "{{ expanded_stack.group }}" when: - - item.enabled + - expanded_stack.enabled - karo_compose_justfile_stack in [stack, 'all'] - name: Create stacks @@ -113,13 +115,14 @@ ansible.builtin.include_tasks: up.yml loop: "{{ karo_compose_expanded_stacks }}" loop_control: - label: "{{ item.name }}" + loop_var: expanded_stack + label: "{{ stack }}" vars: - stack: "{{ item.name }}" - group: "{{ item.group }}" - secrets: "{{ item.secrets }}" + stack: "{{ expanded_stack.name }}" + group: "{{ expanded_stack.group }}" + secrets: "{{ expanded_stack.secrets }}" when: - - item.enabled + - expanded_stack.enabled - karo_compose_justfile_stack in [stack, 'all'] always: From 49b76163cc2abc1910c5d177fd072711b74ded22 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Wed, 24 Jun 2026 23:37:31 +0100 Subject: [PATCH 13/30] chore: organise gitignore file --- .gitignore | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e942f6e..5718e08 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ +# ansible + /.ansible -/.dev /inventory + +# misc + +/.dev /cspell.json From b44204ac4cedbf6b4f06070e316e3d7eb5b88f57 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Wed, 24 Jun 2026 23:42:32 +0100 Subject: [PATCH 14/30] chore: configure gitignore for custom stacks This setup keeps tracking the karo-compose role's defaults and templates directories. --- .gitignore | 10 ++++++++++ roles/karo-compose/tasks/main.yml | 2 +- roles/karo-compose/templates/.gitkeep | 0 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 roles/karo-compose/templates/.gitkeep diff --git a/.gitignore b/.gitignore index 5718e08..780d380 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,16 @@ /.ansible /inventory +# custom stacks + +/custom + +/roles/karo-compose/defaults/main/* +!/roles/karo-compose/defaults/main/main.yml + +/roles/karo-compose/templates/* +!/roles/karo-compose/templates/.gitkeep + # misc /.dev diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index 38c81b9..c9462e1 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -38,7 +38,7 @@ that: stack_group_dir.path in karo_compose_stack_groups fail_msg: "Stack group '{{ stack_group_dir.path }}' missing from 'karo_compose_stack_groups'." quiet: true - with_community.general.filetree: ../templates/ + with_community.general.filetree: ../templates/, exclude='^\.gitkeep$' loop_control: loop_var: stack_group_dir label: "templates/{{ stack_group_dir.path }}/" diff --git a/roles/karo-compose/templates/.gitkeep b/roles/karo-compose/templates/.gitkeep new file mode 100644 index 0000000..e69de29 From 51c69ce7fd69fa195740d850288d5b79f74594e7 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Sun, 28 Jun 2026 01:19:53 +0100 Subject: [PATCH 15/30] refactor(compose): set secrets after expanding stacks Not all stacks will have their configuration present. This will now only set secrets 'when' the stack is 'enabled'. It's then safe to assume the user has defined the required secrets. --- roles/karo-compose/tasks/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index c9462e1..386d70c 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -53,7 +53,6 @@ 'enabled': lookup('vars', group ~ '_' ~ stack ~ '_enabled'), 'group': group, 'name': stack, - 'secrets': lookup('vars', group ~ '_' ~ stack ~ '_secrets', default={}) | dict2items }) -%} {%- endfor -%} {%- endfor -%} @@ -120,7 +119,7 @@ vars: stack: "{{ expanded_stack.name }}" group: "{{ expanded_stack.group }}" - secrets: "{{ expanded_stack.secrets }}" + secrets: "{{ lookup('vars', group ~ '_' ~ stack ~ '_secrets', default={}) | dict2items }}" when: - expanded_stack.enabled - karo_compose_justfile_stack in [stack, 'all'] From 5e68afa3dd4bd22f145631abc4de2979d781b1d8 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Sun, 28 Jun 2026 01:45:12 +0100 Subject: [PATCH 16/30] refactor(compose): streamline loop var implementation --- roles/karo-compose/tasks/deploy.yml | 10 ++++---- roles/karo-compose/tasks/down.yml | 4 ++-- roles/karo-compose/tasks/main.yml | 36 ++++++++++++----------------- roles/karo-compose/tasks/up.yml | 8 +++---- 4 files changed, 26 insertions(+), 32 deletions(-) diff --git a/roles/karo-compose/tasks/deploy.yml b/roles/karo-compose/tasks/deploy.yml index 1fce412..c5328c4 100644 --- a/roles/karo-compose/tasks/deploy.yml +++ b/roles/karo-compose/tasks/deploy.yml @@ -4,18 +4,18 @@ --- -- name: "Create compose stack directory for {{ stack }}" +- name: "Create compose stack directory for {{ stack.name }}" ansible.builtin.file: - path: "/srv/docker/{{ group }}/{{ stack }}" + path: "/srv/docker/{{ stack.path }}" mode: "0774" state: directory -- name: "Deploy compose templates for {{ stack }}" +- name: "Deploy compose templates for {{ stack.name }}" ansible.builtin.template: src: "{{ template.src }}" - dest: "/srv/docker/{{ group }}/{{ stack }}/{{ template.path | splitext | first }}" + dest: "/srv/docker/{{ stack.path }}/{{ template.path | splitext | first }}" mode: "0644" - with_community.general.filetree: ../templates/{{ group }}/{{ stack }}/ + with_community.general.filetree: ../templates/{{ stack.path }}/ loop_control: loop_var: template label: "{{ template.path }}" diff --git a/roles/karo-compose/tasks/down.yml b/roles/karo-compose/tasks/down.yml index bf4a3e7..b832307 100644 --- a/roles/karo-compose/tasks/down.yml +++ b/roles/karo-compose/tasks/down.yml @@ -4,8 +4,8 @@ --- -- name: Down compose stack {{ stack }} +- name: Down compose stack {{ stack.name }} community.docker.docker_compose_v2: docker_host: unix:///run/user/1001/docker.sock - project_src: "/srv/docker/{{ group }}/{{ stack }}" + project_src: "/srv/docker/{{ stack.path }}" state: absent # docker compose down diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index 386d70c..f878653 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -56,6 +56,8 @@ }) -%} {%- endfor -%} {%- endfor -%} + 'namespace': group ~ '_' ~ stack, + 'path': group ~ '/' ~ stack, {{ expanded_stacks }} - name: Assert provided stack name @@ -78,14 +80,11 @@ ansible.builtin.include_tasks: down.yml loop: "{{ karo_compose_expanded_stacks | reverse }}" loop_control: - loop_var: expanded_stack - label: "{{ stack }}" - vars: - stack: "{{ expanded_stack.name }}" - group: "{{ expanded_stack.group }}" + loop_var: stack + label: "{{ stack.name }}" when: - - expanded_stack.enabled - - karo_compose_justfile_stack in [stack, 'all'] + - stack.enabled + - karo_compose_justfile_stack in [stack.name, 'all'] - name: Deploy stacks become: true @@ -96,14 +95,11 @@ ansible.builtin.include_tasks: deploy.yml loop: "{{ karo_compose_expanded_stacks }}" loop_control: - loop_var: expanded_stack - label: "{{ stack }}" - vars: - stack: "{{ expanded_stack.name }}" - group: "{{ expanded_stack.group }}" + loop_var: stack + label: "{{ stack.name }}" when: - - expanded_stack.enabled - - karo_compose_justfile_stack in [stack, 'all'] + - stack.enabled + - karo_compose_justfile_stack in [stack.name, 'all'] - name: Create stacks become: true @@ -114,15 +110,13 @@ ansible.builtin.include_tasks: up.yml loop: "{{ karo_compose_expanded_stacks }}" loop_control: - loop_var: expanded_stack - label: "{{ stack }}" + loop_var: stack + label: "{{ stack.name }}" vars: - stack: "{{ expanded_stack.name }}" - group: "{{ expanded_stack.group }}" - secrets: "{{ lookup('vars', group ~ '_' ~ stack ~ '_secrets', default={}) | dict2items }}" + stack_secrets: "{{ lookup('vars', stack.namespace ~ '_secrets', default={}) | dict2items }}" when: - - expanded_stack.enabled - - karo_compose_justfile_stack in [stack, 'all'] + - stack.enabled + - karo_compose_justfile_stack in [stack.name, 'all'] always: - name: Discard tmpfs secrets directory diff --git a/roles/karo-compose/tasks/up.yml b/roles/karo-compose/tasks/up.yml index 7912695..2b9c5ad 100644 --- a/roles/karo-compose/tasks/up.yml +++ b/roles/karo-compose/tasks/up.yml @@ -20,19 +20,19 @@ ansible.builtin.set_fact: secrets_dir: true -- name: Create secrets for {{ stack }} +- name: Create secrets for {{ stack.name }} ansible.builtin.copy: dest: "/run/user/1001/karo/compose/{{ secret.key }}" content: "{{ secret.value }}" mode: "0644" - loop: "{{ secrets }}" + loop: "{{ stack_secrets }}" loop_control: loop_var: secret label: "{{ secret.key }}" changed_when: false -- name: Up compose stack {{ stack }} +- name: Up compose stack {{ stack.name }} community.docker.docker_compose_v2: docker_host: unix:///run/user/1001/docker.sock - project_src: "/srv/docker/{{ group }}/{{ stack }}" + project_src: "/srv/docker/{{ stack.path }}" state: present # docker compose up From ac744c4d57f54d3fc00c9c36dc5563c53c3e78e0 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Sun, 28 Jun 2026 01:52:14 +0100 Subject: [PATCH 17/30] feat(compose): merge host_vars & defaults stack dicts Using the combine filter is Ansible's recommended approach for merging multiple dictionaries. --- roles/karo-compose/tasks/deploy.yml | 4 ++++ roles/karo-compose/tasks/main.yml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/roles/karo-compose/tasks/deploy.yml b/roles/karo-compose/tasks/deploy.yml index c5328c4..362a788 100644 --- a/roles/karo-compose/tasks/deploy.yml +++ b/roles/karo-compose/tasks/deploy.yml @@ -10,6 +10,10 @@ mode: "0774" state: directory +- name: "Merge compose variables for {{ stack.name }}" + ansible.builtin.set_fact: + compose: "{{ stack_defaults | combine(stack_vars, recursive=true) }}" + - name: "Deploy compose templates for {{ stack.name }}" ansible.builtin.template: src: "{{ template.src }}" diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index f878653..c475d6f 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -97,6 +97,9 @@ loop_control: loop_var: stack label: "{{ stack.name }}" + vars: + stack_vars: "{{ lookup('vars', stack.namespace ~ '_stack') }}" + stack_defaults: "{{ lookup('vars', stack.namespace ~ '_stack_defaults') }}" when: - stack.enabled - karo_compose_justfile_stack in [stack.name, 'all'] From fd4c457d81a9dc742f1a5778432e7c9e7617f1e5 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Sun, 28 Jun 2026 02:04:02 +0100 Subject: [PATCH 18/30] refactor(compose): use normal jinja delimiter --- roles/karo-compose/tasks/main.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index c475d6f..e6c10f2 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -46,18 +46,18 @@ - name: Set expanded stack list ansible.builtin.set_fact: karo_compose_expanded_stacks: | - {%- set expanded_stacks = [] -%} - {%- for group in karo_compose_stack_groups -%} - {%- for stack in lookup('vars', group) -%} - {%- set _ = expanded_stacks.append({ + {% set expanded_stacks = [] %} + {% for group in karo_compose_stack_groups %} + {% for stack in lookup('vars', group) %} + {% set _ = expanded_stacks.append({ 'enabled': lookup('vars', group ~ '_' ~ stack ~ '_enabled'), 'group': group, 'name': stack, - }) -%} - {%- endfor -%} - {%- endfor -%} 'namespace': group ~ '_' ~ stack, 'path': group ~ '/' ~ stack, + }) %} + {% endfor %} + {% endfor %} {{ expanded_stacks }} - name: Assert provided stack name From 7281da171c1a4cb263d41a960a3828f1cfd75a71 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Sun, 28 Jun 2026 03:41:43 +0100 Subject: [PATCH 19/30] revert: "refactor(compose): create oidc defaults dictionary" This reverts commit d597a566df7e89195e7bdbbeff260892672afe5f. The use of dictionary data structures should be avoided. --- roles/karo-compose/defaults/main/main.yml | 53 ++++++++++++----------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/roles/karo-compose/defaults/main/main.yml b/roles/karo-compose/defaults/main/main.yml index 5493cb5..016b113 100644 --- a/roles/karo-compose/defaults/main/main.yml +++ b/roles/karo-compose/defaults/main/main.yml @@ -18,29 +18,30 @@ karo_compose_timezone: "Europe/London" karo_compose_root_domain: example.com -karo_compose_auth: - provider_name: PocketID - admin_group: admin - subdomain: auth - -karo_compose_oidc: - domain: "{{ karo_compose_auth.subdomain }}.{{ karo_compose_root_domain }}" # e.g. auth.example.com - url: "https://{{ karo_compose_oidc.domain }}" # e.g. https://auth.example.com - authorization: - path: "/authorize" - url: "{{ karo_compose_oidc.url }}{{ karo_compose_oidc.authorization.path }}" - discovery: - path: "/.well-known/openid-configuration" - url: "{{ karo_compose_oidc.url }}{{ karo_compose_oidc.discovery.path }}" - token: - path: "/api/oidc/token" - url: "{{ karo_compose_oidc.url }}{{ karo_compose_oidc.token.path }}" - userinfo: - path: "/api/oidc/userinfo" - url: "{{ karo_compose_oidc.url }}{{ karo_compose_oidc.userinfo.path }}" - logout: - path: "/api/oidc/end-session" - url: "{{ karo_compose_oidc.url }}{{ karo_compose_oidc.logout.path }}" - certificate: - path: "/.well-known/jwks.json" - url: "{{ karo_compose_oidc.url }}{{ karo_compose_oidc.certificate.path }}" +# oidc + +karo_compose_oidc_subdomain: auth +karo_compose_oidc_domain: "{{ karo_compose_oidc_subdomain }}.{{ karo_compose_root_domain }}" # e.g. auth.example.com +karo_compose_oidc_url: "https://{{ karo_compose_oidc_domain }}" # e.g. https://auth.example.com + +karo_compose_oidc_authorization_path: "/authorize" +karo_compose_oidc_authorization_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_authorization_path }}" + +karo_compose_oidc_discovery_path: "/.well-known/openid-configuration" +karo_compose_oidc_discovery_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_discovery_path }}" + +karo_compose_oidc_token_path: "/api/oidc/token" +karo_compose_oidc_token_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_token_path }}" + +karo_compose_oidc_userinfo_path: "/api/oidc/userinfo" +karo_compose_oidc_userinfo_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_userinfo_path }}" + +karo_compose_oidc_logout_path: "/api/oidc/end-session" +karo_compose_oidc_logout_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_logout_path }}" + +karo_compose_oidc_certificate_path: "/.well-known/jwks.json" +karo_compose_oidc_certificate_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_certificate_path }}" + +karo_compose_oidc_provider_name: "PocketID" + +karo_compose_oidc_admin_group: "admin" From 39d5326250eed54e0f124cff19c30ad2e529ea73 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Sun, 28 Jun 2026 03:47:20 +0100 Subject: [PATCH 20/30] refactor(compose): reorganise defaults file --- roles/karo-compose/defaults/main/main.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/roles/karo-compose/defaults/main/main.yml b/roles/karo-compose/defaults/main/main.yml index 016b113..ee89610 100644 --- a/roles/karo-compose/defaults/main/main.yml +++ b/roles/karo-compose/defaults/main/main.yml @@ -4,7 +4,7 @@ --- -karo_compose_justfile_stack: all +# stacks # docker will start stacks in this order # docker will stop stacks in reverse order @@ -12,16 +12,24 @@ karo_compose_justfile_stack: all # - hazzuk_core # - hazzuk_extra +# common + karo_compose_restart_policy: unless-stopped karo_compose_timezone: "Europe/London" karo_compose_root_domain: example.com -# oidc +# auth + +karo_compose_oidc_provider_name: PocketID + +karo_compose_oidc_admin_group: admin karo_compose_oidc_subdomain: auth + karo_compose_oidc_domain: "{{ karo_compose_oidc_subdomain }}.{{ karo_compose_root_domain }}" # e.g. auth.example.com + karo_compose_oidc_url: "https://{{ karo_compose_oidc_domain }}" # e.g. https://auth.example.com karo_compose_oidc_authorization_path: "/authorize" @@ -42,6 +50,6 @@ karo_compose_oidc_logout_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_l karo_compose_oidc_certificate_path: "/.well-known/jwks.json" karo_compose_oidc_certificate_url: "{{ karo_compose_oidc_url }}{{ karo_compose_oidc_certificate_path }}" -karo_compose_oidc_provider_name: "PocketID" +# internal -karo_compose_oidc_admin_group: "admin" +karo_compose_justfile_stack: all From 00f2332df444d14f3e1938267a167dba617a9d89 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Mon, 29 Jun 2026 23:16:27 +0100 Subject: [PATCH 21/30] fix(compose): use new stack_secrets variable --- roles/karo-compose/tasks/up.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/karo-compose/tasks/up.yml b/roles/karo-compose/tasks/up.yml index 2b9c5ad..1fc2f57 100644 --- a/roles/karo-compose/tasks/up.yml +++ b/roles/karo-compose/tasks/up.yml @@ -6,7 +6,7 @@ - name: Prepare secrets when: - - secrets | length > 0 + - stack_secrets | length > 0 - secrets_dir is undefined block: - name: Create tmpfs secrets directory From c2d9ad9890ed17d1939fbe57321f7bff0dcbf6c3 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Mon, 29 Jun 2026 23:22:06 +0100 Subject: [PATCH 22/30] refactor(compose): define filetree lookups as loops The loop syntax is recommended over with_ by Ansible. --- roles/karo-compose/tasks/deploy.yml | 2 +- roles/karo-compose/tasks/main.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/karo-compose/tasks/deploy.yml b/roles/karo-compose/tasks/deploy.yml index 362a788..e17971f 100644 --- a/roles/karo-compose/tasks/deploy.yml +++ b/roles/karo-compose/tasks/deploy.yml @@ -19,7 +19,7 @@ src: "{{ template.src }}" dest: "/srv/docker/{{ stack.path }}/{{ template.path | splitext | first }}" mode: "0644" - with_community.general.filetree: ../templates/{{ stack.path }}/ + loop: "{{ query('filetree', '../templates/' ~ stack.path) }}" loop_control: loop_var: template label: "{{ template.path }}" diff --git a/roles/karo-compose/tasks/main.yml b/roles/karo-compose/tasks/main.yml index e6c10f2..a77e555 100644 --- a/roles/karo-compose/tasks/main.yml +++ b/roles/karo-compose/tasks/main.yml @@ -38,7 +38,7 @@ that: stack_group_dir.path in karo_compose_stack_groups fail_msg: "Stack group '{{ stack_group_dir.path }}' missing from 'karo_compose_stack_groups'." quiet: true - with_community.general.filetree: ../templates/, exclude='^\.gitkeep$' + loop: "{{ query('filetree', '../templates', exclude='^\\.gitkeep$') }}" loop_control: loop_var: stack_group_dir label: "templates/{{ stack_group_dir.path }}/" From 47f1af14732528fec49890b5667da81aac87b792 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Tue, 30 Jun 2026 06:04:11 +0100 Subject: [PATCH 23/30] fix(justfile): prevent .gitkeep file deletion --- justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/justfile b/justfile index b943fd0..de28524 100644 --- a/justfile +++ b/justfile @@ -111,7 +111,7 @@ wireguard: # remove role defaults find roles/karo-compose/defaults/main/ -mindepth 1 ! -name "main.yml" -delete # remove role templates - find roles/karo-compose/templates/ -mindepth 1 -delete + find roles/karo-compose/templates/ -mindepth 1 ! -name ".gitkeep" -delete @_stack-add: # symlink custom defaults From bceebb64130f7b0f919594285fe351a94436be59 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Tue, 30 Jun 2026 22:21:11 +0100 Subject: [PATCH 24/30] feat(ansible): set micro editor options Used when editing the Ansible vault file. --- ansible.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible.cfg b/ansible.cfg index e8077ef..4ad1c0d 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -3,7 +3,7 @@ inventory = inventory/hosts.ini interpreter_python = /usr/bin/python3 vault_password_file = /run/user/1000/karo/ansible/vault_pass local_tmp = /run/user/1000/karo/ansible/tmp -editor = micro -backup false +editor = micro -backup false -scrollbar true -statusline false [privilege_escalation] become_ask_pass = false From 370ac65bd2955d72b513d25cd71ba9134840310a Mon Sep 17 00:00:00 2001 From: hazzuk Date: Tue, 30 Jun 2026 23:14:26 +0100 Subject: [PATCH 25/30] feat(justfile): add custom repos recipes Allows for adding and removing karo-custom repos. --- justfile | 81 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 14 deletions(-) diff --git a/justfile b/justfile index de28524..245f641 100644 --- a/justfile +++ b/justfile @@ -9,6 +9,11 @@ help: @{{ just_executable() }} --list --unsorted --list-prefix " - " --justfile "{{ justfile() }}" +# (Internal use) Reusable confirmation statement +[confirm("proceed? (y/N)")] +_confirm: + @echo + # preseed @@ -105,20 +110,68 @@ wireguard: printf 'Public key: %s\n' "$pub" -# custom stacks - -@_stack-remove: - # remove role defaults +# custom + +# Manage custom repos +custom action username: + #!/usr/bin/env bash + set -euo pipefail + case "{{action}}" in + add) + echo "adding karo-custom {{username}}" + just _custom-add {{username}} + ;; + remove) + echo "removing karo-custom {{username}}" + just _custom-remove {{username}} + ;; + *) + echo "action must be 'add' or 'remove'" >&2; exit 1; + ;; + esac + +repo_name := "karo-custom" + +# (Internal use) Add new karo-custom repo +@_custom-add username: + # clone karo-custom git repo + git clone git@github.com:{{username}}/{{repo_name}}.git custom/{{username}} + # manage symlinks + just _custom-symlinks-clear + just _custom-symlinks-create + # list stack groups for username + echo "Added stack groups:" + ls -1 roles/karo-compose/templates | grep {{username}} | awk '{print "- " $0}' + +# (Internal use) Remove existing karo-custom repo +@_custom-remove username: + # check username dir exists + test -d "custom/{{username}}" + # confirm removal + just _confirm + # remove custom dir + rm -rf "custom/{{username}}" + # manage symlinks + just _custom-symlinks-clear + just _custom-symlinks-create + +# (Internal use) Clear existing symbolic links for custom files +@_custom-symlinks-clear: + # clear role defaults find roles/karo-compose/defaults/main/ -mindepth 1 ! -name "main.yml" -delete - # remove role templates + # clear role templates find roles/karo-compose/templates/ -mindepth 1 ! -name ".gitkeep" -delete -@_stack-add: - # symlink custom defaults - ln -sr custom/*/defaults/main/*.yml roles/karo-compose/defaults/main/ - # symlink custom templates - ln -sr custom/*/templates/*/ roles/karo-compose/templates/ - -@stack: - just _stack-remove - just _stack-add +# (Internal use) Create new symbolic links for custom files +_custom-symlinks-create: + #!/usr/bin/env bash + set -euo pipefail + # check custom dir not empty + if [ -z "$(find custom -mindepth 1 -maxdepth 1)" ]; then + exit 0 + else + # symlink custom defaults + ln -sr custom/*/karo-compose/defaults/main/*.yml roles/karo-compose/defaults/main/ + # symlink custom templates + ln -sr custom/*/karo-compose/templates/*/ roles/karo-compose/templates/ + fi From f966975e3001d40c6a4d41e21740dc14a9aa6a7e Mon Sep 17 00:00:00 2001 From: hazzuk Date: Tue, 30 Jun 2026 23:16:59 +0100 Subject: [PATCH 26/30] feat(justfile): annotate recipes with group names --- justfile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/justfile b/justfile index 245f641..0c45aae 100644 --- a/justfile +++ b/justfile @@ -18,6 +18,7 @@ _confirm: # preseed # Host preseed.cfg +[group('Debian install')] @preseed platform='server': # check user input for platform [ "{{platform}}" = "server" ] || [ "{{platform}}" = "desktop" ] || { echo "platform must be 'server' or 'desktop'" >&2; exit 1; } @@ -43,6 +44,7 @@ _host-preseed platform: # server # Setup a system +[group('System setup')] @install hostname='': _check-password ansible-playbook run.yml --tags install --limit "{{hostname}}" @@ -50,6 +52,7 @@ _host-preseed platform: # compose # Deploy/remove stacks +[group('System setup')] [arg("stack", long, short="s")] compose action hostname='' stack='all': _check-password #!/bin/bash @@ -74,6 +77,7 @@ compose action hostname='' stack='all': _check-password password := "/run/user/1000/karo/ansible/vault_pass" # Manage a vault +[group('Ansible vault')] vault hostname: #!/bin/bash # check password file exists @@ -96,6 +100,7 @@ _check-password: @[ -e "{{password}}" ] || micro -backup false -mkparents true "{{password}}" # Set password +[group('Ansible vault')] password: @micro -backup false -mkparents true "{{password}}" @@ -103,7 +108,7 @@ password: # wireguard # Generate key pair -wireguard: +_wireguard: @priv="$(wg genkey)"; \ pub="$(wg pubkey <<<"$priv")"; \ printf 'Private key: %s\n' "$priv"; \ @@ -113,6 +118,7 @@ wireguard: # custom # Manage custom repos +[group('Custom repos')] custom action username: #!/usr/bin/env bash set -euo pipefail From 953bee7c09316142e4397eb9454693ea074ef659 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Thu, 2 Jul 2026 00:26:26 +0100 Subject: [PATCH 27/30] feat(justfile): handle all symlinks as directories Clearing symlinks also becomes easier, as it doesn't need to be recursive. --- justfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/justfile b/justfile index 0c45aae..91c3843 100644 --- a/justfile +++ b/justfile @@ -164,9 +164,9 @@ repo_name := "karo-custom" # (Internal use) Clear existing symbolic links for custom files @_custom-symlinks-clear: # clear role defaults - find roles/karo-compose/defaults/main/ -mindepth 1 ! -name "main.yml" -delete + find roles/karo-compose/defaults/main/ -mindepth 1 -maxdepth 1 ! -name "main.yml" -delete # clear role templates - find roles/karo-compose/templates/ -mindepth 1 ! -name ".gitkeep" -delete + find roles/karo-compose/templates/ -mindepth 1 -maxdepth 1 ! -name ".gitkeep" -delete # (Internal use) Create new symbolic links for custom files _custom-symlinks-create: @@ -177,7 +177,7 @@ _custom-symlinks-create: exit 0 else # symlink custom defaults - ln -sr custom/*/karo-compose/defaults/main/*.yml roles/karo-compose/defaults/main/ + ln -sr custom/*/karo-compose/defaults/main/*/ roles/karo-compose/defaults/main/ # symlink custom templates ln -sr custom/*/karo-compose/templates/*/ roles/karo-compose/templates/ fi From e36c21adf4b941cac682b0be52df7a2356dd8ead Mon Sep 17 00:00:00 2001 From: hazzuk Date: Mon, 13 Jul 2026 20:05:27 +0100 Subject: [PATCH 28/30] feat(justfile): recreate symlinks running compose Ensures correct symlinks whenever the compose recipe is run. --- justfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/justfile b/justfile index 91c3843..4863506 100644 --- a/justfile +++ b/justfile @@ -64,6 +64,9 @@ compose action hostname='' stack='all': _check-password else echo "action must be 'up' or 'down'" >&2; exit 1; fi + # manage symlinks + just _custom-symlinks-clear + just _custom-symlinks-create # run user action ANSIBLE_DISPLAY_SKIPPED_HOSTS=false ansible-playbook run.yml \ --extra-vars "karo_compose_justfile_stack={{stack}}" \ From 532d79e96823afbdfab036cdf5f54ee36e0d8c63 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Thu, 30 Jul 2026 21:58:31 +0100 Subject: [PATCH 29/30] refactor(justfile): combine symlink recipes --- justfile | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/justfile b/justfile index 4863506..1928cb4 100644 --- a/justfile +++ b/justfile @@ -65,8 +65,7 @@ compose action hostname='' stack='all': _check-password echo "action must be 'up' or 'down'" >&2; exit 1; fi # manage symlinks - just _custom-symlinks-clear - just _custom-symlinks-create + just _custom-symlinks # run user action ANSIBLE_DISPLAY_SKIPPED_HOSTS=false ansible-playbook run.yml \ --extra-vars "karo_compose_justfile_stack={{stack}}" \ @@ -146,8 +145,7 @@ repo_name := "karo-custom" # clone karo-custom git repo git clone git@github.com:{{username}}/{{repo_name}}.git custom/{{username}} # manage symlinks - just _custom-symlinks-clear - just _custom-symlinks-create + just _custom-symlinks # list stack groups for username echo "Added stack groups:" ls -1 roles/karo-compose/templates | grep {{username}} | awk '{print "- " $0}' @@ -161,26 +159,20 @@ repo_name := "karo-custom" # remove custom dir rm -rf "custom/{{username}}" # manage symlinks - just _custom-symlinks-clear - just _custom-symlinks-create + just _custom-symlinks -# (Internal use) Clear existing symbolic links for custom files -@_custom-symlinks-clear: - # clear role defaults - find roles/karo-compose/defaults/main/ -mindepth 1 -maxdepth 1 ! -name "main.yml" -delete - # clear role templates - find roles/karo-compose/templates/ -mindepth 1 -maxdepth 1 ! -name ".gitkeep" -delete - -# (Internal use) Create new symbolic links for custom files -_custom-symlinks-create: +# (Internal use) Manage symbolic links for custom files +_custom-symlinks: #!/usr/bin/env bash set -euo pipefail + # clear existing symlinks + find roles/karo-compose/defaults/main/ -mindepth 1 -maxdepth 1 ! -name "main.yml" -delete + find roles/karo-compose/templates/ -mindepth 1 -maxdepth 1 ! -name ".gitkeep" -delete # check custom dir not empty if [ -z "$(find custom -mindepth 1 -maxdepth 1)" ]; then exit 0 else - # symlink custom defaults + # symlink custom files ln -sr custom/*/karo-compose/defaults/main/*/ roles/karo-compose/defaults/main/ - # symlink custom templates ln -sr custom/*/karo-compose/templates/*/ roles/karo-compose/templates/ fi From a5488bef3815dc3d24d9749cc09074850fffe3e9 Mon Sep 17 00:00:00 2001 From: hazzuk Date: Thu, 30 Jul 2026 22:03:15 +0100 Subject: [PATCH 30/30] feat(justfile): check adding empty karo-custom repos Applicable when users are creating a new empty karo-custom repo. But there is nothing to symlink. --- justfile | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/justfile b/justfile index 1928cb4..4b49ce3 100644 --- a/justfile +++ b/justfile @@ -141,14 +141,21 @@ custom action username: repo_name := "karo-custom" # (Internal use) Add new karo-custom repo -@_custom-add username: +_custom-add username: + #!/usr/bin/env bash + set -euo pipefail # clone karo-custom git repo git clone git@github.com:{{username}}/{{repo_name}}.git custom/{{username}} - # manage symlinks - just _custom-symlinks - # list stack groups for username - echo "Added stack groups:" - ls -1 roles/karo-compose/templates | grep {{username}} | awk '{print "- " $0}' + # check karo-custom repo dir not empty + if [ -z "$(find custom/{{username}}/karo-compose -mindepth 1 -maxdepth 1)" ]; then + exit 0 + else + # manage symlinks + just _custom-symlinks + # list stack groups for username + echo "new karo-compose stack groups:" + ls -1 roles/karo-compose/templates | grep {{username}} | awk '{print "- " $0}' + fi # (Internal use) Remove existing karo-custom repo @_custom-remove username: