浏览代码

prepare other templates

xcad 5 月之前
父节点
当前提交
15cfa647dd
共有 76 个文件被更改,包括 1813 次插入822 次删除
  1. 0 35
      cli/modules/compose/spec_v1_2.py
  2. 85 27
      library/compose/alloy/compose.yaml.j2
  3. 1 0
      library/compose/alloy/template.yaml
  4. 0 31
      library/compose/authentik/.env.authentik.j2
  5. 13 0
      library/compose/authentik/.env.j2
  6. 0 9
      library/compose/authentik/.env.postgres.j2
  7. 165 51
      library/compose/authentik/compose.yaml.j2
  8. 2 1
      library/compose/authentik/template.yaml
  9. 26 6
      library/compose/bind9/compose.yaml.j2
  10. 2 11
      library/compose/bind9/template.yaml
  11. 65 22
      library/compose/checkmk/compose.yaml.j2
  12. 34 3
      library/compose/checkmk/template.yaml
  13. 2 2
      library/compose/clamav/compose.yaml.j2
  14. 6 4
      library/compose/clamav/template.yaml
  15. 71 26
      library/compose/dockge/compose.yaml.j2
  16. 20 4
      library/compose/dockge/template.yaml
  17. 0 20
      library/compose/gitea/.env.gitea.j2
  18. 5 0
      library/compose/gitea/.env.j2
  19. 0 9
      library/compose/gitea/.env.postgres.j2
  20. 108 33
      library/compose/gitea/compose.yaml.j2
  21. 1 0
      library/compose/gitea/template.yaml
  22. 2 5
      library/compose/gitlab-runner/template.yaml
  23. 115 39
      library/compose/gitlab/compose.yaml.j2
  24. 3 2
      library/compose/gitlab/template.yaml
  25. 71 16
      library/compose/grafana/compose.yaml.j2
  26. 1 0
      library/compose/grafana/template.yaml
  27. 2 2
      library/compose/heimdall/compose.yaml.j2
  28. 6 3
      library/compose/heimdall/template.yaml
  29. 2 2
      library/compose/homeassistant/compose.yaml.j2
  30. 6 4
      library/compose/homeassistant/template.yaml
  31. 2 3
      library/compose/homepage/template.yaml
  32. 71 16
      library/compose/homer/compose.yaml.j2
  33. 2 1
      library/compose/homer/template.yaml
  34. 83 30
      library/compose/influxdb/compose.yaml.j2
  35. 2 3
      library/compose/influxdb/template.yaml
  36. 2 2
      library/compose/loki/compose.yaml.j2
  37. 6 4
      library/compose/loki/template.yaml
  38. 2 5
      library/compose/mariadb/template.yaml
  39. 4 2
      library/compose/n8n-server/compose.yaml.j2
  40. 1 1
      library/compose/n8n-server/template.yaml
  41. 1 1
      library/compose/n8n-worker/template.yaml
  42. 35 10
      library/compose/n8n/compose.yaml.j2
  43. 72 0
      library/compose/n8n/template.yaml
  44. 127 48
      library/compose/nextcloud/compose.yaml.j2
  45. 9 4
      library/compose/nextcloud/template.yaml
  46. 73 43
      library/compose/nginx/compose.yaml.j2
  47. 2 4
      library/compose/nginx/template.yaml
  48. 2 4
      library/compose/nginxproxymanager/template.yaml
  49. 68 24
      library/compose/openwebui/compose.yaml.j2
  50. 40 3
      library/compose/openwebui/template.yaml
  51. 6 6
      library/compose/passbolt/compose.yaml.j2
  52. 6 3
      library/compose/passbolt/template.yaml
  53. 4 20
      library/compose/pihole/.env.pihole.j2
  54. 1 1
      library/compose/pihole/.env.secret.j2
  55. 24 14
      library/compose/pihole/compose.yaml.j2
  56. 1 1
      library/compose/pihole/template.yaml
  57. 80 19
      library/compose/portainer/compose.yaml.j2
  58. 1 0
      library/compose/portainer/template.yaml
  59. 53 26
      library/compose/postgres/compose.yaml.j2
  60. 2 4
      library/compose/postgres/template.yaml
  61. 4 2
      library/compose/prometheus/compose.yaml.j2
  62. 1 1
      library/compose/prometheus/template.yaml
  63. 0 26
      library/compose/semaphoreui/.env.database.j2
  64. 16 0
      library/compose/semaphoreui/.env.j2
  65. 0 47
      library/compose/semaphoreui/.env.semaphore.j2
  66. 161 41
      library/compose/semaphoreui/compose.yaml.j2
  67. 2 1
      library/compose/semaphoreui/template.yaml
  68. 8 12
      library/compose/traefik/compose.yaml.j2
  69. 1 1
      library/compose/traefik/template.yaml
  70. 2 4
      library/compose/twingate-connector/template.yaml
  71. 2 2
      library/compose/uptimekuma/compose.yaml.j2
  72. 6 4
      library/compose/uptimekuma/template.yaml
  73. 3 3
      library/compose/wazuh/compose.yaml.j2
  74. 6 6
      library/compose/wazuh/template.yaml
  75. 4 2
      library/compose/whoami/compose.yaml.j2
  76. 1 1
      library/compose/whoami/template.yaml

+ 0 - 35
cli/modules/compose/spec_v1_2.py

@@ -272,41 +272,6 @@ spec = OrderedDict(
                     "needs": "swarm_placement_mode=replicated",
                     "extra": "Constrains service to run on specific node by hostname",
                 },
-                "swarm_volume_mode": {
-                    "description": "Swarm volume storage backend",
-                    "type": "enum",
-                    "options": ["local", "mount", "nfs"],
-                    "default": "local",
-                    "extra": "WARNING: 'local' only works on single-node deployments!",
-                },
-                "swarm_volume_mount_path": {
-                    "description": "Host path for bind mount",
-                    "type": "str",
-                    "default": "/mnt/storage",
-                    "needs": "swarm_volume_mode=mount",
-                    "extra": "Useful for shared/replicated storage",
-                },
-                "swarm_volume_nfs_server": {
-                    "description": "NFS server address",
-                    "type": "str",
-                    "default": "192.168.1.1",
-                    "needs": "swarm_volume_mode=nfs",
-                    "extra": "IP address or hostname of NFS server",
-                },
-                "swarm_volume_nfs_path": {
-                    "description": "NFS export path",
-                    "type": "str",
-                    "default": "/export",
-                    "needs": "swarm_volume_mode=nfs",
-                    "extra": "Path to NFS export on the server",
-                },
-                "swarm_volume_nfs_options": {
-                    "description": "NFS mount options",
-                    "type": "str",
-                    "default": "rw,nolock,soft",
-                    "needs": "swarm_volume_mode=nfs",
-                    "extra": "Comma-separated NFS mount options",
-                },
             },
         },
         "database": {

+ 85 - 27
library/compose/alloy/compose.yaml.j2

@@ -1,16 +1,40 @@
 services:
-  {{ service_name | default("alloy") }}:
+  {{ service_name }}:
     image: grafana/alloy:v1.11.3
-    container_name: {{ container_name | default("alloy") }}
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
+    container_name: {{ container_name }}
+    {% endif %}
     hostname: {{ container_hostname }}
     command:
       - run
-      - --server.http.listen-addr=0.0.0.0:{{ ports_main | default("12345") }}
+      - --server.http.listen-addr=0.0.0.0:12345
       - --storage.path=/var/lib/alloy/data
       - /etc/alloy/config.alloy
-    {% if ports_enabled %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
     ports:
-      - "{{ ports_main | default("12345") }}:12345"
+      {% if swarm_enabled %}
+      - target: 12345
+        published: {{ ports_main }}
+        protocol: tcp
+        mode: host
+      {% else %}
+      - "{{ ports_main }}:12345"
+      {% endif %}
     {% endif %}
     volumes:
       - ./config/config.alloy:/etc/alloy/config.alloy
@@ -29,42 +53,76 @@ services:
       {% if metrics_enabled and metrics_system %}
       - /run/udev/data:/run/udev/data:ro
       {% endif %}
-    {% if network_enabled %}
-    networks:
-      - {{ network_name | default("bridge") }}
-    {% endif %}
-    {% if traefik_enabled %}
+    {% if traefik_enabled and not swarm_enabled %}
     labels:
       - traefik.enable=true
-      - traefik.docker.network={{ traefik_network | default("traefik") }}
-      - traefik.http.services.{{ service_name | default("alloy") }}.loadbalancer.server.port=12345
-      - traefik.http.services.{{ service_name | default("alloy") }}.loadbalancer.server.scheme=http
-      - traefik.http.routers.{{ service_name | default("alloy") }}-http.service={{ service_name | default("alloy") }}
-      - traefik.http.routers.{{ service_name | default("alloy") }}-http.rule=Host(`{{ traefik_host }}`)
-      - traefik.http.routers.{{ service_name | default("alloy") }}-http.entrypoints={{ traefik_entrypoint | default("web") }}
+      - traefik.docker.network={{ traefik_network }}
+      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=12345
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+      - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
       {% if traefik_tls_enabled %}
-      - traefik.http.routers.{{ service_name | default("alloy") }}-https.service={{ service_name | default("alloy") }}
-      - traefik.http.routers.{{ service_name | default("alloy") }}-https.rule=Host(`{{ traefik_host }}`)
-      - traefik.http.routers.{{ service_name | default("alloy") }}-https.entrypoints={{ traefik_tls_entrypoint | default("websecure") }}
-      - traefik.http.routers.{{ service_name | default("alloy") }}-https.tls=true
-      - traefik.http.routers.{{ service_name | default("alloy") }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+      - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+      - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-https.tls=true
+      - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+      {% endif %}
+    {% endif %}
+    {% if swarm_enabled %}
+    deploy:
+      mode: {{ swarm_placement_mode }}
+      {% if swarm_placement_mode == 'replicated' %}
+      replicas: {{ swarm_replicas }}
+      {% endif %}
+      restart_policy:
+        condition: on-failure
+      {% if traefik_enabled %}
+      labels:
+        - traefik.enable=true
+        - traefik.docker.network={{ traefik_network }}
+        - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=12345
+        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+        {% if traefik_tls_enabled %}
+        - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-https.tls=true
+        - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+        {% endif %}
       {% endif %}
     {% endif %}
-    restart: {{ restart_policy | default("unless-stopped") }}
 
 volumes:
   alloy_data:
     driver: local
 
-{% if network_enabled or traefik_enabled %}
+{% if network_mode != 'host' %}
 networks:
-  {% if network_enabled %}
-  {{ network_name | default("bridge") }}:
+  {{ network_name }}:
     {% if network_external %}
     external: true
+    {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    name: {{ network_name }}
+    {% elif swarm_enabled %}
+    driver: overlay
+    attachable: true
+    {% else %}
+    driver: bridge
+    {% endif %}
     {% endif %}
-  {% elif traefik_enabled %}
-  {{ traefik_network | default("traefik") }}:
+  {% if traefik_enabled %}
+  {{ traefik_network }}:
     external: true
   {% endif %}
 {% endif %}

+ 1 - 0
library/compose/alloy/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Grafana Alloy
   description: >

+ 0 - 31
library/compose/authentik/.env.authentik.j2

@@ -1,31 +0,0 @@
-# Authentik Application Configuration
-# Contains sensitive application secrets and connection strings
-
-# Timezone
-TZ={{ container_timezone }}
-
-# Secret Key (used for cookie signing and unique user IDs)
-AUTHENTIK_SECRET_KEY={{ authentik_secret_key }}
-
-# Error Reporting
-AUTHENTIK_ERROR_REPORTING__ENABLED={{ authentik_error_reporting }}
-
-# Redis Connection
-AUTHENTIK_REDIS__HOST={{ service_name }}-redis
-
-# PostgreSQL Connection
-AUTHENTIK_POSTGRESQL__HOST={{ service_name }}-postgres
-AUTHENTIK_POSTGRESQL__USER={{ database_user }}
-AUTHENTIK_POSTGRESQL__NAME={{ database_name }}
-AUTHENTIK_POSTGRESQL__PASSWORD={{ database_password }}
-
-{% if email_enabled -%}
-# Email Server Configuration
-AUTHENTIK_EMAIL__HOST={{ email_host }}
-AUTHENTIK_EMAIL__PORT={{ email_port }}
-AUTHENTIK_EMAIL__USERNAME={{ email_username }}
-AUTHENTIK_EMAIL__PASSWORD={{ email_password }}
-AUTHENTIK_EMAIL__USE_TLS={{ email_use_tls }}
-AUTHENTIK_EMAIL__USE_SSL={{ email_use_ssl }}
-AUTHENTIK_EMAIL__FROM={{ email_from }}
-{% endif %}

+ 13 - 0
library/compose/authentik/.env.j2

@@ -0,0 +1,13 @@
+# Authentik Secrets
+# Contains sensitive credentials
+
+# Secret Key (used for cookie signing and unique user IDs)
+AUTHENTIK_SECRET_KEY={{ authentik_secret_key }}
+
+# Database Password
+DATABASE_PASSWORD={{ database_password }}
+
+{% if email_enabled -%}
+# Email Password
+EMAIL_PASSWORD={{ email_password }}
+{% endif %}

+ 0 - 9
library/compose/authentik/.env.postgres.j2

@@ -1,9 +0,0 @@
-# PostgreSQL Database Configuration
-# Contains only database credentials needed by Postgres container
-
-# Timezone
-TZ={{ container_timezone }}
-
-POSTGRES_USER={{ database_user }}
-POSTGRES_PASSWORD={{ database_password }}
-POSTGRES_DB={{ database_name }}

+ 165 - 51
library/compose/authentik/compose.yaml.j2

@@ -1,76 +1,162 @@
 services:
   {{ service_name }}:
     image: ghcr.io/goauthentik/server:2025.10.1
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
     container_name: {{ container_name }}
+    {% endif %}
     command: server
-    env_file:
-      - .env.authentik
-    {% if ports_enabled %}
+    environment:
+      - TZ={{ container_timezone }}
+      - AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY}
+      - AUTHENTIK_ERROR_REPORTING__ENABLED={{ authentik_error_reporting }}
+      - AUTHENTIK_REDIS__HOST={{ service_name }}-redis
+      - AUTHENTIK_POSTGRESQL__HOST={{ service_name }}-postgres
+      - AUTHENTIK_POSTGRESQL__USER={{ database_user }}
+      - AUTHENTIK_POSTGRESQL__NAME={{ database_name }}
+      - AUTHENTIK_POSTGRESQL__PASSWORD=${DATABASE_PASSWORD}
+      {% if email_enabled %}
+      - AUTHENTIK_EMAIL__HOST={{ email_host }}
+      - AUTHENTIK_EMAIL__PORT={{ email_port }}
+      - AUTHENTIK_EMAIL__USERNAME={{ email_username }}
+      - AUTHENTIK_EMAIL__PASSWORD=${EMAIL_PASSWORD}
+      - AUTHENTIK_EMAIL__USE_TLS={{ email_use_tls }}
+      - AUTHENTIK_EMAIL__USE_SSL={{ email_use_ssl }}
+      - AUTHENTIK_EMAIL__FROM={{ email_from }}
+      {% endif %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
     ports:
+      {% if swarm_enabled %}
+      - target: 9000
+        published: {{ ports_http }}
+        protocol: tcp
+        mode: host
+      - target: 9443
+        published: {{ ports_https }}
+        protocol: tcp
+        mode: host
+      {% else %}
       - "{{ ports_http }}:9000"
       - "{{ ports_https }}:9443"
-    {% endif %}
-    {% if network_enabled or traefik_enabled %}
-    networks:
-      {% if network_enabled %}
-      - {{ network_name }}
-      {% endif %}
-      {% if traefik_enabled %}
-      - {{ traefik_network }}
       {% endif %}
     {% endif %}
-    {% if traefik_enabled %}
+    volumes:
+      - ./media:/media
+      - ./custom-templates:/templates
+    {% if traefik_enabled and not swarm_enabled %}
     labels:
       - traefik.enable=true
       - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}.loadbalancer.server.port=9000
-      - traefik.http.services.{{ service_name }}.loadbalancer.server.scheme=http
+      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=9000
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
       {% if traefik_tls_enabled %}
+      - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
       - traefik.http.routers.{{ service_name }}-https.tls=true
       - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
     {% endif %}
-    volumes:
-      - ./media:/media
-      - ./custom-templates:/templates
     depends_on:
       - {{ service_name }}-postgres
       - {{ service_name }}-redis
-    restart: {{ restart_policy }}
+    {% if swarm_enabled %}
+    deploy:
+      mode: {{ swarm_placement_mode }}
+      {% if swarm_placement_mode == 'replicated' %}
+      replicas: {{ swarm_replicas }}
+      {% endif %}
+      restart_policy:
+        condition: on-failure
+      {% if traefik_enabled %}
+      labels:
+        - traefik.enable=true
+        - traefik.docker.network={{ traefik_network }}
+        - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=9000
+        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+        {% if traefik_tls_enabled %}
+        - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-https.tls=true
+        - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+        {% endif %}
+      {% endif %}
+    {% endif %}
 
   {{ service_name }}-worker:
     image: ghcr.io/goauthentik/server:2025.10.1
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
     container_name: {{ service_name }}-worker
+    {% endif %}
     command: worker
-    env_file:
-      - .env.authentik
+    environment:
+      - TZ={{ container_timezone }}
+      - AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY}
+      - AUTHENTIK_ERROR_REPORTING__ENABLED={{ authentik_error_reporting }}
+      - AUTHENTIK_REDIS__HOST={{ service_name }}-redis
+      - AUTHENTIK_POSTGRESQL__HOST={{ service_name }}-postgres
+      - AUTHENTIK_POSTGRESQL__USER={{ database_user }}
+      - AUTHENTIK_POSTGRESQL__NAME={{ database_name }}
+      - AUTHENTIK_POSTGRESQL__PASSWORD=${DATABASE_PASSWORD}
+      {% if email_enabled %}
+      - AUTHENTIK_EMAIL__HOST={{ email_host }}
+      - AUTHENTIK_EMAIL__PORT={{ email_port }}
+      - AUTHENTIK_EMAIL__USERNAME={{ email_username }}
+      - AUTHENTIK_EMAIL__PASSWORD=${EMAIL_PASSWORD}
+      - AUTHENTIK_EMAIL__USE_TLS={{ email_use_tls }}
+      - AUTHENTIK_EMAIL__USE_SSL={{ email_use_ssl }}
+      - AUTHENTIK_EMAIL__FROM={{ email_from }}
+      {% endif %}
     user: root
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
     volumes:
       - /run/docker.sock:/run/docker.sock
       - ./media:/media
       - ./certs:/certs
       - ./custom-templates:/templates
-    {% if network_enabled or traefik_enabled %}
-    networks:
-      {% if network_enabled %}
-      - {{ network_name }}
-      {% endif %}
-      {% if traefik_enabled %}
-      - {{ traefik_network }}
-      {% endif %}
-    {% endif %}
     depends_on:
       - {{ service_name }}-postgres
       - {{ service_name }}-redis
-    restart: {{ restart_policy }}
 
   {{ service_name }}-redis:
     image: docker.io/library/redis:8.2.3
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
     container_name: {{ service_name }}-redis
+    {% endif %}
     command: --save 60 1 --loglevel warning
     healthcheck:
       test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
@@ -78,43 +164,57 @@ services:
       interval: 30s
       retries: 5
       timeout: 3s
-    volumes:
-      - redis_data:/data
-    {% if network_enabled or traefik_enabled %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
     networks:
-      {% if network_enabled %}
-      - {{ network_name }}
-      {% endif %}
       {% if traefik_enabled %}
-      - {{ traefik_network }}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
       {% endif %}
     {% endif %}
-    restart: {{ restart_policy }}
+    volumes:
+      - redis_data:/data
 
   {% if not database_external %}
   {{ service_name }}-postgres:
     image: docker.io/library/postgres:17.6
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
     container_name: {{ service_name }}-db
-    env_file:
-      - .env.postgres
+    {% endif %}
+    environment:
+      - TZ={{ container_timezone }}
+      - POSTGRES_USER={{ database_user }}
+      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
+      - POSTGRES_DB={{ database_name }}
     healthcheck:
       test: ["CMD-SHELL", "pg_isready -U {{ database_user }}"]
       start_period: 30s
       interval: 10s
       timeout: 10s
       retries: 5
-    volumes:
-      - database_data:/var/lib/postgresql/data
-    {% if network_enabled or traefik_enabled %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
     networks:
-      {% if network_enabled %}
-      - {{ network_name }}
-      {% endif %}
       {% if traefik_enabled %}
-      - {{ traefik_network }}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
       {% endif %}
     {% endif %}
-    restart: {{ restart_policy }}
+    volumes:
+      - database_data:/var/lib/postgresql/data
   {% endif %}
 
 volumes:
@@ -123,14 +223,28 @@ volumes:
   redis_data:
     driver: local
 
-{% if network_enabled or traefik_enabled %}
+{% if network_mode != 'host' %}
 networks:
-  {% if network_enabled %}
   {{ network_name }}:
     {% if network_external %}
     external: true
+    {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    name: {{ network_name }}
+    {% elif swarm_enabled %}
+    driver: overlay
+    attachable: true
+    {% else %}
+    driver: bridge
+    {% endif %}
     {% endif %}
-  {% endif %}
   {% if traefik_enabled %}
   {{ traefik_network }}:
     external: true

+ 2 - 1
library/compose/authentik/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Authentik
   description: >
@@ -27,7 +28,7 @@ metadata:
     2. Access the web interface:
        {% if traefik_enabled -%}
        - Via Traefik: https://{{ traefik_host }}
-       {% if ports_enabled %}- Direct access: http://localhost:{{ ports_http }}{% endif %}
+       {% if not traefik_enabled and network_mode == 'bridge' %}- Direct access: http://localhost:{{ ports_http }}{% endif %}
        {%- else -%}
        - Open http://localhost:{{ ports_http }} in your browser
        {%- endif %}

+ 26 - 6
library/compose/bind9/compose.yaml.j2

@@ -6,7 +6,18 @@ services:
     environment:
       - TZ={{ container_timezone | default('UTC') }}
       - BIND9_USER=bind
-    {% if ports_enabled %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if network_mode == 'macvlan' %}
+      {{ network_name | default('bridge') }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name | default('bridge') }}:
+      {% endif %}
+    {% endif %}
+    {% if network_mode != 'host' %}
     ports:
       - "53:53/tcp"
       - "53:53/udp"
@@ -15,10 +26,6 @@ services:
       - ./config:/etc/bind
       - bind9_zones:/var/lib/bind
       - bind9_cache:/var/cache/bind
-    {% if network_enabled %}
-    networks:
-      - {{ network_name | default('bridge') }}
-    {% endif %}
     restart: {{ restart_policy | default('unless-stopped') }}
 
 volumes:
@@ -27,10 +34,23 @@ volumes:
   bind9_cache:
     driver: local
 
-{% if network_enabled %}
+{% if network_mode != 'host' %}
 networks:
   {{ network_name | default('bridge') }}:
     {% if network_external %}
     external: true
+    {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    {% else %}
+    driver: bridge
+    {% endif %}
     {% endif %}
+    name: {{ network_name | default('bridge') }}
 {% endif %}

+ 2 - 11
library/compose/bind9/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: BIND9
   description: >
@@ -14,8 +15,7 @@ metadata:
   version: 9.20-24.10_edge
   author: Christian Lempa
   date: '2025-10-02'
-  tags:
-    - dns
+  tags: []
   next_steps: |
     1. Start the DNS server:
        docker compose up -d
@@ -39,7 +39,6 @@ metadata:
        docker exec bind9 named-checkzone home.arpa /var/lib/bind/db.home.arpa
 
     For more information, visit: https://bind9.readthedocs.io/
-  draft: true
 spec:
   general:
     vars:
@@ -61,11 +60,3 @@ spec:
         default: ""
         sensitive: true
         autogenerated: true
-  ports:
-    vars:
-      ports_enabled:
-        default: true
-  network:
-    vars:
-      network_enabled:
-        default: false

+ 65 - 22
library/compose/checkmk/compose.yaml.j2

@@ -1,38 +1,81 @@
 services:
-  monitoring:
-    image: checkmk/check-mk-raw:2.4.0-latest
-    container_name: checkmk
-    ports:
-      - "8000:8000"
-      - "5000:5000"
-      - "162:162/udp"
+  {{ service_name }}:
+    image: checkmk/check-mk-raw:2.4.0-{{ monitoring_version }}
+    container_name: {{ container_name }}
+    hostname: {{ container_hostname }}
     environment:
-      - TZ=Europe/Berlin
-      - CMK_PASSWORD=${CMK_PASSWORD:?no password set}
-      - CMK_SITE_ID=${CMK_SITE_ID:-cmk}
+      - TZ={{ container_timezone }}
+      - CMK_PASSWORD={{ cmk_password }}
+      - CMK_SITE_ID={{ cmk_site_id }}
     tmpfs:
-      - /opt/omd/sites/cmk/tmp:uid=1000,gid=1000
+      - /opt/omd/sites/{{ cmk_site_id }}/tmp:uid={{ user_uid }},gid={{ user_gid }}
     volumes:
       - /etc/localtime:/etc/localtime:ro
       - data:/omd/sites:rw
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
     networks:
-      - frontend
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
+    ports:
+      - "{{ ports_http }}:8000"
+      - "{{ ports_agent }}:5000"
+      - "{{ ports_snmp }}:162/udp"
+    {% endif %}
+    {% if traefik_enabled %}
     labels:
       - traefik.enable=true
-      - traefik.docker.network=frontend
-      - traefik.http.services.checkmk.loadbalancer.server.port=5000
-      - traefik.http.services.checkmk.loadbalancer.server.scheme=http
-      - traefik.http.routers.checkmk.service=checkmk
-      - traefik.http.routers.checkmk.rule=Host(`checkmk.home.arpa`)
-      - traefik.http.routers.checkmk.entrypoints=websecure
-      - traefik.http.routers.checkmk.tls=true
-      - traefik.http.routers.checkmk.tls.certresolver=cloudflare
-    restart: unless-stopped
+      - traefik.docker.network={{ traefik_network }}
+      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=5000
+      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.scheme=http
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+      - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+      {% if traefik_tls_enabled %}
+      - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+      - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-https.tls=true
+      - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+      {% endif %}
+    {% endif %}
+    restart: {{ restart_policy }}
 
 volumes:
   data:
     driver: local
 
+{% if network_mode != 'host' %}
 networks:
-  frontend:
+  {{ network_name }}:
+    {% if network_external %}
+    external: true
+    {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    {% else %}
+    driver: bridge
+    {% endif %}
+    {% endif %}
+    name: {{ network_name }}
+  {% if traefik_enabled %}
+  {{ traefik_network }}:
     external: true
+  {% endif %}
+{% endif %}

+ 34 - 3
library/compose/checkmk/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Checkmk
   description: >
@@ -17,13 +18,43 @@ metadata:
   version: 2.4.0-latest
   author: Christian Lempa
   date: '2025-09-28'
-  tags:
-    - monitoring
-  draft: true
+  tags: []
 spec:
   general:
     vars:
+      service_name:
+        default: monitoring
+      container_name:
+        default: checkmk
       monitoring_version:
         type: str
         description: Monitoring version
         default: latest
+      cmk_password:
+        type: str
+        description: CheckMK admin password
+        default: ""
+        sensitive: true
+        autogenerated: true
+      cmk_site_id:
+        type: str
+        description: CheckMK site ID
+        default: cmk
+  traefik:
+    vars:
+      traefik_host:
+        default: checkmk.home.arpa
+  ports:
+    vars:
+      ports_http:
+        description: Web UI port
+        type: int
+        default: 8000
+      ports_agent:
+        description: Agent port
+        type: int
+        default: 5000
+      ports_snmp:
+        description: SNMP trap port
+        type: int
+        default: 162

+ 2 - 2
library/compose/clamav/compose.yaml.j2

@@ -1,7 +1,7 @@
 services:
-  clamav:
+  {{ service_name }}:
     image: docker.io/clamav/clamav:1.5.1
-    container_name: clamav
+    container_name: {{ container_name }}
     volumes:
       - ./config/clamd.conf:/etc/clamav/clamd.conf:ro
       - ./config/freshclam.conf:/etc/clamav/freshclam.conf:ro

+ 6 - 4
library/compose/clamav/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Clamav
   description: >
@@ -15,13 +16,14 @@ metadata:
   version: 1.5.1
   author: Christian Lempa
   date: '2025-10-31'
-  tags:
-    - antivirus
-    - security
-  draft: true
+  tags: []
 spec:
   general:
     vars:
+      service_name:
+        default: clamav
+      container_name:
+        default: clamav
       clamav_version:
         type: str
         description: Clamav version

+ 71 - 26
library/compose/dockge/compose.yaml.j2

@@ -1,31 +1,76 @@
 services:
-  dockge:
-    container_name: dockge
-    image: docker.io/louislam/dockge:1.5.0
+  {{ service_name }}:
+    image: docker.io/louislam/dockge:{{ dockge_version }}
+    container_name: {{ container_name }}
+    hostname: {{ container_hostname }}
+    environment:
+      - TZ={{ container_timezone }}
+      - DOCKGE_STACKS_DIR={{ stacks_path }}
     volumes:
-      - /var/run/docker.sock:/var/run/docker.sock
+      - /var/run/docker.sock:/var/run/docker.sock:ro
       - dockge-data:/app/data
-      - /your-stacks-path:/your-stacks-path  # <-- Change this to your stacks path
-    environment:
-      - DOCKGE_STACKS_DIR=/your-stacks-path  # <-- Change this to your stacks path
-    # --> (Optional) When using traefik
-    # labels:
-    #   - traefik.enable=true
-    #   - traefik.docker.network=frontend
-    #   - traefik.http.routers.dockge-http.entrypoints=web
-    #   - traefik.http.routers.dockge-http.rule=Host(`your-dockge-fqdn`)  # <-- Change this to your FQDN
-    #   - traefik.http.routers.dockge-https.entrypoints=websecure
-    #   - traefik.http.routers.dockge-https.rule=Host(`your-dockge-fqdn`)  # <-- Change this to your FQDN
-    #   - traefik.http.routers.dockge-https.tls=true
-    #   - traefik.http.routers.dockge-https.tls.certresolver=cloudflare
-    # networks:
-    #   - frontend
-    # <--
-    restart: unless-stopped
+      - {{ stacks_path }}:{{ stacks_path }}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
+    ports:
+      - "{{ ports_http }}:5001"
+    {% endif %}
+    {% if traefik_enabled %}
+    labels:
+      - traefik.enable=true
+      - traefik.docker.network={{ traefik_network }}
+      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=5001
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+      - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+      {% if traefik_tls_enabled %}
+      - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+      - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-https.tls=true
+      - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+      {% endif %}
+    {% endif %}
+    restart: {{ restart_policy }}
+
 volumes:
   dockge-data:
-# --> (Optional) When using traefik
-# networks:
-#   frontend:
-#     external: true
-# <--
+    driver: local
+
+{% if network_mode != 'host' %}
+networks:
+  {{ network_name }}:
+    {% if network_external %}
+    external: true
+    {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    {% else %}
+    driver: bridge
+    {% endif %}
+    {% endif %}
+    name: {{ network_name }}
+  {% if traefik_enabled %}
+  {{ traefik_network }}:
+    external: true
+  {% endif %}
+{% endif %}

+ 20 - 4
library/compose/dockge/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Dockge
   description: >
@@ -14,14 +15,29 @@ metadata:
   version: 1.5.0
   author: Christian Lempa
   date: '2025-09-28'
-  tags:
-    - management
-    - docker
-  draft: true
+  tags: []
 spec:
   general:
     vars:
+      service_name:
+        default: dockge
+      container_name:
+        default: dockge
       dockge_version:
         type: str
         description: Dockge version
         default: latest
+      stacks_path:
+        type: str
+        description: Path where Docker Compose stacks are stored
+        default: /opt/stacks
+  traefik:
+    vars:
+      traefik_host:
+        default: dockge.home.arpa
+  ports:
+    vars:
+      ports_http:
+        description: Web UI port
+        type: int
+        default: 5001

+ 0 - 20
library/compose/gitea/.env.gitea.j2

@@ -1,20 +0,0 @@
-# Gitea Application Configuration
-# Contains Gitea-specific settings and database connection strings
-
-# Timezone
-TZ={{ container_timezone }}
-
-# User/Group IDs
-USER_UID={{ user_uid}}
-USER_GID={{ user_gid}}
-
-# Database Configuration
-GITEA__database__DB_TYPE=postgres
-GITEA__database__HOST={{ service_name }}-postgres:5432
-GITEA__database__NAME={{ database_name }}
-GITEA__database__USER={{ database_user }}
-GITEA__database__PASSWD={{ database_password }}
-
-# Server Configuration
-GITEA__server__SSH_PORT={{ gitea_ssh_port }}
-GITEA__server__ROOT_URL={{ gitea_root_url }}

+ 5 - 0
library/compose/gitea/.env.j2

@@ -0,0 +1,5 @@
+# Gitea Secrets
+# Contains sensitive credentials
+
+# Database Password
+DATABASE_PASSWORD={{ database_password }}

+ 0 - 9
library/compose/gitea/.env.postgres.j2

@@ -1,9 +0,0 @@
-# PostgreSQL Database Configuration
-# Contains only database credentials needed by Postgres container
-
-# Timezone
-TZ={{ container_timezone }}
-
-POSTGRES_USER={{ database_user }}
-POSTGRES_PASSWORD={{ database_password }}
-POSTGRES_DB={{ database_name }}

+ 108 - 33
library/compose/gitea/compose.yaml.j2

@@ -1,70 +1,131 @@
 services:
   {{ service_name }}:
     image: docker.io/gitea/gitea:1.25.1
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
     container_name: {{ container_name }}
-    env_file:
-      - .env.gitea
-    {% if ports_enabled %}
-    ports:
-      - {{ ports_http }}:3000
-      - {{ ports_ssh }}:22
     {% endif %}
-    {% if network_enabled or traefik_enabled %}
+    environment:
+      - TZ={{ container_timezone }}
+      - USER_UID={{ user_uid }}
+      - USER_GID={{ user_gid }}
+      - GITEA__database__DB_TYPE=postgres
+      - GITEA__database__HOST={{ service_name }}-postgres:5432
+      - GITEA__database__NAME={{ database_name }}
+      - GITEA__database__USER={{ database_user }}
+      - GITEA__database__PASSWD=${DATABASE_PASSWORD}
+      - GITEA__server__SSH_PORT={{ gitea_ssh_port }}
+      - GITEA__server__ROOT_URL={{ gitea_root_url }}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
     networks:
-      {% if network_enabled %}
-      - {{ network_name }}
-      {% endif %}
       {% if traefik_enabled %}
-      - {{ traefik_network }}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if network_mode == 'bridge' %}
+    ports:
+      {% if not traefik_enabled %}
+      {% if swarm_enabled %}
+      - target: 3000
+        published: {{ ports_http }}
+        protocol: tcp
+        mode: host
+      {% else %}
+      - "{{ ports_http }}:3000"
+      {% endif %}
       {% endif %}
+      - "{{ ports_ssh }}:22"
     {% endif %}
-    {% if traefik_enabled %}
+    volumes:
+      - gitea-data:/data
+      - /etc/timezone:/etc/timezone:ro
+      - /etc/localtime:/etc/localtime:ro
+    {% if traefik_enabled and not swarm_enabled %}
     labels:
       - traefik.enable=true
       - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}.loadbalancer.server.port=3000
-      - traefik.http.services.{{ service_name }}.loadbalancer.server.scheme=http
+      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=3000
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
       {% if traefik_tls_enabled %}
+      - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
-      - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint  }}
+      - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
       - traefik.http.routers.{{ service_name }}-https.tls=true
       - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
     {% endif %}
-    volumes:
-      - gitea-data:/data
-      - /etc/timezone:/etc/timezone:ro
-      - /etc/localtime:/etc/localtime:ro
     depends_on:
       - {{ service_name }}-postgres
-    restart: {{ restart_policy }}
+    {% if swarm_enabled %}
+    deploy:
+      mode: {{ swarm_placement_mode }}
+      {% if swarm_placement_mode == 'replicated' %}
+      replicas: {{ swarm_replicas }}
+      {% endif %}
+      restart_policy:
+        condition: on-failure
+      {% if traefik_enabled %}
+      labels:
+        - traefik.enable=true
+        - traefik.docker.network={{ traefik_network }}
+        - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=3000
+        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+        {% if traefik_tls_enabled %}
+        - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-https.tls=true
+        - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+        {% endif %}
+      {% endif %}
+    {% endif %}
 
   {% if not database_external %}
   {{ service_name }}-postgres:
     image: docker.io/library/postgres:17.6
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
     container_name: {{ service_name }}-db
-    env_file:
-      - .env.postgres
+    {% endif %}
+    environment:
+      - TZ={{ container_timezone }}
+      - POSTGRES_USER={{ database_user }}
+      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
+      - POSTGRES_DB={{ database_name }}
     healthcheck:
       test: ["CMD-SHELL", "pg_isready -U {{ database_user }}"]
       start_period: 30s
       interval: 10s
       timeout: 10s
       retries: 5
-    volumes:
-      - gitea-db:/var/lib/postgresql/data
-    {% if network_enabled or traefik_enabled %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
     networks:
-      {% if network_enabled %}
-      - {{ network_name }}
-      {% endif %}
       {% if traefik_enabled %}
-      - {{ traefik_network }}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
       {% endif %}
     {% endif %}
-    restart: {{ restart_policy }}
+    volumes:
+      - gitea-db:/var/lib/postgresql/data
   {% endif %}
 
 volumes:
@@ -75,14 +136,28 @@ volumes:
     driver: local
   {% endif %}
 
-{% if network_enabled or traefik_enabled %}
+{% if network_mode != 'host' %}
 networks:
-  {% if network_enabled %}
   {{ network_name }}:
     {% if network_external %}
     external: true
+    {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    name: {{ network_name }}
+    {% elif swarm_enabled %}
+    driver: overlay
+    attachable: true
+    {% else %}
+    driver: bridge
+    {% endif %}
     {% endif %}
-  {% endif %}
   {% if traefik_enabled %}
   {{ traefik_network }}:
     external: true

+ 1 - 0
library/compose/gitea/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Gitea
   description: >

+ 2 - 5
library/compose/gitlab-runner/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Gitlab-Runner
   description: >
@@ -15,11 +16,7 @@ metadata:
   version: alpine-v17.9.1
   author: Christian Lempa
   date: '2025-09-28'
-  tags:
-    - gitlab
-    - git
-    - ci-cd
-  draft: true
+  tags: []
 spec:
   general:
     vars:

+ 115 - 39
library/compose/gitlab/compose.yaml.j2

@@ -1,60 +1,114 @@
 services:
   {{ service_name }}:
     image: docker.io/gitlab/gitlab-ce:18.5.1-ce.0
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
     container_name: {{ container_name }}
+    {% endif %}
     shm_size: '256m'
-{% if traefik_enabled %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
     networks:
-      - {{ traefik_network }}
-{% endif %}
-    volumes:
-      - ./config/gitlab.rb:/etc/gitlab/gitlab.rb:ro
-      - gitlab-config:/etc/gitlab
-      - gitlab-logs:/var/log/gitlab
-      - gitlab-data:/var/opt/gitlab
-{% if ports_enabled %}
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if network_mode == 'bridge' %}
     ports:
+      {% if not traefik_enabled %}
+      {% if swarm_enabled %}
+      - target: 80
+        published: {{ ports_http }}
+        protocol: tcp
+        mode: host
+      {% else %}
       - "{{ ports_http }}:80"
+      {% endif %}
+      {% endif %}
       - "{{ ports_ssh }}:22"
       {% if registry_enabled %}
       - "{{ ports_registry }}:5678"
       {% endif %}
-{% else %}
-    ports:
-      - "{{ ports_ssh }}:22"
-{% endif %}
-{% if traefik_enabled %}
+    {% endif %}
+    volumes:
+      - ./config/gitlab.rb:/etc/gitlab/gitlab.rb:ro
+      - gitlab-config:/etc/gitlab
+      - gitlab-logs:/var/log/gitlab
+      - gitlab-data:/var/opt/gitlab
+    {% if traefik_enabled and not swarm_enabled %}
     labels:
       - traefik.enable=true
       - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ container_name }}.loadbalancer.server.port=80
-      - traefik.http.services.{{ container_name }}.loadbalancer.server.scheme=http
-      - traefik.http.routers.{{ container_name }}-http.service={{ container_name }}
-      - traefik.http.routers.{{ container_name }}-http.rule=Host(`{{ traefik_host }}`)
-      - traefik.http.routers.{{ container_name }}-http.entrypoints={{ traefik_entrypoint }}
+      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=80
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+      - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
       {% if traefik_tls_enabled %}
-      - traefik.http.routers.{{ container_name }}-https.service={{ container_name }}
-      - traefik.http.routers.{{ container_name }}-https.rule=Host(`{{ traefik_host }}`)
-      - traefik.http.routers.{{ container_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
-      - traefik.http.routers.{{ container_name }}-https.tls=true
-      - traefik.http.routers.{{ container_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+      - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+      - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-https.tls=true
+      - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
-  {% if registry_enabled %}
-      - traefik.http.services.{{ container_name }}-registry.loadbalancer.server.port={{ ports_registry }}
-      - traefik.http.services.{{ container_name }}-registry.loadbalancer.server.scheme=http
-      - traefik.http.routers.{{ container_name }}-registry-http.service={{ container_name }}-registry
-      - traefik.http.routers.{{ container_name }}-registry-http.rule=Host(`{{ traefik_registry_host }}`)
-      - traefik.http.routers.{{ container_name }}-registry-http.entrypoints={{ traefik_entrypoint }}
+      {% if registry_enabled %}
+      - traefik.http.services.{{ service_name }}-registry.loadBalancer.server.port=5678
+      - traefik.http.routers.{{ service_name }}-registry-http.service={{ service_name }}-registry
+      - traefik.http.routers.{{ service_name }}-registry-http.rule=Host(`{{ traefik_registry_host }}`)
+      - traefik.http.routers.{{ service_name }}-registry-http.entrypoints={{ traefik_entrypoint }}
       {% if traefik_tls_enabled %}
-      - traefik.http.routers.{{ container_name }}-registry-https.service={{ container_name }}-registry
-      - traefik.http.routers.{{ container_name }}-registry-https.rule=Host(`{{ traefik_registry_host }}`)
-      - traefik.http.routers.{{ container_name }}-registry-https.entrypoints={{ traefik_tls_entrypoint }}
-      - traefik.http.routers.{{ container_name }}-registry-https.tls=true
-      - traefik.http.routers.{{ container_name }}-registry-https.tls.certresolver={{ traefik_tls_certresolver }}
+      - traefik.http.routers.{{ service_name }}-registry-https.service={{ service_name }}-registry
+      - traefik.http.routers.{{ service_name }}-registry-https.rule=Host(`{{ traefik_registry_host }}`)
+      - traefik.http.routers.{{ service_name }}-registry-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-registry-https.tls=true
+      - traefik.http.routers.{{ service_name }}-registry-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
-  {% endif %}
-{% endif %}
-    restart: {{ restart_policy }}
+      {% endif %}
+    {% endif %}
+    {% if swarm_enabled %}
+    deploy:
+      mode: {{ swarm_placement_mode }}
+      {% if swarm_placement_mode == 'replicated' %}
+      replicas: {{ swarm_replicas }}
+      {% endif %}
+      restart_policy:
+        condition: on-failure
+      {% if traefik_enabled %}
+      labels:
+        - traefik.enable=true
+        - traefik.docker.network={{ traefik_network }}
+        - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=80
+        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+        {% if traefik_tls_enabled %}
+        - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-https.tls=true
+        - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+        {% endif %}
+        {% if registry_enabled %}
+        - traefik.http.services.{{ service_name }}-registry.loadBalancer.server.port=5678
+        - traefik.http.routers.{{ service_name }}-registry-http.service={{ service_name }}-registry
+        - traefik.http.routers.{{ service_name }}-registry-http.rule=Host(`{{ traefik_registry_host }}`)
+        - traefik.http.routers.{{ service_name }}-registry-http.entrypoints={{ traefik_entrypoint }}
+        {% if traefik_tls_enabled %}
+        - traefik.http.routers.{{ service_name }}-registry-https.service={{ service_name }}-registry
+        - traefik.http.routers.{{ service_name }}-registry-https.rule=Host(`{{ traefik_registry_host }}`)
+        - traefik.http.routers.{{ service_name }}-registry-https.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-registry-https.tls=true
+        - traefik.http.routers.{{ service_name }}-registry-https.tls.certresolver={{ traefik_tls_certresolver }}
+        {% endif %}
+        {% endif %}
+      {% endif %}
+    {% endif %}
 
 volumes:
   gitlab-config:
@@ -64,8 +118,30 @@ volumes:
   gitlab-data:
     driver: local
 
-{% if traefik_enabled %}
+{% if network_mode != 'host' %}
 networks:
+  {{ network_name }}:
+    {% if network_external %}
+    external: true
+    {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    name: {{ network_name }}
+    {% elif swarm_enabled %}
+    driver: overlay
+    attachable: true
+    {% else %}
+    driver: bridge
+    {% endif %}
+    {% endif %}
+  {% if traefik_enabled %}
   {{ traefik_network }}:
     external: true
+  {% endif %}
 {% endif %}

+ 3 - 2
library/compose/gitlab/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: GitLab
   description: >
@@ -25,10 +26,10 @@ metadata:
     3. Access the web interface:
        {% if traefik_enabled -%}
        - Via Traefik: https://{{ traefik_host }}
-       {% if ports_enabled %}- Direct access: http://localhost:{{ ports_http }}{% endif %}
+       {% if not traefik_enabled and network_mode == 'bridge' %}- Direct access: http://localhost:{{ ports_http }}{% endif %}
        {%- else -%}
        - Open {{ external_url }} in your browser
-       {% if ports_enabled %}- Or: http://localhost:{{ ports_http }}{% endif %}
+       {% if network_mode == 'bridge' %}- Or: http://localhost:{{ ports_http }}{% endif %}
        {%- endif %}
 
     4. Initial login:

+ 71 - 16
library/compose/grafana/compose.yaml.j2

@@ -1,52 +1,107 @@
 services:
   {{ service_name }}:
     image: docker.io/grafana/grafana-oss:12.1.1
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
     container_name: {{ container_name }}
+    {% endif %}
     environment:
       - TZ={{ container_timezone }}
-    {% if ports_enabled %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
     ports:
+      {% if swarm_enabled %}
+      - target: 3000
+        published: {{ ports_http }}
+        protocol: tcp
+        mode: host
+      {% else %}
       - "{{ ports_http }}:3000"
+      {% endif %}
     {% endif %}
     volumes:
       - grafana-data:/var/lib/grafana
-    {% if network_enabled or traefik_enabled %}
-    networks:
-      {% if network_enabled %}
-      - {{ network_name }}
-      {% endif %}
-      {% if traefik_enabled %}
-      - {{ traefik_network }}
-      {% endif %}
-    {% endif %}
-    {% if traefik_enabled %}
+    {% if traefik_enabled and not swarm_enabled %}
     labels:
       - traefik.enable=true
       - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}.loadbalancer.server.port=3000
+      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=3000
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
       {% if traefik_tls_enabled %}
+      - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
       - traefik.http.routers.{{ service_name }}-https.tls=true
       - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
     {% endif %}
-    restart: {{ restart_policy }}
+    {% if swarm_enabled %}
+    deploy:
+      mode: {{ swarm_placement_mode }}
+      {% if swarm_placement_mode == 'replicated' %}
+      replicas: {{ swarm_replicas }}
+      {% endif %}
+      restart_policy:
+        condition: on-failure
+      {% if traefik_enabled %}
+      labels:
+        - traefik.enable=true
+        - traefik.docker.network={{ traefik_network }}
+        - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=3000
+        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+        {% if traefik_tls_enabled %}
+        - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-https.tls=true
+        - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+        {% endif %}
+      {% endif %}
+    {% endif %}
 
 volumes:
   grafana-data:
     driver: local
 
-{% if network_enabled or traefik_enabled %}
+{% if network_mode != 'host' %}
 networks:
-  {% if network_enabled %}
   {{ network_name }}:
     {% if network_external %}
     external: true
+    {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    name: {{ network_name }}
+    {% elif swarm_enabled %}
+    driver: overlay
+    attachable: true
+    {% else %}
+    driver: bridge
+    {% endif %}
     {% endif %}
-  {% endif %}
   {% if traefik_enabled %}
   {{ traefik_network }}:
     external: true

+ 1 - 0
library/compose/grafana/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Grafana
   description: >

+ 2 - 2
library/compose/heimdall/compose.yaml.j2

@@ -1,7 +1,7 @@
 services:
-  heimdall:
+  {{ service_name }}:
     image: lscr.io/linuxserver/heimdall:2.7.6
-    container_name: heimdall
+    container_name: {{ container_name }}
     environment:
       - PUID=1000
       - PGID=1000

+ 6 - 3
library/compose/heimdall/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Heimdall
   description: >
@@ -13,12 +14,14 @@ metadata:
   version: 2.7.6
   author: Christian Lempa
   date: '2025-10-31'
-  tags:
-    - dashboard
-  draft: true
+  tags: []
 spec:
   general:
     vars:
+      service_name:
+        default: heimdall
+      container_name:
+        default: heimdall
       heimdall_version:
         type: str
         description: Heimdall version

+ 2 - 2
library/compose/homeassistant/compose.yaml.j2

@@ -1,6 +1,6 @@
 services:
-  homeassistant:
-    container_name: homeassistant
+  {{ service_name }}:
+    container_name: {{ container_name }}
     image: ghcr.io/home-assistant/home-assistant:2025.11.1
     volumes:
       - ./config:/config

+ 6 - 4
library/compose/homeassistant/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Homeassistant
   description: >
@@ -13,13 +14,14 @@ metadata:
   version: 2025.11.1
   author: Christian Lempa
   date: '2025-11-07'
-  tags:
-    - home-automation
-    - iot
-  draft: true
+  tags: []
 spec:
   general:
     vars:
+      service_name:
+        default: homeassistant
+      container_name:
+        default: homeassistant
       homeassistant_version:
         type: str
         description: Homeassistant version

+ 2 - 3
library/compose/homepage/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Homepage
   description: >
@@ -15,9 +16,7 @@ metadata:
   version: v1.6.1
   author: Christian Lempa
   date: '2025-11-05'
-  tags:
-    - dashboard
-  draft: true
+  tags: []
 spec:
   general:
     vars:

+ 71 - 16
library/compose/homer/compose.yaml.j2

@@ -1,48 +1,103 @@
 services:
   {{ service_name }}:
     image: docker.io/b4bz/homer:v25.10.1
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
     container_name: {{ container_name }}
+    {% endif %}
     environment:
       - TZ={{ container_timezone }}
-    {% if ports_enabled %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
     ports:
+      {% if swarm_enabled %}
+      - target: 8080
+        published: {{ ports_http }}
+        protocol: tcp
+        mode: host
+      {% else %}
       - "{{ ports_http }}:8080"
+      {% endif %}
     {% endif %}
     volumes:
       - ./assets:/www/assets
-    {% if network_enabled or traefik_enabled %}
-    networks:
-      {% if network_enabled %}
-      - {{ network_name }}
-      {% endif %}
-      {% if traefik_enabled %}
-      - {{ traefik_network }}
-      {% endif %}
-    {% endif %}
-    {% if traefik_enabled %}
+    {% if traefik_enabled and not swarm_enabled %}
     labels:
       - traefik.enable=true
       - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}.loadbalancer.server.port=8080
+      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=8080
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
       {% if traefik_tls_enabled %}
+      - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
       - traefik.http.routers.{{ service_name }}-https.tls=true
       - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
     {% endif %}
-    restart: {{ restart_policy }}
+    {% if swarm_enabled %}
+    deploy:
+      mode: {{ swarm_placement_mode }}
+      {% if swarm_placement_mode == 'replicated' %}
+      replicas: {{ swarm_replicas }}
+      {% endif %}
+      restart_policy:
+        condition: on-failure
+      {% if traefik_enabled %}
+      labels:
+        - traefik.enable=true
+        - traefik.docker.network={{ traefik_network }}
+        - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=8080
+        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+        {% if traefik_tls_enabled %}
+        - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-https.tls=true
+        - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+        {% endif %}
+      {% endif %}
+    {% endif %}
 
-{% if network_enabled or traefik_enabled %}
+{% if network_mode != 'host' %}
 networks:
-  {% if network_enabled %}
   {{ network_name }}:
     {% if network_external %}
     external: true
+    {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    name: {{ network_name }}
+    {% elif swarm_enabled %}
+    driver: overlay
+    attachable: true
+    {% else %}
+    driver: bridge
+    {% endif %}
     {% endif %}
-  {% endif %}
   {% if traefik_enabled %}
   {{ traefik_network }}:
     external: true

+ 2 - 1
library/compose/homer/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Homer
   description: >
@@ -36,7 +37,7 @@ metadata:
     5. Access your dashboard:
        {% if traefik_enabled -%}
        - Via Traefik: https://{{ traefik_host }}
-       {% if ports_enabled %}- Direct access: http://localhost:{{ ports_http }}{% endif %}
+       {% if not traefik_enabled and network_mode == 'bridge' %}- Direct access: http://localhost:{{ ports_http }}{% endif %}
        {%- else -%}
        - Open http://localhost:{{ ports_http }} in your browser
        {%- endif %}

+ 83 - 30
library/compose/influxdb/compose.yaml.j2

@@ -1,56 +1,109 @@
 services:
-  {{ service_name | default('influxdb') }}:
-    container_name: {{ container_name | default('influxdb') }}
+  {{ service_name }}:
     image: docker.io/library/influxdb:2.7.12-alpine
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
+    container_name: {{ container_name }}
+    {% endif %}
     environment:
-      - TZ={{ container_timezone | default('UTC') }}
+      - TZ={{ container_timezone }}
       - DOCKER_INFLUXDB_INIT_MODE=setup
-      - DOCKER_INFLUXDB_INIT_USERNAME={{ influxdb_init_username | default('admin') }}
-      - DOCKER_INFLUXDB_INIT_PASSWORD={{ influxdb_init_password | default('password') }}
-      - DOCKER_INFLUXDB_INIT_ORG={{ influxdb_init_org | default('myorg') }}
-      - DOCKER_INFLUXDB_INIT_BUCKET={{ influxdb_init_bucket | default('mybucket') }}
+      - DOCKER_INFLUXDB_INIT_USERNAME={{ influxdb_init_username }}
+      - DOCKER_INFLUXDB_INIT_PASSWORD=${INFLUXDB_INIT_PASSWORD}
+      - DOCKER_INFLUXDB_INIT_ORG={{ influxdb_init_org }}
+      - DOCKER_INFLUXDB_INIT_BUCKET={{ influxdb_init_bucket }}
       {% if influxdb_init_retention %}
       - DOCKER_INFLUXDB_INIT_RETENTION={{ influxdb_init_retention }}
       {% endif %}
       {% if influxdb_init_token %}
-      - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN={{ influxdb_init_token }}
+      - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=${INFLUXDB_INIT_TOKEN}
+      {% endif %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
       {% endif %}
-    {% if ports_enabled %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
     ports:
-      - "{{ ports_http | default(8086) }}:8086"
+      {% if swarm_enabled %}
+      - target: 8086
+        published: {{ ports_http }}
+        protocol: tcp
+        mode: host
+      {% else %}
+      - "{{ ports_http }}:8086"
+      {% endif %}
     {% endif %}
     volumes:
       - influxdb-data:/var/lib/influxdb2
       - /etc/influxdb2:/etc/influxdb2
-    {% if network_enabled %}
-    networks:
-      - {{ network_name | default('bridge') }}
-    {% endif %}
-    {% if traefik_enabled %}
+    {% if traefik_enabled and not swarm_enabled %}
     labels:
       - traefik.enable=true
-      - traefik.docker.network={{ traefik_network | default('traefik') }}
-      - traefik.http.services.{{ service_name | default('influxdb') }}.loadbalancer.server.port=8086
-      - traefik.http.services.{{ service_name | default('influxdb') }}.loadbalancer.server.scheme=http
-      - traefik.http.routers.{{ service_name | default('influxdb') }}-http.rule=Host(`{{ traefik_host }}`)
-      - traefik.http.routers.{{ service_name | default('influxdb') }}-http.entrypoints={{ traefik_entrypoint | default('web') }}
+      - traefik.docker.network={{ traefik_network }}
+      - traefik.http.services.{{ service_name }}-web.loadbalancer.server.port=8086
+      - traefik.http.services.{{ service_name }}-web.loadbalancer.server.scheme=http
+      - traefik.http.routers.{{ service_name }}-web-http.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-web-http.entrypoints={{ traefik_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-web-http.service={{ service_name }}-web
       {% if traefik_tls_enabled %}
-      - traefik.http.routers.{{ service_name | default('influxdb') }}-https.rule=Host(`{{ traefik_host }}`)
-      - traefik.http.routers.{{ service_name | default('influxdb') }}-https.entrypoints={{ traefik_tls_entrypoint | default('websecure') }}
-      - traefik.http.routers.{{ service_name | default('influxdb') }}-https.tls=true
-      - traefik.http.routers.{{ service_name | default('influxdb') }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+      - traefik.http.routers.{{ service_name }}-web-https.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-web-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-web-https.tls=true
+      - traefik.http.routers.{{ service_name }}-web-https.tls.certresolver={{ traefik_tls_certresolver }}
+      - traefik.http.routers.{{ service_name }}-web-https.service={{ service_name }}-web
+      {% endif %}
+    {% endif %}
+    {% if swarm_enabled %}
+    deploy:
+      replicas: {{ swarm_replicas }}
+      {% if traefik_enabled %}
+      labels:
+        - traefik.enable=true
+        - traefik.docker.network={{ traefik_network }}
+        - traefik.http.services.{{ service_name }}-web.loadbalancer.server.port=8086
+        - traefik.http.services.{{ service_name }}-web.loadbalancer.server.scheme=http
+        - traefik.http.routers.{{ service_name }}-web-http.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-web-http.entrypoints={{ traefik_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-web-http.service={{ service_name }}-web
+        {% if traefik_tls_enabled %}
+        - traefik.http.routers.{{ service_name }}-web-https.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-web-https.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-web-https.tls=true
+        - traefik.http.routers.{{ service_name }}-web-https.tls.certresolver={{ traefik_tls_certresolver }}
+        - traefik.http.routers.{{ service_name }}-web-https.service={{ service_name }}-web
+        {% endif %}
       {% endif %}
     {% endif %}
-    restart: {{ restart_policy | default('unless-stopped') }}
 
 volumes:
   influxdb-data:
     driver: local
 
-{% if network_enabled %}
 networks:
-  {{ network_name | default('bridge') }}:
-    {% if network_external %}
+  {% if traefik_enabled %}
+  {{ traefik_network }}:
     external: true
-    {% endif %}
-{% endif %}
+  {% endif %}
+  {% if network_mode == 'macvlan' %}
+  {{ network_name }}:
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+  {% elif network_mode == 'bridge' %}
+  {{ network_name }}:
+    driver: {% if swarm_enabled %}overlay{% else %}bridge{% endif %}
+  {% endif %}

+ 2 - 3
library/compose/influxdb/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Influxdb
   description: >
@@ -16,9 +17,7 @@ metadata:
   author: Christian Lempa
   date: '2025-09-28'
   tags:
-    - database
-    - time-series
-  draft: true
+    - traefik
 spec:
   ports:
     vars:

+ 2 - 2
library/compose/loki/compose.yaml.j2

@@ -1,6 +1,6 @@
 services:
-  loki:
-    container_name: loki
+  {{ service_name }}:
+    container_name: {{ container_name }}
     image: docker.io/grafana/loki:3.5.8
     command: "-config.file=/etc/loki/config.yaml"
     ports:

+ 6 - 4
library/compose/loki/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Loki
   description: >
@@ -16,13 +17,14 @@ metadata:
   author: Christian Lempa
   date: '2025-11-07'
   tags:
-    - grafana
-    - monitoring
-    - logging
-  draft: true
+    - traefik
 spec:
   general:
     vars:
+      service_name:
+        default: loki
+      container_name:
+        default: loki
       loki_version:
         type: str
         description: Loki version

+ 2 - 5
library/compose/mariadb/template.yaml

@@ -1,16 +1,13 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Volumes
   description: Docker compose setup for volumes
   version: 12.0.2
   author: Christian Lempa
   date: '2025-09-28'
-  tags:
-    - volumes
-    - docker
-    - compose
-  draft: true
+  tags: []
 spec:
   general:
     vars:

+ 4 - 2
library/compose/n8n-server/compose.yaml.j2

@@ -143,7 +143,8 @@ services:
       {% else -%}
       - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_entrypoint }}
       {% endif -%}
-      - traefik.http.services.{{ service_name }}.loadbalancer.server.port=5678
+      - traefik.http.services.{{ service_name }}-web.loadbalancer.server.port=5678
+      - traefik.http.routers.{{ service_name }}.service={{ service_name }}-web
     {% endif -%}
     {% if not swarm_enabled -%}
     restart: {{ restart_policy }}
@@ -172,7 +173,8 @@ services:
         {% else -%}
         - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_entrypoint }}
         {% endif -%}
-        - traefik.http.services.{{ service_name }}.loadbalancer.server.port=5678
+        - traefik.http.services.{{ service_name }}-web.loadbalancer.server.port=5678
+        - traefik.http.routers.{{ service_name }}.service={{ service_name }}-web
       {% endif -%}
     secrets:
       - {{ encryption_key_secret_name }}

+ 1 - 1
library/compose/n8n-server/template.yaml

@@ -1,6 +1,6 @@
 ---
 kind: compose
-schema: "1.1"
+schema: "1.2"
 metadata:
   name: N8N Server
   description: |

+ 1 - 1
library/compose/n8n-worker/template.yaml

@@ -1,6 +1,6 @@
 ---
 kind: compose
-schema: "1.1"
+schema: "1.2"
 metadata:
   name: N8N Worker
   description: >

+ 35 - 10
library/compose/n8n/compose.yaml.j2

@@ -35,42 +35,67 @@ services:
     volumes:
       - /etc/localtime:/etc/localtime:ro
       - data:/home/node/.n8n
-    {% if network_enabled %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
     networks:
-      - {{ network_name | default('bridge') }}
+      {% if traefik_enabled %}
+      {{ traefik_network | default('traefik') }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name | default('bridge') }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name | default('bridge') }}:
+      {% endif %}
     {% endif %}
     {% if traefik_enabled %}
     labels:
       - traefik.enable=true
       - traefik.docker.network={{ traefik_network | default('traefik') }}
+      - traefik.http.services.{{ service_name | default('n8n') }}-web.loadBalancer.server.port=5678
+      - traefik.http.routers.{{ service_name | default('n8n') }}-http.service={{ service_name | default('n8n') }}-web
       - traefik.http.routers.{{ service_name | default('n8n') }}-http.rule=Host(`{{ traefik_host | default('n8n.home.arpa') }}`)
       - traefik.http.routers.{{ service_name | default('n8n') }}-http.entrypoints={{ traefik_entrypoint | default('web') }}
       {% if traefik_tls_enabled %}
+      - traefik.http.routers.{{ service_name | default('n8n') }}-https.service={{ service_name | default('n8n') }}-web
       - traefik.http.routers.{{ service_name | default('n8n') }}-https.rule=Host(`{{ traefik_host | default('n8n.home.arpa') }}`)
       - traefik.http.routers.{{ service_name | default('n8n') }}-https.entrypoints={{ traefik_tls_entrypoint | default('websecure') }}
       - traefik.http.routers.{{ service_name | default('n8n') }}-https.tls=true
       - traefik.http.routers.{{ service_name | default('n8n') }}-https.tls.certresolver={{ traefik_tls_certresolver | default('default') }}
-      {% else %}
-      - traefik.http.routers.{{ service_name | default('n8n') }}.entrypoints={{ traefik_entrypoint | default('web') }}
       {% endif %}
-      - traefik.http.services.{{ service_name | default('n8n') }}.loadbalancer.server.port=5678
     {% endif %}
-    restart: {{ restart_policy | default('unless-stopped') }}
-    {% if ports_enabled %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
     ports:
       - "{{ ports_http | default(5678) }}:5678"
     {% endif %}
+    restart: {{ restart_policy | default('unless-stopped') }}
 
 volumes:
   data:
     driver: local
 
-{% if network_enabled %}
+{% if network_mode != 'host' %}
 networks:
   {{ network_name | default('bridge') }}:
-  {% if network_external %}
+    {% if network_external %}
     external: true
-  {% else %}
+    {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    {% else %}
     driver: bridge
+    {% endif %}
+    {% endif %}
+    name: {{ network_name | default('bridge') }}
+  {% if traefik_enabled %}
+  {{ traefik_network | default('traefik') }}:
+    external: true
   {% endif %}
 {% endif %}

+ 72 - 0
library/compose/n8n/template.yaml

@@ -0,0 +1,72 @@
+---
+kind: compose
+schema: "1.2"
+metadata:
+  name: n8n
+  description: >
+    n8n is a powerful workflow automation tool that allows you to connect different services
+    and automate tasks. It offers a visual workflow editor with support for over 200+ integrations,
+    making it easy to automate complex processes without writing code.
+
+
+    Project: https://n8n.io/
+
+
+    Documentation: https://docs.n8n.io/
+
+
+    GitHub: https://github.com/n8n-io/n8n
+  version: 1.119.0
+  author: Christian Lempa
+  date: '2025-11-09'
+  tags:
+    - traefik
+  next_steps: |
+    1. Start n8n:
+       docker compose up -d
+
+    2. Access the web interface:
+       {% if traefik_enabled -%}
+       - Via Traefik: https://{{ traefik_host }}
+       {% if not traefik_enabled and network_mode == 'bridge' %}- Direct access: http://localhost:{{ ports_http }}{% endif %}
+       {%- else -%}
+       - Open http://localhost:{{ ports_http }} in your browser
+       {%- endif %}
+
+    3. Initial setup:
+       - Create your admin account on first access
+       - Configure your workflows using the visual editor
+       - Set up credentials for services you want to integrate
+
+    4. {% if database_enabled %}Database configuration:
+       - Database: {{ database_type }}
+       - Host: {{ database_host }}
+       - Database: {{ database_name }}
+       {% endif %}
+
+    For more information, visit: https://docs.n8n.io/
+spec:
+  general:
+    vars:
+      service_name:
+        default: n8n
+      container_name:
+        default: n8n
+  database:
+    vars:
+      database_type:
+        default: default
+      database_name:
+        default: n8n
+      database_user:
+        default: n8n
+  traefik:
+    vars:
+      traefik_host:
+        default: n8n.home.arpa
+  ports:
+    vars:
+      ports_http:
+        description: Web UI port
+        type: int
+        default: 5678

+ 127 - 48
library/compose/nextcloud/compose.yaml.j2

@@ -1,79 +1,147 @@
 services:
-  {{ service_name | default('nextcloud-app') }}:
+  {{ service_name }}-app:
     image: docker.io/library/nextcloud:31.0.10-apache
-    container_name: {{ container_name | default('nextcloud-app') }}
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
+    container_name: {{ container_name }}-app
+    {% endif %}
     environment:
-      - TZ={{ container_timezone | default('UTC') }}
+      - TZ={{ container_timezone }}
       {% if database_type == 'mysql' %}
-      - MYSQL_PASSWORD={{ database_password | default('nextcloud') }}
-      - MYSQL_DATABASE={{ database_name | default('nextcloud') }}
-      - MYSQL_USER={{ database_user | default('nextcloud') }}
-      - MYSQL_HOST={{ service_name | default('nextcloud') }}-db
+      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
+      - MYSQL_DATABASE={{ database_name }}
+      - MYSQL_USER={{ database_user }}
+      - MYSQL_HOST={{ service_name }}-db
       {% elif database_type == 'postgres' %}
-      - POSTGRES_PASSWORD={{ database_password | default('nextcloud') }}
-      - POSTGRES_DB={{ database_name | default('nextcloud') }}
-      - POSTGRES_USER={{ database_user | default('nextcloud') }}
-      - POSTGRES_HOST={{ service_name | default('nextcloud') }}-db
+      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
+      - POSTGRES_DB={{ database_name }}
+      - POSTGRES_USER={{ database_user }}
+      - POSTGRES_HOST={{ service_name }}-db
+      {% endif %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
       {% endif %}
-    {% if ports_enabled %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
     ports:
-      - "{{ ports_http | default(80) }}:80"
+      {% if swarm_enabled %}
+      - target: 80
+        published: {{ ports_http }}
+        protocol: tcp
+        mode: host
+      {% else %}
+      - "{{ ports_http }}:80"
+      {% endif %}
     {% endif %}
     volumes:
       - nextcloud-data:/var/www/html
-    {% if network_enabled %}
-    networks:
-      - {{ network_name | default('bridge') }}
-    {% endif %}
-    {% if traefik_enabled %}
+    {% if traefik_enabled and not swarm_enabled %}
     labels:
       - traefik.enable=true
-      - traefik.docker.network={{ traefik_network | default('traefik') }}
-      - traefik.http.services.{{ service_name | default('nextcloud') }}.loadbalancer.server.port=80
-      - traefik.http.routers.{{ service_name | default('nextcloud') }}-http.rule=Host(`{{ traefik_host }}`)
-      - traefik.http.routers.{{ service_name | default('nextcloud') }}-http.entrypoints={{ traefik_entrypoint | default('web') }}
+      - traefik.docker.network={{ traefik_network }}
+      - traefik.http.services.{{ service_name }}-web.loadbalancer.server.port=80
+      - traefik.http.routers.{{ service_name }}-web-http.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-web-http.entrypoints={{ traefik_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-web-http.service={{ service_name }}-web
       {% if traefik_tls_enabled %}
-      - traefik.http.routers.{{ service_name | default('nextcloud') }}-https.rule=Host(`{{ traefik_host }}`)
-      - traefik.http.routers.{{ service_name | default('nextcloud') }}-https.entrypoints={{ traefik_tls_entrypoint | default('websecure') }}
-      - traefik.http.routers.{{ service_name | default('nextcloud') }}-https.tls=true
-      - traefik.http.routers.{{ service_name | default('nextcloud') }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+      - traefik.http.routers.{{ service_name }}-web-https.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-web-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-web-https.tls=true
+      - traefik.http.routers.{{ service_name }}-web-https.tls.certresolver={{ traefik_tls_certresolver }}
+      - traefik.http.routers.{{ service_name }}-web-https.service={{ service_name }}-web
       {% endif %}
     {% endif %}
     depends_on:
-      - {{ service_name | default('nextcloud') }}-db
-    restart: {{ restart_policy | default('unless-stopped') }}
+      - {{ service_name }}-db
+    {% if swarm_enabled %}
+    deploy:
+      replicas: {{ swarm_replicas }}
+      {% if traefik_enabled %}
+      labels:
+        - traefik.enable=true
+        - traefik.docker.network={{ traefik_network }}
+        - traefik.http.services.{{ service_name }}-web.loadbalancer.server.port=80
+        - traefik.http.routers.{{ service_name }}-web-http.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-web-http.entrypoints={{ traefik_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-web-http.service={{ service_name }}-web
+        {% if traefik_tls_enabled %}
+        - traefik.http.routers.{{ service_name }}-web-https.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-web-https.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-web-https.tls=true
+        - traefik.http.routers.{{ service_name }}-web-https.tls.certresolver={{ traefik_tls_certresolver }}
+        - traefik.http.routers.{{ service_name }}-web-https.service={{ service_name }}-web
+        {% endif %}
+      {% endif %}
+    {% endif %}
 
-  {{ service_name | default('nextcloud') }}-db:
+  {{ service_name }}-db:
     {% if database_type == 'mysql' %}
     # See compatibility matrix for Nextcloud 31
     # https://docs.nextcloud.com/server/31/admin_manual/installation/system_requirements.html
     image: docker.io/library/mariadb:10.11.14
-    container_name: {{ service_name | default('nextcloud') }}-db
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
+    container_name: {{ service_name }}-db
+    {% endif %}
     command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
     environment:
-      - TZ={{ container_timezone | default('UTC') }}
+      - TZ={{ container_timezone }}
       - MYSQL_RANDOM_ROOT_PASSWORD=true
-      - MYSQL_PASSWORD={{ database_password | default('nextcloud') }}
-      - MYSQL_DATABASE={{ database_name | default('nextcloud') }}
-      - MYSQL_USER={{ database_user | default('nextcloud') }}
+      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
+      - MYSQL_DATABASE={{ database_name }}
+      - MYSQL_USER={{ database_user }}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address_db }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
     volumes:
       - nextcloud-db:/var/lib/mysql
     {% elif database_type == 'postgres' %}
     image: docker.io/library/postgres:17.6
-    container_name: {{ service_name | default('nextcloud') }}-db
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
+    container_name: {{ service_name }}-db
+    {% endif %}
     environment:
-      - TZ={{ container_timezone | default('UTC') }}
-      - POSTGRES_USER={{ database_user | default('nextcloud') }}
-      - POSTGRES_PASSWORD={{ database_password | default('nextcloud') }}
-      - POSTGRES_DB={{ database_name | default('nextcloud') }}
+      - TZ={{ container_timezone }}
+      - POSTGRES_USER={{ database_user }}
+      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
+      - POSTGRES_DB={{ database_name }}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address_db }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
     volumes:
       - nextcloud-db:/var/lib/postgresql/data
     {% endif %}
-    {% if network_enabled %}
-    networks:
-      - {{ network_name | default('bridge') }}
+    {% if swarm_enabled %}
+    deploy:
+      replicas: 1
     {% endif %}
-    restart: {{ restart_policy | default('unless-stopped') }}
 
 volumes:
   nextcloud-data:
@@ -81,10 +149,21 @@ volumes:
   nextcloud-db:
     driver: local
 
-{% if network_enabled %}
 networks:
-  {{ network_name | default('bridge') }}:
-    {% if network_external %}
+  {% if traefik_enabled %}
+  {{ traefik_network }}:
     external: true
-    {% endif %}
-{% endif %}
+  {% endif %}
+  {% if network_mode == 'macvlan' %}
+  {{ network_name }}:
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+  {% elif network_mode == 'bridge' %}
+  {{ network_name }}:
+    driver: {% if swarm_enabled %}overlay{% else %}bridge{% endif %}
+  {% endif %}

+ 9 - 4
library/compose/nextcloud/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Nextcloud
   description: >
@@ -17,10 +18,7 @@ metadata:
   author: Christian Lempa
   date: '2025-10-31'
   tags:
-    - cloud
-    - file-sharing
-    - collaboration
-  draft: true
+    - traefik
 spec:
   database:
     required: true
@@ -30,6 +28,13 @@ spec:
         type: enum
         options: ["postgres", "mysql"]
         default: "postgres"
+  network:
+    vars:
+      network_macvlan_ipv4_address_db:
+        description: "Static IP address for database container (macvlan only)"
+        type: str
+        default: "192.168.1.252"
+        needs: "network_mode=macvlan"
   ports:
     vars:
       ports_http:

+ 73 - 43
library/compose/nginx/compose.yaml.j2

@@ -1,33 +1,39 @@
 services:
-  {{ service_name | default('nginx') }}:
+  {{ service_name }}:
     image: docker.io/library/nginx:1.28.0-alpine
     {% if not swarm_enabled %}
-    container_name: {{ container_name | default('nginx') }}
+    restart: {{ restart_policy }}
+    container_name: {{ container_name }}
     {% endif %}
-    {% if swarm_enabled %}
-    deploy:
-      replicas: {{ swarm_replicas | default(1) }}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
       {% if traefik_enabled %}
-      labels:
-        - traefik.enable=true
-        - traefik.docker.network={{ traefik_network | default('traefik') }}
-        - traefik.http.services.{{ container_name | default('nginx') }}.loadbalancer.server.port=80
-        - traefik.http.routers.{{ container_name | default('nginx') }}-http.rule=Host(`{{ traefik_host }}`)
-        - traefik.http.routers.{{ container_name | default('nginx') }}-http.entrypoints={{ traefik_entrypoint | default('web') }}
-        - traefik.http.routers.{{ container_name | default('nginx') }}-http.service={{ container_name | default('nginx') }}
-        {% if traefik_tls_enabled %}
-        - traefik.http.routers.{{ container_name | default('nginx') }}-https.rule=Host(`{{ traefik_host }}`)
-        - traefik.http.routers.{{ container_name | default('nginx') }}-https.entrypoints={{ traefik_tls_entrypoint | default('websecure') }}
-        - traefik.http.routers.{{ container_name | default('nginx') }}-https.tls=true
-        - traefik.http.routers.{{ container_name | default('nginx') }}-https.tls.certresolver={{ traefik_tls_certresolver }}
-        - traefik.http.routers.{{ container_name | default('nginx') }}-https.service={{ container_name | default('nginx') }}
-        {% endif %}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
       {% endif %}
     {% endif %}
-    {% if ports_enabled %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
     ports:
-      - "{{ ports_http | default(8080) }}:80"
-      - "{{ ports_https | default(8443) }}:443"
+      {% if swarm_enabled %}
+      - target: 80
+        published: {{ ports_http }}
+        protocol: tcp
+        mode: host
+      - target: 443
+        published: {{ ports_https }}
+        protocol: tcp
+        mode: host
+      {% else %}
+      - "{{ ports_http }}:80"
+      - "{{ ports_https }}:443"
+      {% endif %}
     {% endif %}
     # volumes:
     #   - ./config/default.conf:/etc/nginx/conf.d/default.conf:ro
@@ -35,31 +41,55 @@ services:
     {% if traefik_enabled and not swarm_enabled %}
     labels:
       - traefik.enable=true
-      - traefik.docker.network={{ traefik_network | default('traefik') }}
-      - traefik.http.services.{{ container_name | default('nginx') }}.loadbalancer.server.port=80
-      - traefik.http.routers.{{ container_name | default('nginx') }}-http.rule=Host(`{{ traefik_host }}`)
-      - traefik.http.routers.{{ container_name | default('nginx') }}-http.entrypoints={{ traefik_entrypoint | default('web') }}
-      - traefik.http.routers.{{ container_name | default('nginx') }}-http.service={{ container_name | default('nginx') }}
+      - traefik.docker.network={{ traefik_network }}
+      - traefik.http.services.{{ service_name }}-web.loadbalancer.server.port=80
+      - traefik.http.routers.{{ service_name }}-web-http.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-web-http.entrypoints={{ traefik_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-web-http.service={{ service_name }}-web
       {% if traefik_tls_enabled %}
-      - traefik.http.routers.{{ container_name | default('nginx') }}-https.rule=Host(`{{ traefik_host }}`)
-      - traefik.http.routers.{{ container_name | default('nginx') }}-https.entrypoints={{ traefik_tls_entrypoint | default('websecure') }}
-      - traefik.http.routers.{{ container_name | default('nginx') }}-https.tls=true
-      - traefik.http.routers.{{ container_name | default('nginx') }}-https.tls.certresolver={{ traefik_tls_certresolver }}
-      - traefik.http.routers.{{ container_name | default('nginx') }}-https.service={{ container_name | default('nginx') }}
+      - traefik.http.routers.{{ service_name }}-web-https.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-web-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-web-https.tls=true
+      - traefik.http.routers.{{ service_name }}-web-https.tls.certresolver={{ traefik_tls_certresolver }}
+      - traefik.http.routers.{{ service_name }}-web-https.service={{ service_name }}-web
       {% endif %}
     {% endif %}
-    {% if network_enabled %}
-    networks:
-      - {{ network_name | default('bridge') }}
-    {% endif %}
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy | default('unless-stopped') }}
+    {% if swarm_enabled %}
+    deploy:
+      replicas: {{ swarm_replicas }}
+      {% if traefik_enabled %}
+      labels:
+        - traefik.enable=true
+        - traefik.docker.network={{ traefik_network }}
+        - traefik.http.services.{{ service_name }}-web.loadbalancer.server.port=80
+        - traefik.http.routers.{{ service_name }}-web-http.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-web-http.entrypoints={{ traefik_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-web-http.service={{ service_name }}-web
+        {% if traefik_tls_enabled %}
+        - traefik.http.routers.{{ service_name }}-web-https.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-web-https.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-web-https.tls=true
+        - traefik.http.routers.{{ service_name }}-web-https.tls.certresolver={{ traefik_tls_certresolver }}
+        - traefik.http.routers.{{ service_name }}-web-https.service={{ service_name }}-web
+        {% endif %}
+      {% endif %}
     {% endif %}
 
-{% if network_enabled %}
 networks:
-  {{ network_name | default('bridge') }}:
-    {% if network_external %}
+  {% if traefik_enabled %}
+  {{ traefik_network }}:
     external: true
-    {% endif %}
-{% endif %}
+  {% endif %}
+  {% if network_mode == 'macvlan' %}
+  {{ network_name }}:
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+  {% elif network_mode == 'bridge' %}
+  {{ network_name }}:
+    driver: {% if swarm_enabled %}overlay{% else %}bridge{% endif %}
+  {% endif %}

+ 2 - 4
library/compose/nginx/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Nginx
   description: >
@@ -16,10 +17,7 @@ metadata:
   date: 2023-10-01
   author: Christian Lempa
   tags:
-    - nginx
-    - webserver
-    - reverse-proxy
-  draft: true
+    - traefik
 spec:
   ports:
     vars:

+ 2 - 4
library/compose/nginxproxymanager/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Nginx Proxy Manager
   description: >
@@ -15,10 +16,7 @@ metadata:
   version: 2.12.6
   author: Christian Lempa
   date: '2025-09-28'
-  tags:
-    - nginx
-    - reverse-proxy
-  draft: true
+  tags: []
 spec:
   general:
     vars:

+ 68 - 24
library/compose/openwebui/compose.yaml.j2

@@ -1,40 +1,84 @@
 services:
-  openwebui:
-    image: ghcr.io/open-webui/open-webui:v0.6.36
-    container_name: openwebui
-    ports:
-      - "8080:8080"
+  {{ service_name }}:
+    image: ghcr.io/open-webui/open-webui:v{{ openwebui_version }}
+    container_name: {{ container_name }}
+    hostname: {{ container_hostname }}
     environment:
-      # Ollama Config
-      - OLLAMA_BASE_URL=http://ollama.example.com:11434
-      # Authentik SSO Config
+      - TZ={{ container_timezone }}
+      - OLLAMA_BASE_URL={{ ollama_base_url }}
+      {% if authentik_enabled %}
       - ENABLE_OAUTH_SIGNUP=true
-      - OAUTH_MERGE_ACCOUNTS_BY_EMAIL=false
+      - OAUTH_MERGE_ACCOUNTS_BY_EMAIL={{ oauth_merge_accounts|lower }}
       - OAUTH_PROVIDER_NAME=authentik
-      - OPENID_PROVIDER_URL=https://authentik.example.com/application/o/openwebui-slug/.well-known/openid-configuration
-      - OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID}
-      - OAUTH_CLIENT_SECRET=${OAUTH_CLIENT_SECRET}
-      - OAUTH_SCOPES=openid email profile
-      - OPENID_REDIRECT_URI=https://openwebui.example.com/oauth/oidc/callback
+      - OPENID_PROVIDER_URL={{ openid_provider_url }}
+      - OAUTH_CLIENT_ID={{ authentik_client_id }}
+      - OAUTH_CLIENT_SECRET={{ authentik_client_secret }}
+      - OAUTH_SCOPES={{ oauth_scopes }}
+      - OPENID_REDIRECT_URI={{ openid_redirect_uri }}
+      {% endif %}
     volumes:
       - data:/app/backend/data:rw
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
     networks:
-      - frontend
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
+    ports:
+      - "{{ ports_http }}:8080"
+    {% endif %}
+    {% if traefik_enabled %}
     labels:
       - traefik.enable=true
-      - traefik.docker.network=frontend
-      - traefik.http.routers.openwebui.rule=Host(`openwebui.example.com`)
-      - traefik.http.routers.openwebui.entrypoints=websecure
-      - traefik.http.routers.openwebui.tls=true
-      - traefik.http.routers.openwebui.tls.certresolver=cloudflare
-      - traefik.http.routers.openwebui.service=openwebui
-      - traefik.http.services.openwebui.loadBalancer.server.port=8080
-    restart: unless-stopped
+      - traefik.docker.network={{ traefik_network }}
+      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=8080
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+      - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+      {% if traefik_tls_enabled %}
+      - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+      - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+      - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-https.tls=true
+      - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+      {% endif %}
+    {% endif %}
+    restart: {{ restart_policy }}
 
 volumes:
   data:
     driver: local
 
+{% if network_mode != 'host' %}
 networks:
-  frontend:
+  {{ network_name }}:
+    {% if network_external %}
+    external: true
+    {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    {% else %}
+    driver: bridge
+    {% endif %}
+    {% endif %}
+    name: {{ network_name }}
+  {% if traefik_enabled %}
+  {{ traefik_network }}:
     external: true
+  {% endif %}
+{% endif %}

+ 40 - 3
library/compose/openwebui/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Openwebui
   description: >
@@ -18,13 +19,49 @@ metadata:
   author: Christian Lempa
   date: '2025-11-07'
   tags:
-    - ai
-    - automation
-  draft: true
+    - traefik
 spec:
   general:
     vars:
+      service_name:
+        default: openwebui
+      container_name:
+        default: openwebui
       openwebui_version:
         type: str
         description: Openwebui version
         default: latest
+      ollama_base_url:
+        type: str
+        description: Ollama API base URL
+        default: http://ollama:11434
+  traefik:
+    vars:
+      traefik_host:
+        default: openwebui.home.arpa
+  ports:
+    vars:
+      ports_http:
+        description: Web UI port
+        type: int
+        default: 8080
+  authentik:
+    vars:
+      authentik_enabled:
+        default: false
+      openid_provider_url:
+        type: str
+        description: OpenID provider configuration URL
+        default: https://authentik.example.com/application/o/openwebui/.well-known/openid-configuration
+      openid_redirect_uri:
+        type: str
+        description: OAuth redirect URI
+        default: https://openwebui.example.com/oauth/oidc/callback
+      oauth_scopes:
+        type: str
+        description: OAuth scopes (space-separated)
+        default: openid email profile
+      oauth_merge_accounts:
+        type: bool
+        description: Merge OAuth accounts by email
+        default: false

+ 6 - 6
library/compose/passbolt/compose.yaml.j2

@@ -3,8 +3,8 @@ volumes:
   passbolt-data-gpg:
   passbolt-data-jwt:
 services:
-  passbolt-db:
-    container_name: passbolt-db
+  {{ service_name }}-db:
+    container_name: {{ container_name }}-db
     image: docker.io/library/mariadb:11.3
     environment:
       - MYSQL_RANDOM_ROOT_PASSWORD=true
@@ -14,14 +14,14 @@ services:
     volumes:
       - passbolt-db:/var/lib/mysql
     restart: unless-stopped
-  passbolt:
-    container_name: passbolt-app
+  {{ service_name }}:
+    container_name: {{ container_name }}
     image: docker.io/passbolt/passbolt:5.6.1-1-ce
     depends_on:
-      - passbolt-db
+      - {{ service_name }}-db
     environment:
       - APP_FULL_BASE_URL=https://passbolt.domain.tld
-      - DATASOURCES_DEFAULT_HOST=passbolt-db
+      - DATASOURCES_DEFAULT_HOST={{ service_name }}-db
       - DATASOURCES_DEFAULT_USERNAME=$PASSBOLT_DB_USER
       - DATASOURCES_DEFAULT_PASSWORD=$PASSBOLT_DB_PASS
       - DATASOURCES_DEFAULT_DATABASE=$PASSBOLT_DB_NAME

+ 6 - 3
library/compose/passbolt/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Passbolt
   description: >
@@ -15,12 +16,14 @@ metadata:
   version: 11.3
   author: Christian Lempa
   date: '2025-09-28'
-  tags:
-    - password-manager
-  draft: true
+  tags: []
 spec:
   general:
     vars:
+      service_name:
+        default: passbolt
+      container_name:
+        default: passbolt
       volumes_version:
         type: str
         description: Volumes version

+ 4 - 20
library/compose/pihole/.env.pihole.j2

@@ -1,23 +1,7 @@
-# Pi-hole Configuration
-# Contains application configuration
-
-# Timezone
-TZ={{ container_timezone }}
-
-# User and Group IDs
-PIHOLE_UID={{ user_uid }}
-PIHOLE_GID={{ user_gid }}
+# Pi-hole Secrets
+# Contains sensitive credentials
 
+{% if not swarm_enabled %}
 # Web Interface Admin Password
-{% if swarm_enabled %}
-# In swarm mode, password is loaded from Docker secret (use secret name, not path)
-WEBPASSWORD_FILE={{ webpassword_secret_name }}
-{% else %}
-# In compose mode, password is stored directly
-FTLCONF_webserver_api_password={{ webpassword }}
-{% endif %}
-
-# DNS Listening Mode
-{% if network_mode == 'bridge' %}
-FTLCONF_dns_listeningMode=all
+WEBPASSWORD={{ webpassword }}
 {% endif %}

+ 1 - 1
library/compose/pihole/.env.secret.j2

@@ -1 +1 @@
-{{ webpassword }}
+{% if swarm_enabled %}{{ webpassword }}{% endif %}

+ 24 - 14
library/compose/pihole/compose.yaml.j2

@@ -4,8 +4,18 @@ services:
     container_name: {{ container_name }}
     {% endif %}
     image: docker.io/pihole/pihole:2025.11.0
-    env_file:
-      - .env.pihole
+    environment:
+      - TZ={{ container_timezone }}
+      - PIHOLE_UID={{ user_uid }}
+      - PIHOLE_GID={{ user_gid }}
+      {% if swarm_enabled %}
+      - WEBPASSWORD_FILE={{ webpassword_secret_name }}
+      {% else %}
+      - FTLCONF_webserver_api_password=${WEBPASSWORD}
+      {% endif %}
+      {% if network_mode == 'bridge' %}
+      - FTLCONF_dns_listeningMode=all
+      {% endif %}
     {% if network_mode == 'host' %}
     network_mode: host
     {% else %}
@@ -61,13 +71,13 @@ services:
       - config_dnsmasq:/etc/dnsmasq.d
       - config_pihole:/etc/pihole
       {% else %}
-      {% if swarm_volume_mode == 'mount' %}
-      - {{ swarm_volume_mount_path }}/dnsmasq:/etc/dnsmasq.d:rw
-      - {{ swarm_volume_mount_path }}/pihole:/etc/pihole:rw
-      {% elif swarm_volume_mode == 'local' %}
+      {% if volume_mode == 'mount' %}
+      - {{ volume_mount_path }}/dnsmasq:/etc/dnsmasq.d:rw
+      - {{ volume_mount_path }}/pihole:/etc/pihole:rw
+      {% elif volume_mode == 'local' %}
       - config_dnsmasq:/etc/dnsmasq.d
       - config_pihole:/etc/pihole
-      {% elif swarm_volume_mode == 'nfs' %}
+      {% elif volume_mode == 'nfs' %}
       - config_dnsmasq:/etc/dnsmasq.d
       - config_pihole:/etc/pihole
       {% endif %}
@@ -121,23 +131,23 @@ services:
     {% endif %}
 
 {% if swarm_enabled %}
-{% if swarm_volume_mode in ['local', 'nfs'] %}
+{% if volume_mode in ['local', 'nfs'] %}
 volumes:
   config_dnsmasq:
-    {% if swarm_volume_mode == 'nfs' %}
+    {% if volume_mode == 'nfs' %}
     driver: local
     driver_opts:
       type: nfs
-      o: addr={{ swarm_volume_nfs_server }},{{ swarm_volume_nfs_options }}
-      device: ":{{ swarm_volume_nfs_path }}/dnsmasq"
+      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}/dnsmasq"
     {% endif %}
   config_pihole:
-    {% if swarm_volume_mode == 'nfs' %}
+    {% if volume_mode == 'nfs' %}
     driver: local
     driver_opts:
       type: nfs
-      o: addr={{ swarm_volume_nfs_server }},{{ swarm_volume_nfs_options }}
-      device: ":{{ swarm_volume_nfs_path }}/pihole"
+      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}/pihole"
     {% endif %}
 {% endif %}
 

+ 1 - 1
library/compose/pihole/template.yaml

@@ -1,6 +1,6 @@
 ---
 kind: compose
-schema: "1.1"
+schema: "1.2"
 metadata:
   name: Pihole
   description: |

+ 80 - 19
library/compose/portainer/compose.yaml.j2

@@ -1,57 +1,118 @@
 services:
   {{ service_name }}:
-    container_name: {{ container_name }}
     image: docker.io/portainer/portainer-ce:2.35.0-alpine
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
+    container_name: {{ container_name }}
+    {% endif %}
     environment:
       - TZ={{ container_timezone }}
-    {% if ports_enabled %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
     ports:
+      {% if swarm_enabled %}
+      - target: 9000
+        published: {{ ports_http }}
+        protocol: tcp
+        mode: host
+      - target: 9443
+        published: {{ ports_https }}
+        protocol: tcp
+        mode: host
+      - target: 8000
+        published: {{ ports_edge }}
+        protocol: tcp
+        mode: host
+      {% else %}
       - "{{ ports_http }}:9000"
       - "{{ ports_https }}:9443"
       - "{{ ports_edge }}:8000"
+      {% endif %}
     {% endif %}
     volumes:
       - /run/docker.sock:/var/run/docker.sock
       - portainer-data:/data
-    {% if network_enabled or traefik_enabled %}
-    networks:
-      {% if network_enabled %}
-      - {{ network_name }}
-      {% endif %}
-      {% if traefik_enabled %}
-      - {{ traefik_network }}
-      {% endif %}
-    {% endif %}
-    {% if traefik_enabled %}
+    {% if traefik_enabled and not swarm_enabled %}
     labels:
       - traefik.enable=true
       - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}.loadbalancer.server.port=9000
-      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}
+      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=9000
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
       {% if traefik_tls_enabled %}
-      - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}
+      - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
       - traefik.http.routers.{{ service_name }}-https.tls=true
       - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
     {% endif %}
-    restart: {{ restart_policy }}
+    {% if swarm_enabled %}
+    deploy:
+      mode: {{ swarm_placement_mode }}
+      {% if swarm_placement_mode == 'replicated' %}
+      replicas: {{ swarm_replicas }}
+      {% endif %}
+      restart_policy:
+        condition: on-failure
+      {% if traefik_enabled %}
+      labels:
+        - traefik.enable=true
+        - traefik.docker.network={{ traefik_network }}
+        - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=9000
+        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+        {% if traefik_tls_enabled %}
+        - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-https.tls=true
+        - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+        {% endif %}
+      {% endif %}
+    {% endif %}
 
 volumes:
   portainer-data:
     driver: local
 
-{% if network_enabled or traefik_enabled %}
+{% if network_mode != 'host' %}
 networks:
-  {% if network_enabled %}
   {{ network_name }}:
     {% if network_external %}
     external: true
+    {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    name: {{ network_name }}
+    {% elif swarm_enabled %}
+    driver: overlay
+    attachable: true
+    {% else %}
+    driver: bridge
+    {% endif %}
     {% endif %}
-  {% endif %}
   {% if traefik_enabled %}
   {{ traefik_network }}:
     external: true

+ 1 - 0
library/compose/portainer/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Portainer
   description: >

+ 53 - 26
library/compose/postgres/compose.yaml.j2

@@ -1,41 +1,61 @@
 services:
-  {{ service_name | default('postgres') }}:
+  {{ service_name }}:
     image: docker.io/library/postgres:17.6
-    container_name: {{ container_name | default('postgres') }}
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
+    container_name: {{ container_name }}
+    {% endif %}
     environment:
-      - POSTGRES_INITDB_ARGS={{ postgres_initdb_args | default('--data-checksums') }}
+      - POSTGRES_INITDB_ARGS={{ postgres_initdb_args }}
       {% if postgres_host_auth_method %}
       - POSTGRES_HOST_AUTH_METHOD={{ postgres_host_auth_method }}
       {% endif %}
-      - POSTGRES_USER={{ database_user | default('postgres') }}
+      - POSTGRES_USER={{ database_user }}
       {% if postgres_secrets_enabled %}
       - POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password
       {% else %}
-      - POSTGRES_PASSWORD={{ database_password | default('postgres') }}
+      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
+      {% endif %}
+      - POSTGRES_DB={{ database_name }}
+      - TZ={{ container_timezone }}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
       {% endif %}
-      - POSTGRES_DB={{ database_name | default('postgres') }}
-      - TZ={{ container_timezone | default('UTC') }}
-    {% if ports_enabled %}
+    {% endif %}
+    {% if network_mode == 'bridge' %}
     ports:
-      - "{{ database_port | default(5432) }}:5432"
+      {% if swarm_enabled %}
+      - target: 5432
+        published: {{ database_port }}
+        protocol: tcp
+        mode: host
+      {% else %}
+      - "{{ database_port }}:5432"
+      {% endif %}
+    {% endif %}
+    volumes:
+      - postgres_data:/var/lib/postgresql/data
+    {% if postgres_secrets_enabled %}
+    secrets:
+      - postgres_password
     {% endif %}
     healthcheck:
-      test: ["CMD-SHELL", "pg_isready -U {{ database_user | default('postgres') }}"]
+      test: ["CMD-SHELL", "pg_isready -U {{ database_user }}"]
       start_period: 30s
       interval: 10s
       timeout: 10s
       retries: 5
-    {% if network_enabled %}
-    networks:
-      - {{ network_name | default('bridge') }}
-    {% endif %}
-    {% if postgres_secrets_enabled %}
-    secrets:
-      - postgres_password
+    {% if swarm_enabled %}
+    deploy:
+      replicas: 1
     {% endif %}
-    volumes:
-      - postgres_data:/var/lib/postgresql/data
-    restart: {{ restart_policy | default('unless-stopped') }}
 
 {% if postgres_secrets_enabled %}
 secrets:
@@ -47,10 +67,17 @@ volumes:
   postgres_data:
     driver: local
 
-{% if network_enabled %}
 networks:
-  {{ network_name | default('bridge') }}:
-    {% if network_external %}
-    external: true
-    {% endif %}
-{% endif %}
+  {% if network_mode == 'macvlan' %}
+  {{ network_name }}:
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+  {% elif network_mode == 'bridge' %}
+  {{ network_name }}:
+    driver: {% if swarm_enabled %}overlay{% else %}bridge{% endif %}
+  {% endif %}

+ 2 - 4
library/compose/postgres/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: PostgreSQL
   description: >
@@ -15,10 +16,7 @@ metadata:
   version: 17.6
   author: Christian Lempa
   date: '2025-09-28'
-  tags:
-    - database
-    - sql
-  draft: true
+  tags: []
 spec:
   general:
     vars:

+ 4 - 2
library/compose/prometheus/compose.yaml.j2

@@ -49,7 +49,8 @@ services:
       {% if traefik_enabled %}
       labels:
         - traefik.enable=true
-        - traefik.http.services.{{ service_name }}.loadbalancer.server.port=9090
+        - traefik.http.services.{{ service_name }}-web.loadbalancer.server.port=9090
+        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
         - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
         - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
         {% if traefik_tls_enabled %}
@@ -63,7 +64,8 @@ services:
     {% if traefik_enabled %}
     labels:
       - traefik.enable=true
-      - traefik.http.services.{{ service_name }}.loadbalancer.server.port=9090
+      - traefik.http.services.{{ service_name }}-web.loadbalancer.server.port=9090
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
       {% if traefik_tls_enabled %}

+ 1 - 1
library/compose/prometheus/template.yaml

@@ -1,6 +1,6 @@
 ---
 kind: compose
-schema: "1.1"
+schema: "1.2"
 metadata:
   name: Prometheus
   description: |

+ 0 - 26
library/compose/semaphoreui/.env.database.j2

@@ -1,26 +0,0 @@
-{% if database_type == 'mysql' -%}
-# MySQL Database Configuration
-# Used when database_type=mysql and database_external=false
-
-# Database Settings
-MYSQL_RANDOM_ROOT_PASSWORD=yes
-MYSQL_DATABASE={{ database_name }}
-MYSQL_USER={{ database_user }}
-MYSQL_PASSWORD={{ database_password }}
-
-# Character Set
-MYSQL_CHARSET=utf8mb4
-MYSQL_COLLATION=utf8mb4_unicode_ci
-
-{% elif database_type == 'postgres' -%}
-# PostgreSQL Database Configuration
-# Used when database_type=postgres and database_external=false
-
-# Database Settings
-POSTGRES_DB={{ database_name }}
-POSTGRES_USER={{ database_user }}
-POSTGRES_PASSWORD={{ database_password }}
-
-# PostgreSQL Configuration
-POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C
-{% endif -%}

+ 16 - 0
library/compose/semaphoreui/.env.j2

@@ -0,0 +1,16 @@
+# Semaphore Secrets
+# Contains sensitive credentials
+
+# Database Password
+DATABASE_PASSWORD={{ database_password }}
+
+# Admin Password
+SEMAPHORE_ADMIN_PASSWORD={{ semaphore_admin_password }}
+
+# Access Key Encryption
+SEMAPHORE_ACCESS_KEY_ENCRYPTION={{ semaphore_access_key_encryption }}
+
+{% if email_enabled -%}
+# Email Password
+EMAIL_PASSWORD={{ email_password }}
+{% endif %}

+ 0 - 47
library/compose/semaphoreui/.env.semaphore.j2

@@ -1,47 +0,0 @@
-# Ansible Semaphore Application Configuration
-# Contains application settings and database connection
-
-# Timezone
-TZ={{ container_timezone }}
-
-# Database Connection
-{% if database_type == 'mysql' %}
-SEMAPHORE_DB_DIALECT=mysql
-{% elif database_type == 'postgres' %}
-SEMAPHORE_DB_DIALECT=postgres
-{% endif %}
-{% if database_external %}
-SEMAPHORE_DB_HOST={{ database_host }}
-{% else %}
-SEMAPHORE_DB_HOST={{ service_name }}-{{ database_type }}
-{% endif %}
-SEMAPHORE_DB_PORT={% if database_type == 'postgres' %}5432{% else %}3306{% endif %}
-
-SEMAPHORE_DB={{ database_name }}
-SEMAPHORE_DB_USER={{ database_user }}
-SEMAPHORE_DB_PASS={{ database_password }}
-
-# Admin Configuration
-SEMAPHORE_ADMIN={{ semaphore_admin_name }}
-SEMAPHORE_ADMIN_NAME={{ semaphore_admin_name }}
-SEMAPHORE_ADMIN_EMAIL={{ semaphore_admin_email }}
-SEMAPHORE_ADMIN_PASSWORD={{ semaphore_admin_password }}
-
-# Playbook Configuration
-SEMAPHORE_PLAYBOOK_PATH={{ semaphore_playbook_path }}
-
-# Access Key Encryption
-SEMAPHORE_ACCESS_KEY_ENCRYPTION={{ semaphore_access_key_encryption }}
-
-# Ansible Settings
-ANSIBLE_HOST_KEY_CHECKING={{ ansible_host_key_checking }}
-
-{% if email_enabled -%}
-# Email Server Configuration
-SEMAPHORE_EMAIL_SENDER={{ email_from }}
-SEMAPHORE_EMAIL_HOST={{ email_host }}
-SEMAPHORE_EMAIL_PORT={{ email_port }}
-SEMAPHORE_EMAIL_USERNAME={{ email_username }}
-SEMAPHORE_EMAIL_PASSWORD={{ email_password }}
-SEMAPHORE_EMAIL_SECURE={{ email_use_tls }}
-{% endif %}

+ 161 - 41
library/compose/semaphoreui/compose.yaml.j2

@@ -1,111 +1,231 @@
 services:
   {{ service_name }}:
     image: docker.io/semaphoreui/semaphore:v2.16.43
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
     container_name: {{ container_name }}
-    user: "{{ user_uid }}:{{ user_gid }}"
-    env_file:
-      - .env.semaphore
-    {% if ports_enabled %}
-    ports:
-      - "{{ ports_http }}:3000"
     {% endif %}
-    {% if network_enabled or traefik_enabled %}
-    networks:
-      {% if network_enabled %}
-      - {{ network_name }}
+    user: "{{ user_uid }}:{{ user_gid }}"
+    environment:
+      - TZ={{ container_timezone }}
+      {% if database_type == 'mysql' %}
+      - SEMAPHORE_DB_DIALECT=mysql
+      {% elif database_type == 'postgres' %}
+      - SEMAPHORE_DB_DIALECT=postgres
       {% endif %}
+      {% if database_external %}
+      - SEMAPHORE_DB_HOST={{ database_host }}
+      {% else %}
+      - SEMAPHORE_DB_HOST={{ service_name }}-{{ database_type }}
+      {% endif %}
+      - SEMAPHORE_DB_PORT={% if database_type == 'postgres' %}5432{% else %}3306{% endif %}
+      - SEMAPHORE_DB={{ database_name }}
+      - SEMAPHORE_DB_USER={{ database_user }}
+      - SEMAPHORE_DB_PASS=${DATABASE_PASSWORD}
+      - SEMAPHORE_ADMIN={{ semaphore_admin_name }}
+      - SEMAPHORE_ADMIN_NAME={{ semaphore_admin_name }}
+      - SEMAPHORE_ADMIN_EMAIL={{ semaphore_admin_email }}
+      - SEMAPHORE_ADMIN_PASSWORD=${SEMAPHORE_ADMIN_PASSWORD}
+      - SEMAPHORE_PLAYBOOK_PATH={{ semaphore_playbook_path }}
+      - SEMAPHORE_ACCESS_KEY_ENCRYPTION=${SEMAPHORE_ACCESS_KEY_ENCRYPTION}
+      - ANSIBLE_HOST_KEY_CHECKING={{ ansible_host_key_checking }}
+      {% if email_enabled %}
+      - SEMAPHORE_EMAIL_SENDER={{ email_from }}
+      - SEMAPHORE_EMAIL_HOST={{ email_host }}
+      - SEMAPHORE_EMAIL_PORT={{ email_port }}
+      - SEMAPHORE_EMAIL_USERNAME={{ email_username }}
+      - SEMAPHORE_EMAIL_PASSWORD=${EMAIL_PASSWORD}
+      - SEMAPHORE_EMAIL_SECURE={{ email_use_tls }}
+      {% endif %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
+    networks:
       {% if traefik_enabled %}
-      - {{ traefik_network }}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if not traefik_enabled and network_mode == 'bridge' %}
+    ports:
+      {% if swarm_enabled %}
+      - target: 3000
+        published: {{ ports_http }}
+        protocol: tcp
+        mode: host
+      {% else %}
+      - "{{ ports_http }}:3000"
       {% endif %}
     {% endif %}
-    {% if traefik_enabled %}
+    volumes:
+      - ./inventory:/inventory:ro
+      - ./authorized-keys:/authorized-keys:ro
+      - ./config:/etc/semaphore:rw
+    {% if traefik_enabled and not swarm_enabled %}
     labels:
       - traefik.enable=true
       - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}.loadbalancer.server.port=3000
+      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=3000
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
       {% if traefik_tls_enabled %}
+      - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
       - traefik.http.routers.{{ service_name }}-https.tls=true
       - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
     {% endif %}
-    volumes:
-      - ./inventory:/inventory:ro
-      - ./authorized-keys:/authorized-keys:ro
-      - ./config:/etc/semaphore:rw
     depends_on:
       {% if database_type == 'mysql' %}
       - {{ service_name }}-mysql
       {% elif database_type == 'postgres' %}
       - {{ service_name }}-postgres
       {% endif %}
-    restart: {{ restart_policy }}
+    {% if swarm_enabled %}
+    deploy:
+      mode: {{ swarm_placement_mode }}
+      {% if swarm_placement_mode == 'replicated' %}
+      replicas: {{ swarm_replicas }}
+      {% endif %}
+      restart_policy:
+        condition: on-failure
+      {% if traefik_enabled %}
+      labels:
+        - traefik.enable=true
+        - traefik.docker.network={{ traefik_network }}
+        - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=3000
+        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+        {% if traefik_tls_enabled %}
+        - traefik.http.routers.{{ service_name }}-https.service={{ service_name }}-web
+        - traefik.http.routers.{{ service_name }}-https.rule=Host(`{{ traefik_host }}`)
+        - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}-https.tls=true
+        - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
+        {% endif %}
+      {% endif %}
+    {% endif %}
 
   {% if not database_external %}
   {% if database_type == 'mysql' %}
   {{ service_name }}-mysql:
     image: docker.io/library/mysql:8.4
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
     container_name: {{ service_name }}-mysql
-    env_file:
-      - .env.database
+    {% endif %}
+    environment:
+      {% if database_type == 'mysql' %}
+      - MYSQL_RANDOM_ROOT_PASSWORD=yes
+      - MYSQL_DATABASE={{ database_name }}
+      - MYSQL_USER={{ database_user }}
+      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
+      - MYSQL_CHARSET=utf8mb4
+      - MYSQL_COLLATION=utf8mb4_unicode_ci
+      {% elif database_type == 'postgres' %}
+      - POSTGRES_DB={{ database_name }}
+      - POSTGRES_USER={{ database_user }}
+      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
+      - POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C
+      {% endif %}
     healthcheck:
       test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "{{ database_user }}", "-p{{ database_password }}"]
       start_period: 30s
       interval: 10s
       timeout: 10s
       retries: 5
-    volumes:
-      - database_data:/var/lib/mysql
-    {% if network_enabled or traefik_enabled %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
     networks:
-      {% if network_enabled %}
-      - {{ network_name }}
-      {% endif %}
       {% if traefik_enabled %}
-      - {{ traefik_network }}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
       {% endif %}
     {% endif %}
-    restart: {{ restart_policy }}
+    volumes:
+      - database_data:/var/lib/mysql
   {% elif database_type == 'postgres' %}
   {{ service_name }}-postgres:
     image: docker.io/library/postgres:17.6
+    {% if not swarm_enabled %}
+    restart: {{ restart_policy }}
     container_name: {{ service_name }}-postgres
-    env_file:
-      - .env.database
+    {% endif %}
+    environment:
+      {% if database_type == 'mysql' %}
+      - MYSQL_RANDOM_ROOT_PASSWORD=yes
+      - MYSQL_DATABASE={{ database_name }}
+      - MYSQL_USER={{ database_user }}
+      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
+      - MYSQL_CHARSET=utf8mb4
+      - MYSQL_COLLATION=utf8mb4_unicode_ci
+      {% elif database_type == 'postgres' %}
+      - POSTGRES_DB={{ database_name }}
+      - POSTGRES_USER={{ database_user }}
+      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
+      - POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C
+      {% endif %}
     healthcheck:
       test: ["CMD-SHELL", "pg_isready -U {{ database_user }}"]
       start_period: 30s
       interval: 10s
       timeout: 10s
       retries: 5
-    volumes:
-      - database_data:/var/lib/postgresql/data
-    {% if network_enabled or traefik_enabled %}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% else %}
     networks:
-      {% if network_enabled %}
-      - {{ network_name }}
-      {% endif %}
       {% if traefik_enabled %}
-      - {{ traefik_network }}
+      {{ traefik_network }}:
+      {% endif %}
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
       {% endif %}
     {% endif %}
-    restart: {{ restart_policy }}
+    volumes:
+      - database_data:/var/lib/postgresql/data
   {% endif %}
   {% endif %}
 
-{% if network_enabled or traefik_enabled %}
+{% if network_mode != 'host' %}
 networks:
-  {% if network_enabled %}
   {{ network_name }}:
     {% if network_external %}
     external: true
     {% else %}
+    {% if network_mode == 'macvlan' %}
+    driver: macvlan
+    driver_opts:
+      parent: {{ network_macvlan_parent_interface }}
+    ipam:
+      config:
+        - subnet: {{ network_macvlan_subnet }}
+          gateway: {{ network_macvlan_gateway }}
+    name: {{ network_name }}
+    {% elif swarm_enabled %}
+    driver: overlay
+    attachable: true
+    {% else %}
     driver: bridge
     {% endif %}
-  {% endif %}
+    {% endif %}
   {% if traefik_enabled %}
   {{ traefik_network }}:
     external: true

+ 2 - 1
library/compose/semaphoreui/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Semaphore UI
   description: >
@@ -26,7 +27,7 @@ metadata:
     2. Access the web interface:
        {% if traefik_enabled -%}
        - Via Traefik: https://{{ traefik_host }}
-       {% if ports_enabled %}- Direct access: http://localhost:{{ ports_http }}{% endif %}
+       {% if not traefik_enabled and network_mode == 'bridge' %}- Direct access: http://localhost:{{ ports_http }}{% endif %}
        {%- else -%}
        - Open http://localhost:{{ ports_http }} in your browser
        {%- endif %}

+ 8 - 12
library/compose/traefik/compose.yaml.j2

@@ -19,18 +19,14 @@ services:
       - ./config/:/etc/traefik/:ro
       - ./certs/:/var/traefik/certs/:rw
       {% else %}
-      {% if swarm_volume_mode == 'mount' %}
-      - {{ swarm_volume_mount_path }}:/var/traefik/certs/:rw
-      {% elif swarm_volume_mode == 'local' %}
+      {% if volume_mode == 'mount' %}
+      - {{ volume_mount_path }}:/var/traefik/certs/:rw
+      {% elif volume_mode == 'local' %}
       - traefik_certs:/var/traefik/certs/:rw
-      {% elif swarm_volume_mode == 'nfs' %}
+      {% elif volume_mode == 'nfs' %}
       - traefik_certs:/var/traefik/certs/:rw
       {% endif %}
       {% endif %}
-    {% if not swarm_enabled %}
-    env_file:
-      - ./.env
-    {% endif %}
     environment:
       - TZ={{ container_timezone }}
       {% if traefik_tls_enabled %}
@@ -81,15 +77,15 @@ services:
     {% endif %}
 
 {% if swarm_enabled %}
-{% if swarm_volume_mode in ['local', 'nfs'] %}
+{% if volume_mode in ['local', 'nfs'] %}
 volumes:
   traefik_certs:
-    {% if swarm_volume_mode == 'nfs' %}
+    {% if volume_mode == 'nfs' %}
     driver: local
     driver_opts:
       type: nfs
-      o: addr={{ swarm_volume_nfs_server }},nfsvers=4,{{ swarm_volume_nfs_options }}
-      device: ":{{ swarm_volume_nfs_path }}"
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}"
     {% endif %}
 {% endif %}
 

+ 1 - 1
library/compose/traefik/template.yaml

@@ -1,6 +1,6 @@
 ---
 kind: compose
-schema: "1.1"
+schema: "1.2"
 metadata:
   name: Traefik
   description: >

+ 2 - 4
library/compose/twingate-connector/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Twingate_Connector
   description: >
@@ -17,10 +18,7 @@ metadata:
   version: 1.79.0
   author: Christian Lempa
   date: '2025-10-31'
-  tags:
-    - zero-trust
-    - remote-access
-  draft: true
+  tags: []
 spec:
   general:
     vars:

+ 2 - 2
library/compose/uptimekuma/compose.yaml.j2

@@ -2,9 +2,9 @@ volumes:
   uptimekuma-data:
     driver: local
 services:
-  uptimekuma:
+  {{ service_name }}:
     image: docker.io/louislam/uptime-kuma:1.23.17
-    container_name: uptimekuma
+    container_name: {{ container_name }}
     ports:
       - 3001:3001
     volumes:

+ 6 - 4
library/compose/uptimekuma/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Uptimekuma
   description: >
@@ -15,13 +16,14 @@ metadata:
   version: 1.23.17
   author: Christian Lempa
   date: '2025-10-31'
-  tags:
-    - monitoring
-    - alerting
-  draft: true
+  tags: []
 spec:
   general:
     vars:
+      service_name:
+        default: uptimekuma
+      container_name:
+        default: uptimekuma
       volumes_version:
         type: str
         description: Volumes version

+ 3 - 3
library/compose/wazuh/compose.yaml.j2

@@ -1,7 +1,7 @@
 services:
   wazuh.manager:
     image: docker.io/wazuh/wazuh-manager:4.14.0
-    container_name: wazuh-prod-1-manager
+    container_name: {{ container_name }}-manager
     hostname: wazuh.manager
     ulimits:
       memlock:
@@ -55,7 +55,7 @@ services:
 
   wazuh.indexer:
     image: docker.io/wazuh/wazuh-indexer:4.14.0
-    container_name: wazuh-prod-1-indexer
+    container_name: {{ container_name }}-indexer
     hostname: wazuh.indexer
     ports:
       - "9200:9200"
@@ -88,7 +88,7 @@ services:
 
   wazuh.dashboard:
     image: docker.io/wazuh/wazuh-dashboard:4.14.0
-    container_name: wazuh-prod-1-dashboard
+    container_name: {{ container_name }}-dashboard
     hostname: wazuh.dashboard
     # --> (Optional) Remove the port mapping when using traefik
     ports:

+ 6 - 6
library/compose/wazuh/template.yaml

@@ -1,5 +1,6 @@
 ---
 kind: compose
+schema: "1.2"
 metadata:
   name: Wazuh
   description: >
@@ -16,15 +17,14 @@ metadata:
   version: 4.14.0
   author: Christian Lempa
   date: '2025-11-05'
-  tags:
-    - security
-    - monitoring
-    - xdr
-    - siem
-  draft: true
+  tags: []
 spec:
   general:
     vars:
+      service_name:
+        default: wazuh
+      container_name:
+        default: wazuh-prod-1
       wazuh.manager_version:
         type: str
         description: Wazuh.Manager version

+ 4 - 2
library/compose/whoami/compose.yaml.j2

@@ -22,7 +22,8 @@ services:
       labels:
         - traefik.enable=true
         - traefik.docker.network={{ traefik_network }}
-        - traefik.http.services.{{ service_name }}.loadbalancer.server.port=80
+        - traefik.http.services.{{ service_name }}-web.loadbalancer.server.port=80
+        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
         - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
         - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
         {% if traefik_tls_enabled %}
@@ -35,7 +36,8 @@ services:
     labels:
       - traefik.enable=true
       - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}.loadbalancer.server.port=80
+      - traefik.http.services.{{ service_name }}-web.loadbalancer.server.port=80
+      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
       - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}`)
       - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
       {% if traefik_tls_enabled %}

+ 1 - 1
library/compose/whoami/template.yaml

@@ -1,6 +1,6 @@
 ---
 kind: compose
-schema: "1.1"
+schema: "1.2"
 metadata:
   name: Whoami
   description: |