xcad пре 5 месеци
родитељ
комит
8e65a560fa
84 измењених фајлова са 1432 додато и 4702 уклоњено
  1. 10 2
      cli/core/schema/compose/v1.2.json
  2. 34 14
      cli/core/template/template.py
  3. 9 1
      cli/core/template/variable.py
  4. 0 39
      library/compose/adguardhome/common/networks.yaml.j2
  5. 0 28
      library/compose/adguardhome/common/volumes.yaml.j2
  6. 135 12
      library/compose/adguardhome/compose.yaml.j2
  7. 0 157
      library/compose/adguardhome/services/adguardhome.yaml.j2
  8. 11 45
      library/compose/adguardhome/template.yaml
  9. 12 117
      library/compose/alloy/compose.yaml.j2
  10. 17 21
      library/compose/alloy/template.yaml
  11. 4 9
      library/compose/authentik/.env.j2
  12. 0 9
      library/compose/authentik/common/networks.yaml.j2
  13. 0 55
      library/compose/authentik/common/volumes.yaml.j2
  14. 204 18
      library/compose/authentik/compose.yaml.j2
  15. 0 326
      library/compose/authentik/compose.yaml.j2.backup
  16. 0 28
      library/compose/authentik/services/postgres.yaml.j2
  17. 0 24
      library/compose/authentik/services/redis.yaml.j2
  18. 0 124
      library/compose/authentik/services/server.yaml.j2
  19. 0 48
      library/compose/authentik/services/worker.yaml.j2
  20. 28 59
      library/compose/authentik/template.yaml
  21. 49 63
      library/compose/bind9/compose.yaml.j2
  22. 2 2
      library/compose/bind9/config/named.conf.j2
  23. 9 27
      library/compose/bind9/config/named.conf.zones.j2
  24. 3 10
      library/compose/bind9/config/tsig.key.j2
  25. 20 47
      library/compose/bind9/template.yaml
  26. 1 0
      library/compose/checkmk/.env.j2
  27. 10 49
      library/compose/checkmk/compose.yaml.j2
  28. 9 23
      library/compose/checkmk/template.yaml
  29. 9 41
      library/compose/dockge/compose.yaml.j2
  30. 9 22
      library/compose/dockge/template.yaml
  31. 0 4
      library/compose/gitea/.env.j2
  32. 32 129
      library/compose/gitea/compose.yaml.j2
  33. 12 31
      library/compose/gitea/template.yaml
  34. 0 4
      library/compose/grafana/.env.j2
  35. 19 171
      library/compose/grafana/compose.yaml.j2
  36. 7 18
      library/compose/grafana/template.yaml
  37. 0 26
      library/compose/heimdall/compose.yaml.j2
  38. 0 28
      library/compose/heimdall/template.yaml
  39. 1 0
      library/compose/homeassistant/template.yaml
  40. 1 0
      library/compose/homepage/template.yaml
  41. 1 0
      library/compose/homer/template.yaml
  42. 8 99
      library/compose/influxdb/compose.yaml.j2
  43. 14 28
      library/compose/influxdb/template.yaml
  44. 1 0
      library/compose/komodo/template.yaml
  45. 18 144
      library/compose/loki/compose.yaml.j2
  46. 0 154
      library/compose/loki/compose.yaml.j2.final
  47. 0 148
      library/compose/loki/compose.yaml.j2.portfix
  48. 1 3
      library/compose/loki/config/config.yaml
  49. 11 22
      library/compose/loki/template.yaml
  50. 0 46
      library/compose/loki/template.yaml.backup
  51. 1 0
      library/compose/mariadb/.env.j2
  52. 11 143
      library/compose/mariadb/compose.yaml.j2
  53. 16 56
      library/compose/mariadb/template.yaml
  54. 0 116
      library/compose/n8n-worker/compose.yaml.j2
  55. 0 143
      library/compose/n8n-worker/template.yaml
  56. 296 0
      library/compose/n8n/compose.yaml.j2
  57. 0 305
      library/compose/n8n/compose.yaml.j2.bak3
  58. 0 302
      library/compose/n8n/compose.yaml.j2.final
  59. 6 26
      library/compose/n8n/template.yaml
  60. 0 1
      library/compose/netbox/.env.secret.database_password.j2
  61. 0 1
      library/compose/netbox/.env.secret.email_password.j2
  62. 0 1
      library/compose/netbox/.env.secret.netbox_secret_key.j2
  63. 0 1
      library/compose/netbox/.env.secret.redis_password.j2
  64. 0 18
      library/compose/netbox/common/networks.yaml.j2
  65. 0 15
      library/compose/netbox/common/secrets.yaml.j2
  66. 0 64
      library/compose/netbox/common/volumes.yaml.j2
  67. 202 23
      library/compose/netbox/compose.yaml.j2
  68. 0 205
      library/compose/netbox/services/netbox.yaml.j2
  69. 0 28
      library/compose/netbox/services/postgres.yaml.j2
  70. 0 88
      library/compose/netbox/services/redis.yaml.j2
  71. 0 101
      library/compose/netbox/services/worker.yaml.j2
  72. 0 4
      library/compose/netbox/template.yaml
  73. 1 0
      library/compose/nextcloud/template.yaml
  74. 0 148
      library/compose/nginxproxymanager/compose.yaml.j2
  75. 0 188
      library/compose/nginxproxymanager/compose.yaml.j2.bak3
  76. 0 51
      library/compose/nginxproxymanager/template.yaml
  77. 1 0
      library/compose/pangolin/template.yaml
  78. 0 11
      library/compose/traefik/common/configs.yaml.j2
  79. 0 17
      library/compose/traefik/common/networks.yaml.j2
  80. 0 12
      library/compose/traefik/common/secrets.yaml.j2
  81. 0 17
      library/compose/traefik/common/volumes.yaml.j2
  82. 185 6
      library/compose/traefik/compose.yaml.j2
  83. 0 132
      library/compose/traefik/services/traefik.yaml.j2
  84. 2 4
      library/compose/traefik/template.yaml

+ 10 - 2
cli/core/schema/compose/v1.2.json

@@ -161,6 +161,13 @@
         "type": "int",
         "default": 25,
         "required": true
+      },
+      {
+        "name": "ports_snmp",
+        "description": "SNMP trap port",
+        "type": "int",
+        "default": 162,
+        "required": true
       }
     ]
   },
@@ -367,8 +374,8 @@
         "name": "database_type",
         "description": "Database type",
         "type": "enum",
-        "options": ["default", "sqlite", "postgres", "mysql"],
-        "default": "default",
+        "options": ["sqlite", "postgres", "mysql"],
+        "default": "sqlite",
         "required": true
       },
       {
@@ -376,6 +383,7 @@
         "description": "Use an external database server?",
         "extra": "skips creation of internal database container",
         "type": "bool",
+        "needs": ["database_type=postgres,mysql"],
         "default": false
       },
       {

+ 34 - 14
cli/core/template/template.py

@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+import base64
 import importlib
 import logging
 import os
@@ -621,14 +622,14 @@ class Template:
         undefined_variables = used_variables - defined_variables
         if undefined_variables:
             undefined_list = sorted(undefined_variables)
-            
+
             # Build file location info for each undefined variable
             file_locations = []
             for var_name in undefined_list:
-                if hasattr(self, '_variable_usage_map') and var_name in self._variable_usage_map:
+                if hasattr(self, "_variable_usage_map") and var_name in self._variable_usage_map:
                     files = self._variable_usage_map[var_name]
                     file_locations.append(f"  • {var_name}: {', '.join(files)}")
-            
+
             error_msg = (
                 f"Template validation error in '{self.id}': "
                 f"Variables used in template content but not defined in spec:\n"
@@ -637,14 +638,14 @@ class Template:
                 error_msg += "\n".join(file_locations) + "\n"
             else:
                 error_msg += f"{undefined_list}\n"
-            
+
             error_msg += (
-                f"\nPlease add these variables to your template's template.yaml spec. "
-                f"Each variable must have a default value.\n\n"
-                f"Example:\n"
-                f"spec:\n"
-                f"  general:\n"
-                f"    vars:\n"
+                "\nPlease add these variables to your template's template.yaml spec. "
+                "Each variable must have a default value.\n\n"
+                "Example:\n"
+                "spec:\n"
+                "  general:\n"
+                "    vars:\n"
             )
             for var_name in undefined_list:
                 error_msg += (
@@ -677,15 +678,34 @@ class Template:
         )
 
     def _generate_autogenerated_values(self, variables: VariableCollection, variable_values: dict) -> None:
-        """Generate values for autogenerated variables that are empty."""
+        """Generate values for autogenerated variables that are empty.
+
+        Supports both plain and base64-encoded autogenerated values based on
+        the autogenerated_base64 flag. Base64 encoding generates random bytes
+        and encodes them, which is more suitable for cryptographic keys.
+        """
         for section in variables.get_sections().values():
             for var_name, variable in section.variables.items():
                 if variable.autogenerated and (variable.value is None or variable.value == ""):
-                    alphabet = string.ascii_letters + string.digits
                     length = getattr(variable, "autogenerated_length", 32)
-                    generated_value = "".join(secrets.choice(alphabet) for _ in range(length))
+                    use_base64 = getattr(variable, "autogenerated_base64", False)
+
+                    if use_base64:
+                        # Generate random bytes and base64 encode
+                        # Note: length refers to number of random bytes, not base64 string length
+                        random_bytes = secrets.token_bytes(length)
+                        generated_value = base64.b64encode(random_bytes).decode("utf-8")
+                        logger.debug(
+                            f"Auto-generated base64 value for variable '{var_name}' "
+                            f"(bytes: {length}, encoded length: {len(generated_value)})"
+                        )
+                    else:
+                        # Generate alphanumeric string
+                        alphabet = string.ascii_letters + string.digits
+                        generated_value = "".join(secrets.choice(alphabet) for _ in range(length))
+                        logger.debug(f"Auto-generated value for variable '{var_name}' (length: {length})")
+
                     variable_values[var_name] = generated_value
-                    logger.debug(f"Auto-generated value for variable '{var_name}' (length: {length})")
 
     def _log_render_start(self, debug: bool, variable_values: dict) -> None:
         """Log rendering start information."""

+ 9 - 1
cli/core/template/variable.py

@@ -54,7 +54,9 @@ class Variable:
         elif "default" in data:
             self.value: Any = data.get("default")
         else:
-            self.value: Any = None
+            # RULE: If bool variables don't have any default value or value at all,
+            # automatically set them to false
+            self.value: Any = False if self.type == "bool" else None
         self.origin: str | None = data.get("origin")
         self.sensitive: bool = data.get("sensitive", False)
         # Optional extra explanation used by interactive prompts
@@ -63,6 +65,8 @@ class Variable:
         self.autogenerated: bool = data.get("autogenerated", False)
         # Length of auto-generated value
         self.autogenerated_length: int = data.get("autogenerated_length", DEFAULT_AUTOGENERATED_LENGTH)
+        # Flag indicating if autogenerated value should be base64 encoded
+        self.autogenerated_base64: bool = data.get("autogenerated_base64", False)
         # Flag indicating this variable is required (must have a value)
         self.required: bool = data.get("required", False)
         # Original value before config override (used for display)
@@ -251,6 +255,9 @@ class Variable:
             # Only include length if not default
             if self.autogenerated_length != DEFAULT_AUTOGENERATED_LENGTH:
                 result["autogenerated_length"] = self.autogenerated_length
+            # Include base64 flag if enabled
+            if self.autogenerated_base64:
+                result["autogenerated_base64"] = True
         if self.required:
             result["required"] = True
         if self.options is not None:  # Allow empty list
@@ -402,6 +409,7 @@ class Variable:
             "extra": self.extra,
             "autogenerated": self.autogenerated,
             "autogenerated_length": self.autogenerated_length,
+            "autogenerated_base64": self.autogenerated_base64,
             "required": self.required,
             "original_value": self.original_value,
             "needs": self.needs.copy() if self.needs else None,

+ 0 - 39
library/compose/adguardhome/common/networks.yaml.j2

@@ -1,39 +0,0 @@
----
-{#
-  Network definitions (only when needed):
-  - When network_mode is empty: no definition needed (uses Docker's default bridge)
-  - When network_mode is 'bridge': define custom bridge network
-  - When network_mode is 'macvlan': configure macvlan with static IP (for Compose mode)
-    Note: In Swarm mode, macvlan networks must be created manually with config-only networks on each node
-  - When swarm_enabled: use overlay network for multi-host communication
-  - Traefik network: always external (managed by Traefik)
-#}
-{% if network_mode == 'bridge' or network_mode == 'macvlan' or traefik_enabled %}
-networks:
-  {% if network_mode == 'bridge' or network_mode == 'macvlan'%}
-  {{ 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
-  {% endif %}
-{% endif %}

+ 0 - 28
library/compose/adguardhome/common/volumes.yaml.j2

@@ -1,28 +0,0 @@
----
-{#
-  Volume definitions:
-  - When volume_mode is 'local' (default): use docker-managed local volumes
-  - When volume_mode is 'nfs': configure NFS-backed volumes
-  - When volume_mode is 'mount': no volume definition needed (bind mounts used directly)
-#}
-{% if volume_mode == 'local' %}
-volumes:
-  {{ service_name }}-work:
-    driver: local
-  {{ service_name }}-conf:
-    driver: local
-{% elif volume_mode == 'nfs' %}
-volumes:
-  {{ service_name }}-work:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/work"
-  {{ service_name }}-conf:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/conf"
-{% endif %}

+ 135 - 12
library/compose/adguardhome/compose.yaml.j2

@@ -1,16 +1,139 @@
 ---
+services:
+  {{ service_name }}:
+    image: docker.io/adguard/adguardhome:v0.107.69
+    restart: {{ restart_policy }}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% elif network_mode == 'bridge' or network_mode == 'macvlan' or traefik_enabled %}
+    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 network_mode == '' or network_mode == 'bridge' or traefik_enabled %}
+    ports:
+      {% if not traefik_enabled %}
+      - "{{ ports_http }}:80/tcp"
+      - "{{ ports_https }}:443/tcp"
+      {% if initial_setup %}
+      - "{{ ports_initial }}:3000/tcp"
+      {% endif %}
+      {% endif %}
+      - "{{ ports_https }}:443/udp"
+      - "{{ ports_dns }}:53/tcp"
+      - "{{ ports_dns }}:53/udp"
+      - "{{ ports_tls }}:853/tcp"
+      - "{{ ports_dnscrypt }}:5443/tcp"
+      - "{{ ports_dnscrypt }}:5443/udp"
+      {% endif %}
+    {% endif %}
+    volumes:
+      {% if volume_mode == 'mount' %}
+      - {{ volume_mount_path }}/work:/opt/adguardhome/work:rw
+      - {{ volume_mount_path }}/conf:/opt/adguardhome/conf:rw
+      {% else  %}
+      - {{ service_name }}_work:/opt/adguardhome/work
+      - {{ service_name }}_conf:/opt/adguardhome/conf
+      {% endif %}
+    cap_add:
+      - NET_ADMIN
+      - NET_BIND_SERVICE
+      - NET_RAW
+    {% 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_domain }}`)
+      - traefik.http.routers.{{ service_name }}_http.entrypoints=web
+      {% 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_domain }}`)
+      - traefik.http.routers.{{ service_name }}_https.entrypoints=websecure
+      - traefik.http.routers.{{ service_name }}_https.tls=true
+      - traefik.http.routers.{{ service_name }}_https.tls.certresolver={{ traefik_tls_certresolver }}
+      {% endif %}
+      {% if initial_setup %}
+      - traefik.http.services.{{ service_name }}_setup.loadBalancer.server.port=3000
+      - traefik.http.routers.{{ service_name }}_setup.service={{ service_name }}_setup
+      - traefik.http.routers.{{ service_name }}_setup.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`) && PathPrefix(`/setup`)
+      - traefik.http.routers.{{ service_name }}_setup.entrypoints=web
+      - traefik.http.middlewares.{{ service_name }}_setup-strip.stripprefix.prefixes=/setup
+      - traefik.http.routers.{{ service_name }}_setup.middlewares={{ service_name }}_setup-strip
+      {% endif %}
+    {% endif %}
+
 {#
-  AdGuard Home Docker Compose Configuration
-  
-  This is the main orchestration file that includes all service definitions.
-  Services are split into separate files for better maintainability:
-  - services/adguardhome.yaml: Main AdGuard Home service
-  - common/networks.yaml: Network definitions
-  - common/volumes.yaml: Volume definitions
+  Network definitions (only when needed):
+  - When network_mode is empty: no definition needed (uses Docker's default bridge)
+  - When network_mode is 'bridge': define custom bridge network
+  - When network_mode is 'macvlan': configure macvlan with static IP (for Compose mode)
+    Note: In Swarm mode, macvlan networks must be created manually with config-only networks on each node
+  - When swarm_enabled: use overlay network for multi-host communication
+  - Traefik network: always external (managed by Traefik)
 #}
-include:
-  - services/adguardhome.yaml
-  {% if network_mode == 'bridge' or network_mode == 'macvlan' or traefik_enabled %}
-  - common/networks.yaml
+{% if network_mode == 'bridge' or network_mode == 'macvlan' or traefik_enabled %}
+networks:
+  {% if network_mode == 'bridge' or network_mode == 'macvlan'%}
+  {{ 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 %}
-  - common/volumes.yaml
+  {% if traefik_enabled %}
+  {{ traefik_network }}:
+    external: true
+  {% endif %}
+{% endif %}
+
+{#
+  Volume definitions:
+  - When volume_mode is 'local' (default): use docker-managed local volumes
+  - When volume_mode is 'nfs': configure NFS-backed volumes
+  - When volume_mode is 'mount': no volume definition needed (bind mounts used directly)
+#}
+{% if volume_mode == 'local' %}
+volumes:
+  {{ service_name }}_work:
+    driver: local
+  {{ service_name }}_conf:
+    driver: local
+{% elif volume_mode == 'nfs' %}
+volumes:
+  {{ service_name }}_work:
+    driver: local
+    driver_opts:
+      type: nfs
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}/work"
+  {{ service_name }}_conf:
+    driver: local
+    driver_opts:
+      type: nfs
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}/conf"
+{% endif %}

+ 0 - 157
library/compose/adguardhome/services/adguardhome.yaml.j2

@@ -1,157 +0,0 @@
----
-services:
-  {{ service_name }}:
-    image: docker.io/adguard/adguardhome:v0.107.69
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    {% if container_name %}
-    container_name: {{ container_name }}
-    {% endif %}
-    {% endif %}
-    {% if container_hostname %}
-    hostname: {{ container_hostname }}
-    {% endif %}
-    {% if network_mode == 'host' %}
-    network_mode: host
-    {% elif network_mode == 'bridge' or network_mode == 'macvlan' or traefik_enabled %}
-    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 network_mode == '' or network_mode == 'bridge' or traefik_enabled %}
-    ports:
-      {% if not traefik_enabled %}
-      {% if swarm_enabled %}
-      - target: 80
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      - target: 443
-        published: {{ ports_https }}
-        protocol: tcp
-        mode: host
-      {% if initial_setup %}
-      - target: 3000
-        published: {{ ports_initial }}
-        protocol: tcp
-        mode: host
-      {% endif %}
-      {% else %}
-      - "{{ ports_http }}:80/tcp"
-      - "{{ ports_https }}:443/tcp"
-      {% if initial_setup %}
-      - "{{ ports_initial }}:3000/tcp"
-      {% endif %}
-      {% endif %}
-      {% endif %}
-      {% if swarm_enabled %}
-      - target: 443
-        published: {{ ports_https }}
-        protocol: udp
-        mode: host
-      - target: 53
-        published: {{ ports_dns }}
-        protocol: tcp
-        mode: host
-      - target: 53
-        published: {{ ports_dns }}
-        protocol: udp
-        mode: host
-      - target: 853
-        published: {{ ports_tls }}
-        protocol: tcp
-        mode: host
-      - target: 5443
-        published: {{ ports_dnscrypt }}
-        protocol: tcp
-        mode: host
-      - target: 5443
-        published: {{ ports_dnscrypt }}
-        protocol: udp
-        mode: host
-      {% else %}
-      - "{{ ports_https }}:443/udp"
-      - "{{ ports_dns }}:53/tcp"
-      - "{{ ports_dns }}:53/udp"
-      - "{{ ports_tls }}:853/tcp"
-      - "{{ ports_dnscrypt }}:5443/tcp"
-      - "{{ ports_dnscrypt }}:5443/udp"
-      {% endif %}
-    {% endif %}
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/work:/opt/adguardhome/work:rw
-      - {{ volume_mount_path }}/conf:/opt/adguardhome/conf:rw
-      {% else  %}
-      - {{ service_name }}-work:/opt/adguardhome/work
-      - {{ service_name }}-conf:/opt/adguardhome/conf
-      {% endif %}
-    cap_add:
-      - NET_ADMIN
-      - NET_BIND_SERVICE
-      - NET_RAW
-    {% if swarm_enabled %}
-    deploy:
-      mode: replicated
-      replicas: 1
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      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_domain }}`)
-        - 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_domain }}`)
-        - 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 initial_setup %}
-        - traefik.http.services.{{ service_name }}-setup.loadBalancer.server.port=3000
-        - traefik.http.routers.{{ service_name }}-setup.service={{ service_name }}-setup
-        - traefik.http.routers.{{ service_name }}-setup.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`) && PathPrefix(`/setup`)
-        - traefik.http.routers.{{ service_name }}-setup.entrypoints={{ traefik_entrypoint }}
-        - traefik.http.middlewares.{{ service_name }}-setup-strip.stripprefix.prefixes=/setup
-        - traefik.http.routers.{{ service_name }}-setup.middlewares={{ service_name }}-setup-strip
-        {% endif %}
-      {% endif %}
-    {% endif %}
-    {% if traefik_enabled and not swarm_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_domain }}`)
-      - 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_domain }}`)
-      - 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 initial_setup %}
-      - traefik.http.services.{{ service_name }}-setup.loadBalancer.server.port=3000
-      - traefik.http.routers.{{ service_name }}-setup.service={{ service_name }}-setup
-      - traefik.http.routers.{{ service_name }}-setup.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`) && PathPrefix(`/setup`)
-      - traefik.http.routers.{{ service_name }}-setup.entrypoints={{ traefik_entrypoint }}
-      - traefik.http.middlewares.{{ service_name }}-setup-strip.stripprefix.prefixes=/setup
-      - traefik.http.routers.{{ service_name }}-setup.middlewares={{ service_name }}-setup-strip
-      {% endif %}
-    {% endif %}

+ 11 - 45
library/compose/adguardhome/template.yaml

@@ -1,15 +1,18 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   name: AdGuard Home
   description: |-
     Network-wide software for blocking ads and tracking. AdGuard Home operates as a DNS server that
     re-routes tracking domains to a "black hole", thus preventing your devices from connecting to those servers.
     It features advanced DNS filtering, parental controls, safe browsing, and HTTPS/DNS-over-TLS/DNS-over-QUIC support.
-    
-    :warning: In Docker Swarm, AdGuard Home does NOT support running multiple replicas.
-    This template enforces a single replica with node placement constraints to ensure stable DNS resolution.
+    ## Prerequisites
+    - :info: During the initial setup, AdGuard Home runs an HTTP server on port 3000 to guide you through configuration.
+    After completing the setup, AdGuard Home switches to the configured HTTP port, and port, consider re-deploying the
+    service with `initial_setup=false`.
+    - :warning: If you require DHCP functionality or want AdGuard Home to bind directly to port 53,
+    you must set `network_mode` to `host` or `macvlan`. Note this exposes all container ports directly on the host.
+    You can't use `traefik_enabled` in this case!
     ## References
     - **Project:** https://adguard.com/adguard-home/overview.html
     - **Documentation:** https://github.com/AdguardTeam/AdGuardHome/wiki
@@ -19,47 +22,10 @@ metadata:
   date: '2025-11-13'
   tags:
     - traefik
-    - swarm
-    - network_modes
-    - volume_modes
-  next_steps: |
-    ### 1. Deploy the Service
-    {% if swarm_enabled -%}
-    Deploy to Docker Swarm:
-    ```bash
-    docker stack deploy -c compose.yaml adguardhome
-    ```
-    {% else -%}
-    Start AdGuard Home using Docker Compose:
-    ```bash
-    docker compose up -d
-    ```
-    {% endif -%}
-    {% if initial_setup -%}
-    ### 2. Initial Setup
-    **NOTE:** Port {{ ports_initial }} is only used for the initial setup wizard. After completing the setup, AdGuard Home will automatically switch to port {{ ports_http }} for the web interface, and port {{ ports_initial }} will become inactive. Make sure to switch the `initial_setup` variable to `false` after the initial configuration to avoid exposing the setup wizard.
-    {% if traefik_enabled -%}
-    * Navigate to: **http://{{ traefik_host }}.{{ traefik_domain }}/setup**
-    {% else -%}
-    * Navigate to: **http://localhost:{{ ports_initial }}**
-    {% endif -%}
-    * Complete the initial setup wizard to configure:
-      - Admin credentials (username and password)
-      - DNS server settings and upstream DNS providers
-      - Network interface bindings
-      - Optional features (DHCP, DNS-over-HTTPS, etc.)
-    * **IMPORTANT:** During setup, configure the web interface to use port {{ ports_http }}.
-    ### 3. Access the Web Interface
-    After completing the initial setup:
-    {% else -%}
-    ### 2. Access the Web Interface
-    {% endif -%}
-    {% if traefik_enabled -%}
-    * Navigate to: **https://{{ traefik_host }}.{{ traefik_domain }}**
-    {% else -%}
-    * Navigate to: **http://localhost:{{ ports_http }}**
-    {% endif -%}
-    * Login using the credentials configured during initial setup.
+    - network
+    - volume
+  next_steps:
+schema: "1.2"
 spec:
   general:
     vars:

+ 12 - 117
library/compose/alloy/compose.yaml.j2

@@ -1,60 +1,26 @@
 services:
   {{ service_name }}:
     image: grafana/alloy:v1.11.3
-    {#
-      If not in swarm mode, check whether container_name is set and apply restart policy,
-      else swarm mode handles restarts via deploy.restart_policy
-    #}
-    {% if not swarm_enabled %}
     restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    {#
-      Set container hostname for identification in metrics/logs
-    #}
+    {% if container_hostname %}
     hostname: {{ container_hostname }}
-    {#
-      Alloy command configuration:
-      - HTTP server listens on 0.0.0.0:12345
-      - Data storage in /var/lib/alloy/data
-      - Configuration file at /etc/alloy/config.alloy
-    #}
+    {% endif %}
     command:
       - run
       - --server.http.listen-addr=0.0.0.0:12345
       - --storage.path=/var/lib/alloy/data
       - /etc/alloy/config.alloy
-    {#
-      When traefik is enabled, add traefik network for reverse proxy access
-    #}
     {% if traefik_enabled %}
     networks:
       {{ traefik_network }}:
     {% endif %}
-    {#
-      Port mappings for HTTP server (only when Traefik is disabled)
-      Note: Swarm mode uses 'host' mode for port publishing to avoid port conflicts
-    #}
-    {% if not traefik_enabled %}
+    {% if not traefik_enabled and general_webui_enabled %}
     ports:
-      {% if swarm_enabled %}
-      - target: 12345
-        published: {{ ports_main }}
-        protocol: tcp
-        mode: host
-      {% else %}
-      - "{{ ports_main }}:12345"
-      {% endif %}
+      - "{{ ports_webui }}:12345"
     {% endif %}
-    {#
-      Volume configuration:
-      - Config file: Alloy configuration (mounted from local directory)
-      - Data: Persistent storage for Alloy data
-      - System mounts: Required for log/metric collection from host
-    #}
     volumes:
       - ./config/config.alloy:/etc/alloy/config.alloy
-      - alloy_data:/var/lib/alloy/data
+      - {{ service_name }}_data:/var/lib/alloy/data
       {% if logs_enabled or metrics_enabled %}
       - /:/rootfs:ro
       - /sys:/sys:ro
@@ -69,100 +35,29 @@ services:
       {% if metrics_enabled and metrics_system %}
       - /run/udev/data:/run/udev/data:ro
       {% endif %}
-    {#
-      When traefik_enabled is set, and not running in swarm mode, add traefik labels
-      (optionally enable TLS if traefik_tls_enabled is set)
-    #}
-    {% if traefik_enabled and not swarm_enabled %}
+    {% if traefik_enabled and general_webui_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_domain }}`)
-      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-http.entrypoints=web
       {% 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_domain }}`)
-      - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-https.entrypoints=websecure
       - traefik.http.routers.{{ service_name }}-https.tls=true
       - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
     {% endif %}
-    {#
-      Deploy configuration for Swarm mode and/or resource limits:
-      - Swarm: Configure replicas, placement constraints, and restart policy
-      - Resources: Set CPU/memory limits (and reservations in Swarm mode)
-      - Traefik: Labels for reverse proxy integration (Swarm mode)
-    #}
-    {% if swarm_enabled or resources_enabled %}
-    deploy:
-      {% if swarm_enabled %}
-      mode: {{ swarm_placement_mode }}
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      {% endif %}
-      {% if swarm_placement_host %}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif %}
-      restart_policy:
-        condition: on-failure
-      {% endif %}
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        {% if swarm_enabled %}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-        {% endif %}
-      {% endif %}
-      {#
-        When traefik_enabled is set in swarm mode, add traefik labels
-        (optionally enable TLS if traefik_tls_enabled is set, and authentik middleware if enabled)
-      #}
-      {% if swarm_enabled and 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_domain }}`)
-        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
-        {% if authentik_enabled %}
-        - traefik.http.routers.{{ service_name }}-http.middlewares={{ authentik_traefik_middleware }}
-        {% endif %}
-        {% 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_domain }}`)
-        - 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 }}
-        {% if authentik_enabled %}
-        - traefik.http.routers.{{ service_name }}-https.middlewares={{ authentik_traefik_middleware }}
-        {% endif %}
-        {% endif %}
-      {% endif %}
-    {% endif %}
-
-{#
-  Volume definitions:
-  - alloy_data: Persistent storage for Alloy data
-#}
-volumes:
-  alloy_data:
-    driver: local
 
-{#
-  Network definitions (only when Traefik is enabled):
-  - Traefik network: always external (managed by Traefik)
-#}
 {% if traefik_enabled %}
 networks:
   {{ traefik_network }}:
     external: true
 {% endif %}
+
+volumes:
+  {{ service_name }}_data:
+    driver: local

+ 17 - 21
library/compose/alloy/template.yaml

@@ -1,41 +1,39 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   name: Grafana Alloy
-  description: >
+  description: |-
     Grafana Alloy is an open telemetry collector distribution that gathers
     and processes logs, metrics, traces and profiles. It combines features
     from the OpenTelemetry Collector and Prometheus and provides programmable
     pipelines and high-performance, vendor-neutral observability.
-
-
-    Project: https://grafana.com/docs/alloy/
-
-    Source: https://github.com/grafana/alloy
-
-    Documentation: https://grafana.com/docs/alloy/latest/
+    ## Prerequisites
+    - :info: Set the `container_hostname` to your desired hostname. (This is
+    useful for identification in logs and metrics systems.)
+    ## References
+    * **Project:** https://grafana.com/docs/alloy/
+    * **Source:** https://github.com/grafana/alloy
+    * **Documentation:** https://grafana.com/docs/alloy/latest/
   version: v1.11.2
   author: Christian Lempa
   date: '2025-10-13'
   tags:
     - traefik
-    - swarm
-    - authentik
+schema: "1.2"
 spec:
   general:
     vars:
-      container_hostname:
-        type: str
-        description: Docker host hostname for container identification
-        default: hostname
-        extra: This is needed because when alloy runs in a container, it doesn't know the hostname of the docker host.
+      general_webui_enabled:
+        type: bool
+        description: Enable Alloy web user interface
   ports:
     vars:
-      ports_main:
+      ports_webui:
         type: int
         description: Main port for Alloy HTTP server
         default: 12345
+        needs: ["general_webui_enabled=true"]
+        required: true
   logs:
     name: Log Collection
     description: Configure log collection for Docker containers and system logs
@@ -49,14 +47,13 @@ spec:
         type: url
         description: Loki endpoint URL for sending logs
         default: http://loki:3100/loki/api/v1/push
+        required: true
       logs_docker:
         type: bool
         description: Enable Docker container log collection
-        default: true
       logs_system:
         type: bool
         description: Enable system and journalctl log collection
-        default: true
   metrics:
     name: Metrics Collection
     description: Configure metrics collection for Docker containers and system metrics
@@ -70,11 +67,10 @@ spec:
         type: url
         description: Prometheus remote write endpoint
         default: http://prometheus:9090/api/v1/write
+        required: true
       metrics_docker:
         type: bool
         description: Enable Docker container metrics collection (cAdvisor)
-        default: true
       metrics_system:
         type: bool
         description: Enable system (node) metrics collection
-        default: true

+ 4 - 9
library/compose/authentik/.env.j2

@@ -1,13 +1,8 @@
-# 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
+{% if email_enabled %}
 EMAIL_PASSWORD={{ email_password }}
 {% endif %}
+{% if authentik_admin_password %}
+AUTHENTIK_ADMIN_PASSWORD={{ authentik_admin_password }}
+{% endif %}

+ 0 - 9
library/compose/authentik/common/networks.yaml.j2

@@ -1,9 +0,0 @@
-{#
-  Network definitions (only when Traefik is enabled):
-  - Traefik network: always external (managed by Traefik)
-#}
-{% if traefik_enabled %}
-networks:
-  {{ traefik_network }}:
-    external: true
-{% endif %}

+ 0 - 55
library/compose/authentik/common/volumes.yaml.j2

@@ -1,55 +0,0 @@
-{#
-  Volume definitions:
-  - When volume_mode is 'local' (default): use docker-managed local volumes
-  - When volume_mode is 'nfs': configure NFS-backed volumes
-  - When volume_mode is 'mount': no volume definition needed (bind mounts used directly)
-#}
-{% if volume_mode == 'local' %}
-volumes:
-  {% if not database_external %}
-  {{ service_name }}-postgres:
-    driver: local
-  {% endif %}
-  {{ service_name }}-redis:
-    driver: local
-  {{ service_name }}-media:
-    driver: local
-  {{ service_name }}-certs:
-    driver: local
-  {{ service_name }}-templates:
-    driver: local
-{% elif volume_mode == 'nfs' %}
-volumes:
-  {% if not database_external %}
-  {{ service_name }}-postgres:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/postgres"
-  {% endif %}
-  {{ service_name }}-redis:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/redis"
-  {{ service_name }}-media:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/media"
-  {{ service_name }}-certs:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/certs"
-  {{ service_name }}-templates:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/templates"
-{% endif %}

+ 204 - 18
library/compose/authentik/compose.yaml.j2

@@ -1,23 +1,209 @@
+---
+services:
+  {{ service_name }}:
+    image: ghcr.io/goauthentik/server:2025.10.1
+    restart: {{ restart_policy }}
+    command: server
+    environment:
+      - AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY}
+      - AUTHENTIK_ERROR_REPORTING__ENABLED={{ authentik_error_reporting }}
+      - 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__FROM={{ email_from }}
+      - AUTHENTIK_EMAIL__USERNAME={{ email_username }}
+      - AUTHENTIK_EMAIL__PASSWORD=${EMAIL_PASSWORD}
+      {% if email_encryption == "ssl" %}
+      - AUTHENTIK_EMAIL__USE_SSL=True
+      {% elif email_encryption == "starttls" %}
+      - AUTHENTIK_EMAIL__USE_TLS=True
+      {% endif %}
+      {% endif %}
+    networks:
+      {% if traefik_enabled %}
+      - {{ traefik_network }}
+      {% endif %}
+      - {{ service_name }}_backend
+    {% if not traefik_enabled %}
+    ports:
+      - "{{ ports_http }}:9000"
+      - "{{ ports_https }}:9443"
+    {% endif %}
+    volumes:
+      {% if volume_mode == 'mount' %}
+      - {{ volume_mount_path }}/media:/media
+      - {{ volume_mount_path }}/templates:/templates
+      {% elif volume_mode == 'local' or volume_mode == 'nfs' %}
+      - {{ service_name }}_media:/media
+      - {{ service_name }}_templates:/templates
+      {% endif %}
+    {% 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_domain }}`)
+      - traefik.http.routers.{{ service_name }}_http.entrypoints=web
+      {% 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_domain }}`)
+      - traefik.http.routers.{{ service_name }}_https.entrypoints=websecure
+      - traefik.http.routers.{{ service_name }}_https.tls=true
+      - traefik.http.routers.{{ service_name }}_https.tls.certresolver={{ traefik_tls_certresolver }}
+      {% endif %}
+    {% endif %}
+    {% if not database_external %}
+    depends_on:
+      - {{ service_name }}_postgres
+    {% endif %}
+
+  {#
+    Authentik Worker: Background task processor
+    Handles long-running tasks like email sending, cleanup jobs, and scheduled tasks
+  #}
+  {{ service_name }}_worker:
+    image: ghcr.io/goauthentik/server:2025.10.1
+    restart: {{ restart_policy }}
+    command: worker
+    environment:
+      - AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY}
+      - AUTHENTIK_ERROR_REPORTING__ENABLED={{ authentik_error_reporting }}
+      - 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__FROM={{ email_from }}
+      - AUTHENTIK_EMAIL__USERNAME={{ email_username }}
+      - AUTHENTIK_EMAIL__PASSWORD=${EMAIL_PASSWORD}
+      {% if email_encryption == "ssl" %}
+      - AUTHENTIK_EMAIL__USE_SSL=True
+      {% elif email_encryption == "starttls" %}
+      - AUTHENTIK_EMAIL__USE_TLS=True
+      {% endif %}
+      {% endif %}
+      {% if authentik_admin_password %}
+      - AUTHENTIK_BOOTSTRAP_PASSWORD=${AUTHENTIK_ADMIN_PASSWORD}
+      {% endif %}
+    user: root
+    networks:
+      - {{ service_name }}_backend
+    volumes:
+      {# the embedded outpost uses the docker socket to manage containers #}
+      - /run/docker.sock:/run/docker.sock
+      {% if volume_mode == 'mount' %}
+      - {{ volume_mount_path }}/media:/media
+      - {{ volume_mount_path }}/certs:/certs
+      - {{ volume_mount_path }}/templates:/templates
+      {% elif volume_mode == 'local' or volume_mode == 'nfs' %}
+      - {{ service_name }}_media:/media
+      - {{ service_name }}_certs:/certs
+      - {{ service_name }}_templates:/templates
+      {% endif %}
+    {% if not database_external %}
+    depends_on:
+      - {{ service_name }}_postgres
+    {% endif %}
+
+  {#
+    PostgreSQL database service
+  #}
+  {% if not database_external %}
+  {{ service_name }}_postgres:
+    image: docker.io/library/postgres:17.6
+    restart: {{ restart_policy }}
+    environment:
+      - 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
+    networks:
+      - {{ service_name }}_backend
+    volumes:
+      {% if volume_mode == 'mount' %}
+      - {{ volume_mount_path }}/postgres:/var/lib/postgresql/data
+      {% elif volume_mode == 'local' or volume_mode == 'nfs' %}
+      - {{ service_name }}_postgres:/var/lib/postgresql/data
+      {% endif %}
+  {% endif %}
+
+{#
+  Network definitions:
+  - Backend network: Internal communication between services
+  - Traefik network: External access via reverse proxy (always external)
+#}
+networks:
+  {{ service_name }}_backend:
+    driver: bridge
+  {% if traefik_enabled %}
+  {{ traefik_network }}:
+    external: true
+  {% endif %}
+
 {#
-  Authentik Docker Compose Configuration
-  
-  This is the main orchestration file that includes all service definitions.
-  Services are split into separate files for better maintainability:
-  - services/server.yaml: Main Authentik web application and API
-  - services/worker.yaml: Background task worker
-  - services/redis.yaml: Redis cache and message broker
-  - services/postgres.yaml: PostgreSQL database (if not external)
-  - common/networks.yaml: Network definitions
-  - common/volumes.yaml: Volume definitions
+  Volume definitions:
+  - When volume_mode is 'local' (default): use docker-managed local volumes
+  - When volume_mode is 'nfs': configure NFS-backed volumes
+  - When volume_mode is 'mount': no volume definition needed (bind mounts used directly)
 #}
-include:
-  - services/server.yaml
-  - services/worker.yaml
-  - services/redis.yaml
+{% if volume_mode == 'local' %}
+volumes:
   {% if not database_external %}
-  - services/postgres.yaml
+  {{ service_name }}_postgres:
+    driver: local
   {% endif %}
-  {% if traefik_enabled %}
-  - common/networks.yaml
+  {{ service_name }}_redis:
+    driver: local
+  {{ service_name }}_media:
+    driver: local
+  {{ service_name }}_certs:
+    driver: local
+  {{ service_name }}_templates:
+    driver: local
+{% elif volume_mode == 'nfs' %}
+volumes:
+  {% if not database_external %}
+  {{ service_name }}_postgres:
+    driver: local
+    driver_opts:
+      type: nfs
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}/postgres"
   {% endif %}
-  - common/volumes.yaml
+  {{ service_name }}_redis:
+    driver: local
+    driver_opts:
+      type: nfs
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}/redis"
+  {{ service_name }}_media:
+    driver: local
+    driver_opts:
+      type: nfs
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}/media"
+  {{ service_name }}_certs:
+    driver: local
+    driver_opts:
+      type: nfs
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}/certs"
+  {{ service_name }}_templates:
+    driver: local
+    driver_opts:  
+      type: nfs
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}/templates"
+{% endif %}

+ 0 - 326
library/compose/authentik/compose.yaml.j2.backup

@@ -1,326 +0,0 @@
-services:
-  {{ service_name }}:
-    image: ghcr.io/goauthentik/server:2025.10.1
-    {#
-      If not in swarm mode, check whether container_name is set and apply restart policy,
-      else swarm mode handles restarts via deploy.restart_policy
-    #}
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    {#
-      Set container hostname for identification
-    #}
-    hostname: {{ container_hostname }}
-    command: server
-    {#
-      Environment variables for Authentik server configuration:
-      - Secret key, error reporting, Redis/PostgreSQL connection settings
-      - Optional email configuration for notifications
-    #}
-    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 %}
-    {#
-      When traefik is enabled, add traefik network for reverse proxy access
-    #}
-    {% if traefik_enabled %}
-    networks:
-      {{ traefik_network }}:
-    {% endif %}
-    {#
-      Port mappings for HTTP/HTTPS (only when Traefik is disabled)
-      Note: Swarm mode uses 'host' mode for port publishing to avoid port conflicts
-    #}
-    {% if not traefik_enabled %}
-    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 %}
-    {% endif %}
-    {#
-      Volume configuration for persistent data:
-      - media: User-uploaded media files
-      - templates: Custom template files
-    #}
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/media:/media
-      - {{ volume_mount_path }}/templates:/templates
-      {% elif volume_mode in ['local', 'nfs'] %}
-      - {{ service_name }}-media:/media
-      - {{ service_name }}-templates:/templates
-      {% endif %}
-    {#
-      When traefik_enabled is set, and not running in swarm mode, add traefik labels
-      (optionally enable TLS if traefik_tls_enabled is set)
-    #}
-    {% if traefik_enabled and not swarm_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_domain }}`)
-      - 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_domain }}`)
-      - 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 %}
-    depends_on:
-      - {{ service_name }}-postgres
-      - {{ service_name }}-redis
-    {#
-      Deploy configuration for Swarm mode and/or resource limits:
-      - Swarm: Configure replicas, placement constraints, and restart policy
-      - Resources: Set CPU/memory limits (and reservations in Swarm mode)
-      - Traefik: Labels for reverse proxy integration (Swarm mode)
-    #}
-    {% if swarm_enabled or resources_enabled %}
-    deploy:
-      {% if swarm_enabled %}
-      mode: {{ swarm_placement_mode }}
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      {% endif %}
-      {% if swarm_placement_host %}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif %}
-      restart_policy:
-        condition: on-failure
-      {% endif %}
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        {% if swarm_enabled %}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-        {% endif %}
-      {% endif %}
-      {#
-        When traefik_enabled is set in swarm mode, add traefik labels
-        (optionally enable TLS if traefik_tls_enabled is set)
-      #}
-      {% if swarm_enabled and 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_domain }}`)
-        - 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_domain }}`)
-        - 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 %}
-
-  {#
-    Authentik Worker: Background task processor
-    Handles long-running tasks like email sending, cleanup jobs, and scheduled tasks
-  #}
-  {{ service_name }}-worker:
-    image: ghcr.io/goauthentik/server:2025.10.1
-    {#
-      If not in swarm mode, apply restart policy and container name
-    #}
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ service_name }}-worker
-    {% endif %}
-    command: worker
-    {#
-      Environment variables (same as server for consistency)
-    #}
-    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
-    {#
-      Volume configuration:
-      - Docker socket: Required for worker to manage containers
-      - media/certs/templates: Shared with server container
-    #}
-    volumes:
-      - /run/docker.sock:/run/docker.sock
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/media:/media
-      - {{ volume_mount_path }}/certs:/certs
-      - {{ volume_mount_path }}/templates:/templates
-      {% elif volume_mode in ['local', 'nfs'] %}
-      - {{ service_name }}-media:/media
-      - {{ service_name }}-certs:/certs
-      - {{ service_name }}-templates:/templates
-      {% endif %}
-    depends_on:
-      - {{ service_name }}-postgres
-      - {{ service_name }}-redis
-
-  {#
-    Redis: Cache and message broker for Authentik
-    Stores sessions, caching data, and task queue messages
-  #}
-  {{ service_name }}-redis:
-    image: docker.io/library/redis:8.2.3
-    {#
-      If not in swarm mode, apply restart policy and container name
-    #}
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ service_name }}-redis
-    {% endif %}
-    command: --save 60 1 --loglevel warning
-    {#
-      Health check: Verify Redis is responding to ping commands
-    #}
-    healthcheck:
-      test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
-      start_period: 20s
-      interval: 30s
-      retries: 5
-      timeout: 3s
-    {#
-      Volume configuration for Redis data persistence
-    #}
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/redis:/data
-      {% elif volume_mode in ['local', 'nfs'] %}
-      - {{ service_name }}-redis:/data
-      {% endif %}
-
-  {#
-    PostgreSQL: Main database for Authentik (only if not using external database)
-    Stores all user data, configurations, and authentication flows
-  #}
-  {% if not database_external %}
-  {{ service_name }}-postgres:
-    image: docker.io/library/postgres:17.6
-    {#
-      If not in swarm mode, apply restart policy and container name
-    #}
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ service_name }}-db
-    {% endif %}
-    {#
-      Environment variables for PostgreSQL configuration
-    #}
-    environment:
-      - TZ={{ container_timezone }}
-      - POSTGRES_USER={{ database_user }}
-      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
-      - POSTGRES_DB={{ database_name }}
-    {#
-      Health check: Verify PostgreSQL is ready to accept connections
-    #}
-    healthcheck:
-      test: ["CMD-SHELL", "pg_isready -U {{ database_user }}"]
-      start_period: 30s
-      interval: 10s
-      timeout: 10s
-      retries: 5
-    {#
-      Volume configuration for PostgreSQL data persistence
-    #}
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/postgres:/var/lib/postgresql/data
-      {% elif volume_mode in ['local', 'nfs'] %}
-      - {{ service_name }}-postgres:/var/lib/postgresql/data
-      {% endif %}
-  {% endif %}
-
-{#
-  Volume definitions:
-  - When volume_mode is 'local' (default): use docker-managed local volumes
-  - When volume_mode is 'nfs': configure NFS-backed volumes
-  - When volume_mode is 'mount': no volume definition needed (bind mounts used directly)
-#}
-{% if volume_mode == 'local' %}
-volumes:
-  {% if not database_external %}
-  {{ service_name }}-postgres:
-    driver: local
-  {% endif %}
-  {{ service_name }}-redis:
-    driver: local
-{% elif volume_mode == 'nfs' %}
-volumes:
-  {{ service_name }}-postgres:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/postgres"
-  {{ service_name }}-redis:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/redis"
-{% endif %}
-
-{#
-  Network definitions (only when Traefik is enabled):
-  - Traefik network: always external (managed by Traefik)
-#}
-{% if traefik_enabled %}
-networks:
-  {{ traefik_network }}:
-    external: true
-{% endif %}

+ 0 - 28
library/compose/authentik/services/postgres.yaml.j2

@@ -1,28 +0,0 @@
-{#
-  PostgreSQL: Main database for Authentik
-  Stores all user data, configurations, and authentication flows
-#}
-services:
-  {{ service_name }}-postgres:
-    image: docker.io/library/postgres:17.6
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ service_name }}-db
-    {% 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:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/postgres:/var/lib/postgresql/data
-      {% elif volume_mode in ['local', 'nfs'] %}
-      - {{ service_name }}-postgres:/var/lib/postgresql/data
-      {% endif %}

+ 0 - 24
library/compose/authentik/services/redis.yaml.j2

@@ -1,24 +0,0 @@
-{#
-  Redis: Cache and message broker for Authentik
-  Stores sessions, caching data, and task queue messages
-#}
-services:
-  {{ 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"]
-      start_period: 20s
-      interval: 30s
-      retries: 5
-      timeout: 3s
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/redis:/data
-      {% elif volume_mode in ['local', 'nfs'] %}
-      - {{ service_name }}-redis:/data
-      {% endif %}

+ 0 - 124
library/compose/authentik/services/server.yaml.j2

@@ -1,124 +0,0 @@
-{#
-  Authentik Server: Main web application and API
-  Handles authentication, authorization, and user interface
-#}
-services:
-  {{ service_name }}:
-    image: ghcr.io/goauthentik/server:2025.10.1
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
-    command: server
-    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}
-      {% if email_encryption == "starttls" %}
-      - AUTHENTIK_EMAIL__USE_TLS=true
-      {% elif email_encryption == "ssl" %}
-      - AUTHENTIK_EMAIL__USE_SSL=true
-      {% endif %}
-      - AUTHENTIK_EMAIL__FROM={{ email_from }}
-      {% endif %}
-    {% if traefik_enabled %}
-    networks:
-      {{ traefik_network }}:
-    {% endif %}
-    {% if not traefik_enabled %}
-    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 %}
-    {% endif %}
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/media:/media
-      - {{ volume_mount_path }}/templates:/templates
-      {% elif volume_mode in ['local', 'nfs'] %}
-      - {{ service_name }}-media:/media
-      - {{ service_name }}-templates:/templates
-      {% endif %}
-    {% if traefik_enabled and not swarm_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_domain }}`)
-      - 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_domain }}`)
-      - 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 %}
-    depends_on:
-      - {{ service_name }}-postgres
-      - {{ service_name }}-redis
-    {% if swarm_enabled or resources_enabled %}
-    deploy:
-      {% if swarm_enabled %}
-      mode: {{ swarm_placement_mode }}
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      {% endif %}
-      {% if swarm_placement_host %}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif %}
-      restart_policy:
-        condition: on-failure
-      {% endif %}
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        {% if swarm_enabled %}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-        {% endif %}
-      {% endif %}
-      {% if swarm_enabled and 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_domain }}`)
-        - 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_domain }}`)
-        - 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 %}

+ 0 - 48
library/compose/authentik/services/worker.yaml.j2

@@ -1,48 +0,0 @@
-{#
-  Authentik Worker: Background task processor
-  Handles long-running tasks like email sending, cleanup jobs, and scheduled tasks
-#}
-services:
-  {{ 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
-    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}
-      {% if email_encryption == "starttls" %}
-      - AUTHENTIK_EMAIL__USE_TLS=true
-      {% elif email_encryption == "ssl" %}
-      - AUTHENTIK_EMAIL__USE_SSL=true
-      {% endif %}
-      - AUTHENTIK_EMAIL__FROM={{ email_from }}
-      {% endif %}
-    user: root
-    volumes:
-      - /run/docker.sock:/run/docker.sock
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/media:/media
-      - {{ volume_mount_path }}/certs:/certs
-      - {{ volume_mount_path }}/templates:/templates
-      {% elif volume_mode in ['local', 'nfs'] %}
-      - {{ service_name }}-media:/media
-      - {{ service_name }}-certs:/certs
-      - {{ service_name }}-templates:/templates
-      {% endif %}
-    depends_on:
-      - {{ service_name }}-postgres
-      - {{ service_name }}-redis

+ 28 - 59
library/compose/authentik/template.yaml

@@ -1,72 +1,41 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   name: Authentik
-  description: >
+  description: |-
     Integrate Authentik Single Sign-On (SSO) for secure and streamlined user authentication.
     Authentik is an open-source identity provider that supports various authentication protocols.
     This configuration enables OAuth-based SSO, allowing users to log in using their Authentik
     credentials, enhancing security and user experience.
-
-
-    Project: https://goauthentik.io/
-
-    Documentation: https://goauthentik.io/docs/
-
-    GitHub: https://github.com/goauthentik/authentik
+    ## Prerequisites
+    - :warning: The `authentik_secret_key` must be generated using the following command
+    according to the official documentation.
+    ```bash
+    echo "$(openssl rand -base64 60 | tr -d '\n')"
+    ```
+    ## References
+    * **Project:** https://goauthentik.io/
+    * **Documentation:** https://goauthentik.io/docs/
+    * **GitHub:** https://github.com/goauthentik/authentik
+  next_steps: |-
+    Log in with your initial admin user:
+    ```bash
+    Username: akadmin
+    Password: {{ authentik_admin_password }}
+    ```
   version: 2025.10.1
   author: Christian Lempa
   date: '2025-11-05'
   tags:
     - traefik
-    - swarm
-    - volume_modes
-  next_steps: |
-    1. Start Authentik:
-       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:
-       - Follow the setup wizard to create your admin account
-       - Configure authentication flows and providers
-       - Set up user directory (LDAP, Active Directory, or local)
-
-    4. Configure your first application:
-       - Navigate to Applications → Create
-       - Choose authentication provider (OAuth2, SAML, LDAP, etc.)
-       - Configure redirect URIs and client credentials
-       - Assign users or groups to the application
-
-    5. Important configuration:
-       - Secret Key: {{ authentik_secret_key }}
-       - Database Password: {{ database_password }}
-       - Store these credentials securely!
-
-    6. Security recommendations:
-       - Enable two-factor authentication for admin accounts
-       - Configure backup flows and recovery tokens
-       - Set up email notifications for security events
-       - Review and customize authentication policies
-       - Regularly backup the database and media files
-
-    For more information, visit: https://goauthentik.io/docs/
+    - volume
+schema: "1.2"
 spec:
   general:
     vars:
       service_name:
         default: authentik
-      container_name:
-        default: authentik-server
   database:
-    required: true
     vars:
       database_name:
         default: authentik
@@ -75,28 +44,28 @@ spec:
   ports:
     vars:
       ports_http:
-        description: Host port for HTTP
-        type: int
         default: 8000
       ports_https:
-        description: Host port for HTTPS
-        type: int
         default: 8443
   traefik:
     vars:
       traefik_host:
-        default: authentik.home.arpa
+        default: authentik
   authentik:
     description: Configure Authentik application settings
     required: true
     vars:
-      authentik_error_reporting:
-        description: Enable error reporting to Authentik developers
-        type: bool
-        default: false
       authentik_secret_key:
         description: Secret Key
         extra: Used for cookie signing and unique user IDs
         type: str
         sensitive: true
+        required: true
+      authentik_admin_password:
+        description: Initial admin user password
+        type: str
+        sensitive: true
         autogenerated: true
+      authentik_error_reporting:
+        description: Enable error reporting to Authentik developers
+        type: bool

+ 49 - 63
library/compose/bind9/compose.yaml.j2

@@ -1,79 +1,66 @@
 services:
   {{ service_name }}:
-    image: docker.io/ubuntu/bind9:{{ bind9_version }}
-    {#
-      If not in swarm mode, check whether container_name is set and apply restart policy,
-      else swarm mode handles restarts via deploy.restart_policy
-    #}
-    {% if not swarm_enabled %}
+    image: docker.io/ubuntu/bind9:9.20-24.10_edge
     restart: {{ restart_policy }}
-    {% if container_name %}
-    container_name: {{ container_name }}
-    {% endif %}
-    {% endif %}
-    {#
-      Set container hostname (BIND9 uses this for identification)
-    #}
     {% if container_hostname %}
     hostname: {{ container_hostname }}
     {% endif %}
-    {#
-      Environment variables for BIND9 configuration
-      - TZ: Timezone for proper log rotation
-      - BIND9_USER: User to run BIND9 as
-    #}
     environment:
       - TZ={{ container_timezone }}
       - BIND9_USER=bind
-    {#
-      Port mappings for DNS queries:
-      - DNS ports: Port 53 TCP/UDP (always exposed for DNS queries)
-      Note: Swarm mode uses 'host' mode for port publishing to avoid port conflicts
-    #}
+    {% if network_mode == 'host' %}
+    network_mode: host
+    {% elif network_mode == 'bridge' or network_mode == 'macvlan' %}
+    networks:
+      {% if network_mode == 'macvlan' %}
+      {{ network_name }}:
+        ipv4_address: {{ network_macvlan_ipv4_address }}
+      {% elif network_mode == 'bridge' %}
+      {{ network_name }}:
+      {% endif %}
+    {% endif %}
+    {% if network_mode == '' or network_mode == 'bridge' %}
     ports:
-      {% if swarm_enabled %}
-      - target: 53
-        published: 53
-        protocol: tcp
-        mode: host
-      - target: 53
-        published: 53
-        protocol: udp
-        mode: host
-      {% else %}
       - "53:53/tcp"
       - "53:53/udp"
-      {% endif %}
-    {#
-      Volume configuration for persistent data
-      - When volume_mode is 'mount': bind mount from host path
-      - When volume_mode is 'local', 'nfs', or empty: use docker-managed volumes
-    #}
+    {% endif %}
     volumes:
       {% if volume_mode == 'mount' %}
       - {{ volume_mount_path }}/config:/etc/bind:rw
       - {{ volume_mount_path }}/zones:/var/lib/bind:rw
       - {{ volume_mount_path }}/cache:/var/cache/bind:rw
       {% else %}
-      - {{ service_name }}-config:/etc/bind
-      - {{ service_name }}-zones:/var/lib/bind
-      - {{ service_name }}-cache:/var/cache/bind
+      - {{ service_name }}_config:/etc/bind
+      - {{ service_name }}_zones:/var/lib/bind
+      - {{ service_name }}_cache:/var/cache/bind
       {% endif %}
-    {#
-      Deploy configuration for Swarm mode:
-      - Single replica with node placement constraint (BIND9 doesn't support multi-replica)
-      - Multiple instances would conflict with DNS services
-    #}
-    {% if swarm_enabled %}
-    deploy:
-      mode: replicated
-      replicas: 1
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      restart_policy:
-        condition: on-failure
+
+{#
+  Network definitions (only when needed):
+  - When network_mode is empty: no definition needed (uses Docker's default bridge)
+  - When network_mode is 'bridge': define custom bridge network
+  - When network_mode is 'macvlan': configure macvlan with static IP (for Compose mode)
+#}
+{% if network_mode == 'bridge' or network_mode == 'macvlan' %}
+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 network_mode == 'bridge' %}
+    driver: bridge
     {% endif %}
+  {% endif %}
+{% endif %}
 
 {#
   Volume definitions:
@@ -83,31 +70,30 @@ services:
 #}
 {% if volume_mode == 'local' %}
 volumes:
-  {{ service_name }}-config:
+  {{ service_name }}_config:
     driver: local
-  {{ service_name }}-zones:
+  {{ service_name }}_zones:
     driver: local
-  {{ service_name }}-cache:
+  {{ service_name }}_cache:
     driver: local
 {% elif volume_mode == 'nfs' %}
 volumes:
-  {{ service_name }}-config:
+  {{ service_name }}_config:
     driver: local
     driver_opts:
       type: nfs
       o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
       device: ":{{ volume_nfs_path }}/config"
-  {{ service_name }}-zones:
+  {{ service_name }}_zones:
     driver: local
     driver_opts:
       type: nfs
       o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
       device: ":{{ volume_nfs_path }}/zones"
-  {{ service_name }}-cache:
+  {{ service_name }}_cache:
     driver: local
     driver_opts:
       type: nfs
       o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
       device: ":{{ volume_nfs_path }}/cache"
 {% endif %}
-

+ 2 - 2
library/compose/bind9/config/named.conf.j2

@@ -1,10 +1,10 @@
 // BIND9 Main Configuration File
 // Documentation: https://bind9.readthedocs.io/
 
-// Include TSIG keys for secure zone transfers
+{% if tsig_enabled %}
 include "/etc/bind/tsig.key";
+{% endif %}
 
-// ACL definitions for access control
 acl "trusted" {
     127.0.0.1;
     ::1;

+ 9 - 27
library/compose/bind9/config/named.conf.zones.j2

@@ -1,32 +1,14 @@
-// Custom DNS Zones Configuration
-// Add your authoritative zones here
-
-// Example zone for {{ domain_name }}
 zone "{{ domain_name }}" {
     type master;
     file "/var/lib/bind/db.{{ domain_name }}";
-    
-    // Allow zone transfers using TSIG key
-    allow-transfer { key "{{ tsig_key_name }}"; };
-    
-    // Enable zone updates with TSIG (for dynamic DNS)
-    // allow-update { key "{{ tsig_key_name }}"; };
-    
-    // Enable DNSSEC inline signing (optional)
-    // dnssec-policy default;
-    // inline-signing yes;
-};
 
-// Example reverse zone for 192.168.1.0/24
-// zone "1.168.192.in-addr.arpa" {
-//     type master;
-//     file "/var/lib/bind/db.192.168.1";
-//     allow-transfer { key "{{ tsig_key_name }}"; };
-// };
+    {% if tsig_enabled %}
+    allow-transfer { key "tsig-transfer-key"; };
+    allow-update { key "tsig-transfer-key"; };
+    {% endif %}
 
-// Secondary/Slave zone example
-// zone "secondary.example.com" {
-//     type slave;
-//     masters { 192.168.1.100 key {{ tsig_key_name }}; };
-//     file "/var/lib/bind/db.secondary.example.com";
-// };
+    {% if dnssec_enabled %}
+    dnssec-policy default;
+    inline-signing yes;
+    {% endif %}
+};

+ 3 - 10
library/compose/bind9/config/tsig.key.j2

@@ -1,13 +1,6 @@
-// TSIG Key for Secure Zone Transfers
-// Auto-generated base64-encoded secret for secure zone transfers and dynamic DNS updates
-// Algorithm: hmac-sha256
-
-key "{{ tsig_key_name }}" {
+{% if tsig_enabled %}
+key "tsig-transfer-key" {
     algorithm hmac-sha256;
     secret "{{ tsig_key_secret }}";
 };
-
-// To manually generate a new key:
-// docker exec bind9 tsig-keygen -a hmac-sha256 {{ tsig_key_name }}
-//
-// Then update the secret value above with the generated secret
+{% endif %}

+ 20 - 47
library/compose/bind9/template.yaml

@@ -1,70 +1,43 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   name: BIND9
-  description: >
+  description: |-
     BIND9 is the most widely used DNS server on the Internet.
     This template provides an authoritative and recursive DNS server with example zones,
     TSIG authentication for secure zone transfers, and DNSSEC support.
-
-
-    Project: https://www.isc.org/bind/
-
-    Documentation: https://bind9.readthedocs.io/
+    ## References
+    * **Project:** https://www.isc.org/bind/
+    * **Documentation:** https://bind9.readthedocs.io/
   version: 9.20-24.10_edge
   author: Christian Lempa
   date: '2025-10-02'
   tags:
-    - swarm
-    - volume_modes
-  next_steps: |
-    1. Start the DNS server:
-       docker compose up -d
-
-    2. View the auto-generated TSIG key:
-       cat config/tsig.key
-
-    3. Test DNS queries:
-       dig @localhost home.arpa
-
-    4. Customize your zone:
-       - Edit config/named.conf.zones to add more zones
-       - Add zone files to /var/lib/bind/ volume
-       - Update config/named.conf to adjust forwarders and ACLs
-
-    5. Reload configuration after changes:
-       docker exec bind9 rndc reload
-
-    6. Check BIND9 configuration syntax:
-       docker exec bind9 named-checkconf /etc/bind/named.conf
-       docker exec bind9 named-checkzone home.arpa /var/lib/bind/db.home.arpa
-
-    For more information, visit: https://bind9.readthedocs.io/
+    - network
+    - volume
+schema: "1.2"
 spec:
   general:
     vars:
       service_name:
         default: bind9
-      container_name:
-        default: bind9
-      container_hostname:
-        default: ns1
-      bind9_version:
-        type: str
-        description: BIND9 Docker image tag
-        default: "9.20-24.10_edge"
+  dns_zone:
+    vars:
       domain_name:
         type: str
-        description: "Primary domain name for your zone (e.g., home.arpa)"
+        description: "Primary domain name"
         default: "home.arpa"
-      tsig_key_name:
-        type: str
-        description: "TSIG key name for secure zone transfers"
-        default: "transfer-key"
+      tsig_enabled:
+        type: bool
+        description: "Enable TSIG"
       tsig_key_secret:
         type: str
-        description: "TSIG key secret (base64, auto-generated if empty)"
-        default: ""
+        description: "TSIG key secret"
+        needs: ["tsig_enabled=true"]
         sensitive: true
         autogenerated: true
+  dns_security:
+    vars:
+      dnssec_enabled:
+        type: bool
+        description: "Enable DNSSEC"

+ 1 - 0
library/compose/checkmk/.env.j2

@@ -0,0 +1 @@
+CMK_PASSWORD={{ cmk_password }}

+ 10 - 49
library/compose/checkmk/compose.yaml.j2

@@ -1,57 +1,27 @@
+---
 services:
   {{ service_name }}:
-    image: checkmk/check-mk-raw:2.4.0-{{ monitoring_version }}
-    container_name: {{ container_name }}
-    {#
-      Set container hostname for Checkmk identification
-    #}
-    hostname: {{ container_hostname }}
-    {#
-      Environment variables for Checkmk configuration:
-      - TZ: Timezone for proper log timestamps
-      - CMK_PASSWORD: Admin password for web interface
-      - CMK_SITE_ID: Site identifier for Checkmk instance
-    #}
+    image: checkmk/check-mk-raw:2.4.0-p1
+    restart: {{ restart_policy }}
     environment:
       - TZ={{ container_timezone }}
-      - CMK_PASSWORD={{ cmk_password }}
+      - CMK_PASSWORD=${CMK_PASSWORD}
       - CMK_SITE_ID={{ cmk_site_id }}
-    {#
-      Temporary filesystem for Checkmk runtime files
-    #}
     tmpfs:
       - /opt/omd/sites/{{ cmk_site_id }}/tmp:uid={{ user_uid }},gid={{ user_gid }}
-    {#
-      Volume configuration:
-      - /etc/localtime: System time synchronization
-      - data: Persistent storage for Checkmk sites and configuration
-    #}
     volumes:
       - /etc/localtime:/etc/localtime:ro
-      - data:/omd/sites:rw
-    {#
-      When traefik is enabled, add traefik network for reverse proxy access
-    #}
+      - {{ service_name }}_data:/omd/sites:rw
     {% if traefik_enabled %}
     networks:
       {{ traefik_network }}:
     {% endif %}
-    {#
-      Port mappings (only when Traefik is disabled):
-      - HTTP: Web interface
-      - Agent: Checkmk agent communication
-      - SNMP: SNMP trap receiver
-    #}
-    {% if not traefik_enabled %}
     ports:
+    {% if not traefik_enabled %}
       - "{{ ports_http }}:8000"
+    {% endif %}
       - "{{ ports_agent }}:5000"
       - "{{ ports_snmp }}:162/udp"
-    {% endif %}
-    {#
-      When traefik_enabled is set, add traefik labels
-      (optionally enable TLS if traefik_tls_enabled is set)
-    #}
     {% if traefik_enabled %}
     labels:
       - traefik.enable=true
@@ -60,29 +30,20 @@ services:
       - 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_domain }}`)
-      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-http.entrypoints=web
       {% 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_domain }}`)
-      - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-https.entrypoints=websecure
       - traefik.http.routers.{{ service_name }}-https.tls=true
       - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
     {% endif %}
-    restart: {{ restart_policy }}
 
-{#
-  Volume definitions:
-  - data: Persistent storage for Checkmk sites and configuration
-#}
 volumes:
-  data:
+  {{ service_name }}_data:
     driver: local
 
-{#
-  Network definitions (only when Traefik is enabled):
-  - Traefik network: always external (managed by Traefik)
-#}
 {% if traefik_enabled %}
 networks:
   {{ traefik_network }}:

+ 9 - 23
library/compose/checkmk/template.yaml

@@ -1,61 +1,47 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   name: Checkmk
-  description: >
+  description: |-
     Checkmk is a comprehensive IT monitoring solution that provides real-time insights
     into the health and performance of your infrastructure, applications, and services.
     It offers a wide range of monitoring capabilities, including server, network, cloud,
     and application monitoring, with an emphasis on ease of use and scalability.
-
-
-    Project: https://checkmk.com/
-
-    Documentation: https://docs.checkmk.com/latest/en/
-
-    GitHub: https://github.com/tribe29/checkmk
+    ## References
+    * **Project:** https://checkmk.com/
+    * **Documentation:** https://docs.checkmk.com/latest/en/
+    * **GitHub:** https://github.com/tribe29/checkmk
   version: 2.4.0-latest
   author: Christian Lempa
   date: '2025-09-28'
   tags:
     - traefik
+schema: "1.2"
 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
+        required: true
       cmk_site_id:
         type: str
         description: CheckMK site ID
         default: cmk
+        required: true
   traefik:
     vars:
       traefik_host:
-        default: checkmk.home.arpa
+        default: checkmk
   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

+ 9 - 41
library/compose/dockge/compose.yaml.j2

@@ -1,47 +1,23 @@
+---
 services:
   {{ service_name }}:
-    image: docker.io/louislam/dockge:{{ dockge_version }}
-    container_name: {{ container_name }}
-    {#
-      Set container hostname for Dockge identification
-    #}
-    hostname: {{ container_hostname }}
-    {#
-      Environment variables for Dockge configuration:
-      - TZ: Timezone for proper log timestamps
-      - DOCKGE_STACKS_DIR: Directory where Docker Compose stacks are stored
-    #}
+    image: docker.io/louislam/dockge:1.5.0
+    restart: {{ restart_policy }}
     environment:
       - TZ={{ container_timezone }}
       - DOCKGE_STACKS_DIR={{ stacks_path }}
-    {#
-      Volume configuration:
-      - Docker socket: Required for managing Docker containers (read-only)
-      - dockge-data: Persistent storage for Dockge configuration
-      - stacks_path: Directory for Docker Compose stacks management
-    #}
     volumes:
       - /var/run/docker.sock:/var/run/docker.sock:ro
-      - dockge-data:/app/data
+      - {{ service_name }}_data:/app/data
       - {{ stacks_path }}:{{ stacks_path }}
-    {#
-      When traefik is enabled, add traefik network for reverse proxy access
-    #}
     {% if traefik_enabled %}
     networks:
-      {{ traefik_network }}:
+      - {{ traefik_network }}
     {% endif %}
-    {#
-      Port mappings for web interface (only when Traefik is disabled)
-    #}
     {% if not traefik_enabled %}
     ports:
       - "{{ ports_http }}:5001"
     {% endif %}
-    {#
-      When traefik_enabled is set, add traefik labels
-      (optionally enable TLS if traefik_tls_enabled is set)
-    #}
     {% if traefik_enabled %}
     labels:
       - traefik.enable=true
@@ -49,29 +25,21 @@ services:
       - 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_domain }}`)
-      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-http.entrypoints=web
       {% 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_domain }}`)
-      - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-https.entrypoints=websecure
       - traefik.http.routers.{{ service_name }}-https.tls=true
       - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
     {% endif %}
-    restart: {{ restart_policy }}
 
-{#
-  Volume definitions:
-  - dockge-data: Persistent storage for Dockge configuration and data
-#}
+
 volumes:
-  dockge-data:
+  {{ service_name }}_data:
     driver: local
 
-{#
-  Network definitions (only when Traefik is enabled):
-  - Traefik network: always external (managed by Traefik)
-#}
 {% if traefik_enabled %}
 networks:
   {{ traefik_network }}:

+ 9 - 22
library/compose/dockge/template.yaml

@@ -1,48 +1,35 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   name: Dockge
-  description: >
+  description: |-
     Dockge is a powerful Docker management platform that simplifies container orchestration and monitoring.
     It provides an intuitive web interface to manage Docker containers, images, networks, and volumes with ease.
-
-    Project: https://dockge.com/
-
-    Documentation: https://docs.dockge.com/
-
-    GitHub: https://github.com/dockge/dockge
+    ## Prerequisites
+    * **Project:** https://dockge.com/
+    * **Documentation:** https://docs.dockge.com/
+    * **GitHub:** https://github.com/dockge/dockge
   version: 1.5.0
   author: Christian Lempa
   date: '2025-09-28'
   tags:
     - traefik
+schema: "1.2"
 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
+        description: Docker Compose Path
         default: /opt/stacks
-  network:
-    vars:
-      network_name:
-        default: "dockge_network"
+        required: true
   traefik:
     vars:
       traefik_host:
-        default: dockge.home.arpa
+        default: dockge
   ports:
     vars:
       ports_http:
-        description: Web UI port
-        type: int
         default: 5001

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

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

+ 32 - 129
library/compose/gitea/compose.yaml.j2

@@ -1,24 +1,7 @@
 services:
   {{ service_name }}:
     image: docker.io/gitea/gitea:1.25.1
-    {#
-      If not in swarm mode, check whether container_name is set and apply restart policy,
-      else swarm mode handles restarts via deploy.restart_policy
-    #}
-    {% if not swarm_enabled %}
     restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    {#
-      Set container hostname for identification
-    #}
-    hostname: {{ container_hostname }}
-    {#
-      Environment variables for Gitea configuration:
-      - User/group IDs for file permissions
-      - PostgreSQL database connection settings
-      - SSH port and root URL for clone operations
-    #}
     environment:
       - TZ={{ container_timezone }}
       - USER_UID={{ user_uid }}
@@ -28,122 +11,49 @@ services:
       - 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 }}
-    {#
-      When traefik is enabled, add traefik network for reverse proxy access
-    #}
-    {% if traefik_enabled %}
+      - GITEA__server__SSH_PORT={{ ports_ssh }}
+      {% if not traefik_enabled %}
+      - GITEA__server__ROOT_URL={{ gitea_url }}
+      {% else %}
+      - GITEA__server__ROOT_URL=https://{{ traefik_host }}.{{ traefik_domain }}
+      {% endif %}
+    {% if not database_external or traefik_enabled %}
     networks:
-      {{ traefik_network }}:
+    {% if not database_external %}
+      - {{ service_name }}_backend
+    {% endif %}
+    {% if traefik_enabled %}
+      - {{ traefik_network }}
+    {% endif %}
     {% endif %}
-    {#
-      Port mappings:
-      - HTTP: Web interface (only if Traefik is disabled)
-      - SSH: Git SSH access (always exposed, cannot be proxied)
-      Note: Swarm mode uses 'host' mode for port publishing to avoid port conflicts
-    #}
     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"
-    {#
-      Volume configuration:
-      - gitea-data: Persistent storage for repositories and configuration
-      - System time synchronization files
-    #}
     volumes:
-      - gitea-data:/data
+      - {{ service_name }}_data:/data
       - /etc/timezone:/etc/timezone:ro
       - /etc/localtime:/etc/localtime:ro
-    {#
-      When traefik_enabled is set, and not running in swarm mode, add traefik labels
-      (optionally enable TLS if traefik_tls_enabled is set)
-    #}
-    {% if traefik_enabled and not swarm_enabled %}
+    {% 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_domain }}`)
-      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-http.entrypoints=web
       {% 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_domain }}`)
-      - traefik.http.routers.{{ service_name }}-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-https.entrypoints=websecure
       - traefik.http.routers.{{ service_name }}-https.tls=true
       - traefik.http.routers.{{ service_name }}-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
     {% endif %}
+    {% if not database_external %}
     depends_on:
       - {{ service_name }}-postgres
-    {#
-      Deploy configuration for Swarm mode and/or resource limits:
-      - Swarm: Configure replicas, placement constraints, and restart policy
-      - Resources: Set CPU/memory limits (and reservations in Swarm mode)
-      - Traefik: Labels for reverse proxy integration (Swarm mode)
-    #}
-    {% if swarm_enabled or resources_enabled %}
-    deploy:
-      {% if swarm_enabled %}
-      mode: {{ swarm_placement_mode }}
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      {% endif %}
-      {% if swarm_placement_host %}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif %}
-      restart_policy:
-        condition: on-failure
-      {% endif %}
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        {% if swarm_enabled %}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-        {% endif %}
-      {% endif %}
-      {#
-        When traefik_enabled is set in swarm mode, add traefik labels
-        (optionally enable TLS if traefik_tls_enabled is set, and authentik middleware if enabled)
-      #}
-      {% if swarm_enabled and 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_domain }}`)
-        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
-        {% if authentik_enabled %}
-        - traefik.http.routers.{{ service_name }}-http.middlewares={{ authentik_traefik_middleware }}
-        {% endif %}
-        {% 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_domain }}`)
-        - 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 }}
-        {% if authentik_enabled %}
-        - traefik.http.routers.{{ service_name }}-https.middlewares={{ authentik_traefik_middleware }}
-        {% endif %}
-        {% endif %}
-      {% endif %}
     {% endif %}
 
   {#
@@ -153,47 +63,34 @@ services:
   {% if not database_external %}
   {{ service_name }}-postgres:
     image: docker.io/library/postgres:17.6
-    {#
-      If not in swarm mode, apply restart policy and container name
-    #}
-    {% if not swarm_enabled %}
     restart: {{ restart_policy }}
-    container_name: {{ service_name }}-db
-    {% endif %}
-    {#
-      Environment variables for PostgreSQL configuration
-    #}
     environment:
       - TZ={{ container_timezone }}
       - POSTGRES_USER={{ database_user }}
       - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
       - POSTGRES_DB={{ database_name }}
-    {#
-      Health check: Verify PostgreSQL is ready to accept connections
-    #}
+    networks:
+      - {{ service_name }}_backend
     healthcheck:
       test: ["CMD-SHELL", "pg_isready -U {{ database_user }}"]
       start_period: 30s
       interval: 10s
       timeout: 10s
       retries: 5
-    {#
-      Volume configuration for PostgreSQL data persistence
-    #}
     volumes:
-      - gitea-db:/var/lib/postgresql/data
+      - {{ service_name }}_postgres:/var/lib/postgresql/data
   {% endif %}
 
 {#
   Volume definitions:
-  - gitea-data: Persistent storage for repositories and configuration
-  - gitea-db: PostgreSQL database storage (only if not using external database)
+  - {{ service_name }}_data: Persistent storage for repositories and configuration
+  - {{ service_name }}_postgres: PostgreSQL database storage (only if not using external database)
 #}
 volumes:
-  gitea-data:
+  {{ service_name }}_data:
     driver: local
   {% if not database_external %}
-  gitea-db:
+  {{ service_name }}_postgres:
     driver: local
   {% endif %}
 
@@ -201,8 +98,14 @@ volumes:
   Network definitions (only when Traefik is enabled):
   - Traefik network: always external (managed by Traefik)
 #}
-{% if traefik_enabled %}
+{% if not database_external or traefik_enabled %}
 networks:
+  {% if not database_external %}
+  {{ service_name }}_backend:
+    driver: bridge
+  {% endif %}
+  {% if traefik_enabled %}
   {{ traefik_network }}:
     external: true
+  {% endif %}
 {% endif %}

+ 12 - 31
library/compose/gitea/template.yaml

@@ -1,35 +1,32 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   name: Gitea
-  description: >
+  description: |-
     Self-hosted Git service with web interface. Gitea is a painless, self-hosted Git service
     written in Go. It's similar to GitHub, Bitbucket, and GitLab, providing Git repository
     hosting, code review, team collaboration, and more.
-
-
-    Project: https://gitea.io/
-
-    Documentation: https://docs.gitea.io/
-
-    GitHub: https://github.com/go-gitea/gitea
+    ## Prerequisites
+    * **Project:** https://gitea.io/
+    * **Documentation:** https://docs.gitea.io/
+    * **GitHub:** https://github.com/go-gitea/gitea
   version: 1.25.1
   author: Christian Lempa
   date: '2025-11-05'
   tags:
     - traefik
-    - swarm
-    - authentik
+schema: "1.2"
 spec:
   general:
     vars:
       service_name:
         default: "gitea"
-      container_name:
-        default: "gitea"
+      gitea_url:
+        description: "Public URL"
+        type: str
+        needs: ["traefik_enabled=false"]
+        default: "https://git.example.com"
   database:
-    required: true
     vars:
       database_type:
         default: "postgres"
@@ -40,26 +37,10 @@ spec:
   ports:
     vars:
       ports_http:
-        description: "Host port for HTTP web interface"
-        type: int
         default: 3000
       ports_ssh:
-        description: "Host port for SSH Git access"
-        type: int
         default: 2221
   traefik:
     vars:
       traefik_host:
-        default: gitea.home.arpa
-  gitea:
-    description: "Configure Gitea application settings"
-    required: true
-    vars:
-      gitea_root_url:
-        description: "Public URL for your Gitea instance (e.g., https://git.example.com)"
-        type: str
-        default: "https://git.example.com"
-      gitea_ssh_port:
-        description: "SSH port number (should match ports_ssh)"
-        type: int
-        default: 2221
+        default: gitea

+ 0 - 4
library/compose/grafana/.env.j2

@@ -1,12 +1,8 @@
-{% if not swarm_enabled %}
 {% if database_type == 'postgres' %}
-# Database Configuration
 GRAFANA_DB_PASSWORD={{ database_password }}
 {% endif %}
-
 {% if authentik_enabled %}
 # OAuth Configuration
 GRAFANA_OAUTH_CLIENT_ID={{ authentik_client_id }}
 GRAFANA_OAUTH_CLIENT_SECRET={{ authentik_client_secret }}
 {% endif %}
-{% endif %}

+ 19 - 171
library/compose/grafana/compose.yaml.j2

@@ -1,50 +1,28 @@
 services:
-  {{ service_name }}:
+  {{ service_name }}_
     image: docker.io/grafana/grafana-oss:12.1.1
-    {#
-      If not in swarm mode, check whether container_name is set and apply restart policy,
-      else swarm mode handles restarts via deploy.restart_policy
-    #}
-    {% if not swarm_enabled %}
     restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    {#
-      Set container hostname for identification
-    #}
-    hostname: {{ container_hostname }}
-    {#
-      Environment variables for Grafana configuration:
-      - User/group IDs for file permissions
-      - PostgreSQL database connection (if enabled)
-      - Authentik OAuth integration (if enabled)
-    #}
     environment:
       - TZ={{ container_timezone }}
       - UID={{ user_uid }}
       - GID={{ user_gid }}
       {% if database_type == 'postgres' %}
       - GF_DATABASE_TYPE=postgres
+      {% if database_external %}
       - GF_DATABASE_HOST={{ database_host }}
+      {% else %}
+      - GF_DATABASE_HOST={{ service_name }}_postgres:5432
+      {% endif %}
       - GF_DATABASE_NAME={{ database_name }}
       - GF_DATABASE_USER={{ database_user }}
-      {% if swarm_enabled %}
-      - GF_DATABASE_PASSWORD__FILE=/run/secrets/{{ service_name }}_db_password
-      {% else %}
       - GF_DATABASE_PASSWORD=${GRAFANA_DB_PASSWORD}
-      {% endif %}
       - GF_DATABASE_SSL_MODE=disable
       {% endif %}
       {% if authentik_enabled %}
       - GF_AUTH_GENERIC_OAUTH_ENABLED=true
       - GF_AUTH_GENERIC_OAUTH_NAME={{ authentik_slug }}
-      {% if swarm_enabled %}
-      - GF_AUTH_GENERIC_OAUTH_CLIENT_ID__FILE=/run/secrets/{{ service_name }}_oauth_client_id
-      - GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET__FILE=/run/secrets/{{ service_name }}_oauth_client_secret
-      {% else %}
       - GF_AUTH_GENERIC_OAUTH_CLIENT_ID=${GRAFANA_OAUTH_CLIENT_ID}
       - GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=${GRAFANA_OAUTH_CLIENT_SECRET}
-      {% endif %}
       - GF_AUTH_GENERIC_OAUTH_SCOPES=openid profile email
       - GF_AUTH_GENERIC_OAUTH_AUTH_URL={{ authentik_url }}/application/o/authorize/
       - GF_AUTH_GENERIC_OAUTH_TOKEN_URL={{ authentik_url }}/application/o/token/
@@ -52,174 +30,44 @@ services:
       - GF_AUTH_SIGNOUT_REDIRECT_URL={{ authentik_url }}/application/o/{{ authentik_slug }}/end-session/
       - GF_AUTH_OAUTH_AUTO_LOGIN=true
       {% if traefik_enabled %}
-      - GF_SERVER_ROOT_URL=https://{{ traefik_host }}
+      - GF_SERVER_ROOT_URL=https://{{ traefik_host }}.{{ traefik_domain }}
       {% endif %}
       - GF_AUTH_OAUTH_ALLOW_INSECURE_EMAIL_LOOKUP=true
       - GF_AUTH_GENERIC_OAUTH_SKIP_ORG_ROLE_SYNC=true
       {% endif %}
-    {#
-      When traefik is enabled, add traefik network for reverse proxy access
-    #}
     {% if traefik_enabled %}
     networks:
       {{ traefik_network }}:
     {% endif %}
-    {#
-      Port mappings for web interface (only when Traefik is disabled)
-      Note: Swarm mode uses 'host' mode for port publishing to avoid port conflicts
-    #}
     {% if not traefik_enabled %}
     ports:
-      {% if swarm_enabled %}
-      - target: 3000
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      {% else %}
       - "{{ ports_http }}:3000"
-      {% endif %}
     {% endif %}
-    {#
-      Volume configuration for persistent data
-    #}
     volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/data:/var/lib/grafana:rw
-      {% else %}
-      - {{ service_name }}-data:/var/lib/grafana
-      {% endif %}
-    {#
-      When swarm_enabled is set, use Docker secrets for sensitive data
-    #}
-    {% if swarm_enabled %}
-    secrets:
-      {% if database_type == 'postgres' %}
-      - {{ service_name }}_db_password
-      {% endif %}
-      {% if authentik_enabled %}
-      - {{ service_name }}_oauth_client_id
-      - {{ service_name }}_oauth_client_secret
-      {% endif %}
-    {% endif %}
-    {#
-      When traefik_enabled is set, and not running in swarm mode, add traefik labels
-      (optionally enable TLS if traefik_tls_enabled is set)
-    #}
+      - {{ service_name }}_data:/var/lib/grafana
     {% if traefik_enabled and not swarm_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_domain }}`)
-      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+      - 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_domain }}`)
+      - traefik.http.routers.{{ service_name }}_http.entrypoints=web
       {% 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_domain }}`)
-      - 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 %}
-    {#
-      Deploy configuration for Swarm mode and/or resource limits:
-      - Swarm: Configure replicas, placement constraints, and restart policy
-      - Resources: Set CPU/memory limits (and reservations in Swarm mode)
-      - Traefik: Labels for reverse proxy integration (Swarm mode)
-    #}
-    {% if swarm_enabled or resources_enabled %}
-    deploy:
-      {% if swarm_enabled %}
-      mode: {{ swarm_placement_mode }}
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      {% endif %}
-      {% if swarm_placement_host %}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif %}
-      restart_policy:
-        condition: on-failure
-      {% endif %}
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        {% if swarm_enabled %}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-        {% endif %}
-      {% endif %}
-      {#
-        When traefik_enabled is set in swarm mode, add traefik labels
-        (optionally enable TLS if traefik_tls_enabled is set)
-      #}
-      {% if swarm_enabled and 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_domain }}`)
-        - 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_domain }}`)
-        - 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 %}
+      - traefik.http.routers.{{ service_name }}_https.service={{ service_name }}_web
+      - traefik.http.routers.{{ service_name }}_https.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
+      - traefik.http.routers.{{ service_name }}_https.entrypoints=websecure
+      - traefik.http.routers.{{ service_name }}_https.tls=true
+      - traefik.http.routers.{{ service_name }}_https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
     {% endif %}
 
-{#
-  Volume definitions:
-  - When volume_mode is 'local' (default): use docker-managed local volumes
-  - When volume_mode is 'nfs': configure NFS-backed volumes
-  - When volume_mode is 'mount': no volume definition needed (bind mounts used directly)
-#}
-{% if volume_mode == 'local' %}
-volumes:
-  {{ service_name }}-data:
-    driver: local
-{% elif volume_mode == 'nfs' %}
-volumes:
-  {{ service_name }}-data:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/data"
-{% endif %}
-
-{#
-  Network definitions (only when Traefik is enabled):
-  - Traefik network: always external (managed by Traefik)
-#}
 {% if traefik_enabled %}
 networks:
   {{ traefik_network }}:
     external: true
 {% endif %}
 
-{#
-  Docker Swarm secrets (only when swarm_enabled is set):
-  - Database password for PostgreSQL (if using external database)
-  - OAuth credentials for Authentik integration (if enabled)
-#}
-{% if swarm_enabled %}
-secrets:
-  {% if database_type == 'postgres' %}
-  {{ service_name }}_db_password:
-    file: ./.env.secret.grafana_db_password
-  {% endif %}
-  {% if authentik_enabled %}
-  {{ service_name }}_oauth_client_id:
-    file: ./.env.secret.grafana_oauth_client_id
-  {{ service_name }}_oauth_client_secret:
-    file: ./.env.secret.grafana_oauth_client_secret
-  {% endif %}
-{% endif %}
+volumes:
+  {{ service_name }}_data:
+    driver: local

+ 7 - 18
library/compose/grafana/template.yaml

@@ -1,46 +1,35 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   name: Grafana
-  description: >
+  description: |-
     Grafana is an open-source platform for monitoring and observability that allows you to visualize and analyze metrics, logs, and traces from various data
     sources. It provides a powerful and flexible dashboarding solution for IT infrastructure and application monitoring.
-
-
-    Project: https://grafana.com/
-
-    Documentation: https://grafana.com/docs/grafana/latest/
-
-    GitHub: https://github.com/grafana/grafana
+    ## Prerequisites
+    * **Project:** https://grafana.com/
+    * **Documentation:** https://grafana.com/docs/grafana/latest/
+    * **GitHub:** https://github.com/grafana/grafana
   version: 12.1.1
   author: Christian Lempa
   date: '2025-09-28'
   tags:
     - traefik
-    - swarm
     - authentik
-    - volume_modes
+schema: "1.2"
 spec:
   general:
     vars:
       service_name:
         default: "grafana"
-      container_name:
-        default: "grafana"
   ports:
     vars:
       ports_http:
-        description: "Host port for HTTP (3000)"
-        type: int
         default: 3000
   database:
     vars:
       database_type:
-        description: "Database backend type"
         options: ["sqlite", "postgres"]
         default: "sqlite"
-        extra: "sqlite (default) or postgres for external database"
       database_host:
         default: "postgres:5432"
         needs: "database_type=postgres"
@@ -61,4 +50,4 @@ spec:
   traefik:
     vars:
       traefik_host:
-        default: grafana.home.arpa
+        default: grafana

+ 0 - 26
library/compose/heimdall/compose.yaml.j2

@@ -1,26 +0,0 @@
-{#
-  Heimdall: Application dashboard
-  Organizes and provides quick access to web applications
-#}
-services:
-  {{ service_name }}:
-    image: lscr.io/linuxserver/heimdall:2.7.6
-    container_name: {{ container_name }}
-    {#
-      Environment variables for user/group permissions
-    #}
-    environment:
-      - PUID=1000
-      - PGID=1000
-    {#
-      Volume configuration for persistent data
-    #}
-    volumes:
-      - ./heimdall/config:/config
-    {#
-      Port mappings for HTTP and HTTPS access
-    #}
-    ports:
-      - 80:80
-      - 443:443
-    restart: unless-stopped

+ 0 - 28
library/compose/heimdall/template.yaml

@@ -1,28 +0,0 @@
----
-kind: compose
-schema: "1.2"
-metadata:
-  name: Heimdall
-  description: >
-    Heimdall is a simple, fast, and secure web-based application dashboard for organizing and accessing your web applications.
-    It provides a visually appealing interface to manage and launch your favorite web apps from a single location.
-
-
-    Project: https://heimdall.site/
-
-    Documentation: https://docs.heimdall.site/
-  version: 2.7.6
-  author: Christian Lempa
-  date: '2025-10-31'
-  tags: []
-spec:
-  general:
-    vars:
-      service_name:
-        default: heimdall
-      container_name:
-        default: heimdall
-      heimdall_version:
-        type: str
-        description: Heimdall version
-        default: latest

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

@@ -15,6 +15,7 @@ metadata:
   author: Christian Lempa
   date: '2025-11-07'
   tags: []
+  draft: true
 spec:
   general:
     vars:

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

@@ -17,6 +17,7 @@ metadata:
     - traefik
     - swarm
     - volume_modes
+  draft: true
 spec:
   general:
     vars:

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

@@ -17,6 +17,7 @@ metadata:
     - traefik
     - swarm
     - authentik
+  draft: true
   next_steps: |
     1. Start the Homer dashboard:
        docker compose up -d

+ 8 - 99
library/compose/influxdb/compose.yaml.j2

@@ -1,137 +1,46 @@
+---
 services:
   {{ service_name }}:
     image: docker.io/library/influxdb:2.7.12-alpine
-    {#
-      If not in swarm mode, apply restart policy and container name
-    #}
-    {% if not swarm_enabled %}
     restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    {#
-      Set container hostname for identification
-    #}
-    hostname: {{ container_hostname }}
-    {#
-      Environment variables for InfluxDB initialization:
-      - Initial admin credentials, organization, and bucket
-      - Optional retention period and admin token
-    #}
     environment:
       - TZ={{ container_timezone }}
       - DOCKER_INFLUXDB_INIT_MODE=setup
       - 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}
-      {% endif %}
-    {#
-      When traefik is enabled, add traefik network for reverse proxy access
-    #}
     {% if traefik_enabled %}
     networks:
       {{ traefik_network }}:
     {% endif %}
-    {#
-      Port mappings for HTTP API (only when Traefik is disabled)
-      Note: Swarm mode uses 'host' mode for port publishing to avoid port conflicts
-    #}
     {% if not traefik_enabled %}
     ports:
-      {% if swarm_enabled %}
-      - target: 8086
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      {% else %}
       - "{{ ports_http }}:8086"
-      {% endif %}
     {% endif %}
-    {#
-      Volume configuration for persistent data
-    #}
     volumes:
-      - influxdb-data:/var/lib/influxdb2
-      - /etc/influxdb2:/etc/influxdb2
-    {% if traefik_enabled and not swarm_enabled %}
+      - {{ service_name }}_data:/var/lib/influxdb2
+      - /etc/influxdb2:/etc/influxdb2  # FIXME: WHAT IS THAT?
+    {% 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_domain }}`)
-      - traefik.http.routers.{{ service_name }}-web-http.entrypoints={{ traefik_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-web-http.entrypoints=web
       - 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_domain }}`)
-      - traefik.http.routers.{{ service_name }}-web-https.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}-web-https.entrypoints=websecure
       - 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 or resources_enabled %}
-    deploy:
-      {% if swarm_enabled %}
-      mode: {{ swarm_placement_mode }}
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      {% endif %}
-      {% if swarm_placement_host %}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif %}
-      restart_policy:
-        condition: on-failure
-      {% endif %}
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        {% if swarm_enabled %}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-        {% endif %}
-      {% endif %}
-      {% 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_domain }}`)
-        - 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_domain }}`)
-        - 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 %}
-
-{#
-  Volume definitions:
-  - influxdb-data: Persistent storage for InfluxDB time series data
-#}
+    
 volumes:
-  influxdb-data:
+  {{ service_name }}_data:
     driver: local
 
-{#
-  Network definitions (only when Traefik is enabled):
-  - Traefik network: always external (managed by Traefik)
-#}
 {% if traefik_enabled %}
 networks:
   {{ traefik_network }}:

+ 14 - 28
library/compose/influxdb/template.yaml

@@ -1,24 +1,26 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   name: Influxdb
-  description: >
+  description: |-
     InfluxDB is a powerful, open-source time series database designed for high-performance handling of time-stamped data.
     It is commonly used for monitoring, analytics, and IoT applications.
-
-
-    Project: https://www.influxdata.com/
-
-    Documentation: https://docs.influxdata.com/influxdb/
-
-    GitHub: https://github.com/influxdata/influxdb
+    ## References
+    * **Project:** https://www.influxdata.com/
+    * **Documentation:** https://docs.influxdata.com/influxdb/
+    * **GitHub:** https://github.com/influxdata/influxdb
+  next_steps: |-
+    Log in with your initial admin user:
+    ```bash
+    Username: {{ influxdb_init_username }}
+    Password: {{ influxdb_init_password }}
+    ```
   version: 2.7.12-alpine
   author: Christian Lempa
   date: '2025-09-28'
   tags:
     - traefik
-    - swarm
+schema: "1.2"
 spec:
   ports:
     vars:
@@ -34,29 +36,13 @@ spec:
         description: "Initial admin username"
         type: str
         default: "admin"
+        required: true
       influxdb_init_password:
         description: "Initial admin password"
         type: str
         sensitive: true
-        default: "changeme"
-      influxdb_init_org:
-        description: "Initial organization name"
-        type: str
-        default: "myorg"
-      influxdb_init_bucket:
-        description: "Initial bucket name"
-        type: str
-        default: "mybucket"
-      influxdb_init_retention:
-        description: "Data retention period (e.g., 30d, 1y, 0 for infinite)"
-        type: str
-        default: ""
-      influxdb_init_token:
-        description: "Initial admin API token (leave empty to auto-generate)"
-        type: str
-        sensitive: true
         autogenerated: true
-        default: ""
+        required: true
   general:
     vars:
       influxdb_version:

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

@@ -16,6 +16,7 @@ metadata:
     * **Project:** https://github.com/moghtech/komodo
     * **Documentation:** https://github.com/moghtech/komodo/tree/main/docsite/docs
     * **Docker Hub:** https://hub.docker.com/r/moghtech/komodo
+  draft: true
   version: latest
   author: Christian Lempa
   date: '2025-11-13'

+ 18 - 144
library/compose/loki/compose.yaml.j2

@@ -1,175 +1,49 @@
+---
 services:
   {{ service_name }}:
     image: docker.io/grafana/loki:3.5.8
-    {% if not swarm_enabled %}
     restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
     command: "-config.file=/etc/loki/config.yaml"
-    environment:
-      - TZ={{ container_timezone }}
-      - UID={{ user_uid }}
-      - GID={{ user_gid }}
-    {% if not traefik_enabled and network_mode == 'bridge' %}
+    {% if not traefik_enabled %}
     ports:
-      {% if swarm_enabled %}
-      - target: 3100
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      {% else %}
       - "{{ ports_http }}:3100"
-      {% endif %}
     {% endif %}
     volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/data:/loki:rw
-      {% elif volume_mode in ['local', 'nfs'] %}
-      - {{ service_name }}-data:/loki:rw
-      {% endif %}
-      {% if not swarm_enabled %}
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/config/config.yaml:/etc/loki/config.yaml:ro
-      {% else %}
+      - {{ service_name }}_data:/loki:rw
       - ./config/config.yaml:/etc/loki/config.yaml:ro
-      {% endif %}
-      {% endif %}
-    {% if network_mode == 'host' %}
-    network_mode: host
-    {% else %}
+    {% if traefik_enabled %}
     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 swarm_enabled %}
-    configs:
-      - source: {{ service_name }}_config
-        target: /etc/loki/config.yaml
-    {% endif %}
-    {% if swarm_enabled or resources_enabled %}
-    deploy:
-      {% if swarm_enabled %}
-      mode: {{ swarm_placement_mode }}
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      {% endif %}
-      {% if swarm_placement_host %}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif %}
-      restart_policy:
-        condition: on-failure
-      {% endif %}
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        {% if swarm_enabled %}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-        {% endif %}
-      {% endif %}
-      {% if swarm_enabled and traefik_enabled %}
-      labels:
-        - traefik.enable=true
-        - traefik.docker.network={{ traefik_network }}
-        - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=3100
-        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
-        - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
-        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
-        {% if authentik_enabled %}
-        - traefik.http.routers.{{ service_name }}-http.middlewares={{ authentik_traefik_middleware }}
-        {% endif %}
-        {% 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_domain }}`)
-        - 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 }}
-        {% if authentik_enabled %}
-        - traefik.http.routers.{{ service_name }}-https.middlewares={{ authentik_traefik_middleware }}
-        {% endif %}
-        {% endif %}
-      {% endif %}
     {% endif %}
-    {% if traefik_enabled and not swarm_enabled %}
+    {% if traefik_enabled %}
     labels:
       - traefik.enable=true
       - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=3100
-      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
-      - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
-      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
+      - traefik.http.services.{{ service_name }}_web.loadBalancer.server.port=3100
+      - traefik.http.routers.{{ service_name }}_http.service={{ service_name }}_web
+      - traefik.http.routers.{{ service_name }}_http.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
+      - traefik.http.routers.{{ service_name }}_http.entrypoints=web
       {% if authentik_enabled %}
-      - traefik.http.routers.{{ service_name }}-http.middlewares={{ authentik_traefik_middleware }}
+      - traefik.http.routers.{{ service_name }}_http.middlewares=authentik-headers@file
       {% endif %}
       {% 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_domain }}`)
-      - 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 }}
+      - traefik.http.routers.{{ service_name }}_https.service={{ service_name }}_web
+      - traefik.http.routers.{{ service_name }}_https.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
+      - traefik.http.routers.{{ service_name }}_https.entrypoints=websecure
+      - traefik.http.routers.{{ service_name }}_https.tls=true
+      - traefik.http.routers.{{ service_name }}_https.tls.certresolver={{ traefik_tls_certresolver }}
       {% if authentik_enabled %}
-      - traefik.http.routers.{{ service_name }}-https.middlewares={{ authentik_traefik_middleware }}
+      - traefik.http.routers.{{ service_name }}_https.middlewares=authentik-headers@file
       {% endif %}
       {% endif %}
     {% endif %}
 
 volumes:
-{% if volume_mode == 'local' %}
-  {{ service_name }}-data:
+  {{ service_name }}_data:
     driver: local
-{% elif volume_mode == 'nfs' %}
-volumes:
-  {{ service_name }}-data:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/data"
-{% endif %}
 
-{% if network_mode != 'host' %}
+{% if traefik_enabled %}
 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 %}
-
-{% if swarm_enabled %}
-configs:
-  {{ service_name }}_config:
-    file: ./config/config.yaml
 {% endif %}

+ 0 - 154
library/compose/loki/compose.yaml.j2.final

@@ -1,154 +0,0 @@
-services:
-  {{ service_name }}:
-    image: docker.io/grafana/loki:3.5.8
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
-    command: "-config.file=/etc/loki/config.yaml"
-    environment:
-      - TZ={{ container_timezone }}
-      - UID={{ user_uid }}
-      - GID={{ user_gid }}
-    {% if not traefik_enabled and network_mode == 'bridge' %}
-    ports:
-      {% if swarm_enabled %}
-      - target: 3100
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      {% else %}
-      - "{{ ports_http }}:3100"
-      {% endif %}
-    {% endif %}
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/data:/loki:rw
-      {% elif volume_mode in ['local', 'nfs'] %}
-      - {{ service_name }}-data:/loki:rw
-      {% endif %}
-      {% if not swarm_enabled %}
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/config/config.yaml:/etc/loki/config.yaml:ro
-      {% else %}
-      - ./config/config.yaml:/etc/loki/config.yaml:ro
-      {% endif %}
-      {% endif %}
-    {#
-      When traefik is enabled, add traefik network for reverse proxy access
-    #}
-    {% if traefik_enabled %}
-    networks:
-      {{ traefik_network }}:
-    {% endif %}
-      {% if network_mode == 'macvlan' %}
-      {{ network_name }}:
-        ipv4_address: {{ network_macvlan_ipv4_address }}
-      {% elif network_mode == 'bridge' %}
-      {{ network_name }}:
-      {% endif %}
-    {% endif %}
-    {% if swarm_enabled %}
-    configs:
-      - source: {{ service_name }}_config
-        target: /etc/loki/config.yaml
-    {% endif %}
-    {% if swarm_enabled or resources_enabled %}
-    deploy:
-      {% if swarm_enabled %}
-      mode: {{ swarm_placement_mode }}
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      {% endif %}
-      {% if swarm_placement_host %}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif %}
-      restart_policy:
-        condition: on-failure
-      {% endif %}
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        {% if swarm_enabled %}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-        {% endif %}
-      {% endif %}
-      {% if swarm_enabled and traefik_enabled %}
-      labels:
-        - traefik.enable=true
-        - traefik.docker.network={{ traefik_network }}
-        - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=3100
-        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
-        - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
-        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
-        {% if authentik_enabled %}
-        - traefik.http.routers.{{ service_name }}-http.middlewares={{ authentik_traefik_middleware }}
-        {% endif %}
-        {% 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_domain }}`)
-        - 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 }}
-        {% if authentik_enabled %}
-        - traefik.http.routers.{{ service_name }}-https.middlewares={{ authentik_traefik_middleware }}
-        {% endif %}
-        {% endif %}
-      {% endif %}
-    {% endif %}
-    {% if traefik_enabled and not swarm_enabled %}
-    labels:
-      - traefik.enable=true
-      - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=3100
-      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
-      - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
-      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
-      {% if authentik_enabled %}
-      - traefik.http.routers.{{ service_name }}-http.middlewares={{ authentik_traefik_middleware }}
-      {% endif %}
-      {% 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_domain }}`)
-      - 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 }}
-      {% if authentik_enabled %}
-      - traefik.http.routers.{{ service_name }}-https.middlewares={{ authentik_traefik_middleware }}
-      {% endif %}
-      {% endif %}
-    {% endif %}
-
-volumes:
-{% if volume_mode == 'local' %}
-  {{ service_name }}-data:
-    driver: local
-{% elif volume_mode == 'nfs' %}
-volumes:
-  {{ service_name }}-data:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/data"
-{% endif %}
-
-    {% endif %}
-  {% if traefik_enabled %}
-  {{ traefik_network }}:
-    external: true
-  {% endif %}
-{% endif %}
-
-{% if swarm_enabled %}
-configs:
-  {{ service_name }}_config:
-    file: ./config/config.yaml
-{% endif %}

+ 0 - 148
library/compose/loki/compose.yaml.j2.portfix

@@ -1,148 +0,0 @@
-services:
-  {{ service_name }}:
-    image: docker.io/grafana/loki:3.5.8
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
-    command: "-config.file=/etc/loki/config.yaml"
-    environment:
-      - TZ={{ container_timezone }}
-      - UID={{ user_uid }}
-      - GID={{ user_gid }}
-    {% if not traefik_enabled and network_mode == 'bridge' %}
-    ports:
-      {% if swarm_enabled %}
-      - target: 3100
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      {% else %}
-      - "{{ ports_http }}:3100"
-      {% endif %}
-    {% endif %}
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/data:/loki:rw
-      {% elif volume_mode in ['local', 'nfs'] %}
-      - {{ service_name }}-data:/loki:rw
-      {% endif %}
-      {% if not swarm_enabled %}
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/config/config.yaml:/etc/loki/config.yaml:ro
-      {% else %}
-      - ./config/config.yaml:/etc/loki/config.yaml:ro
-      {% endif %}
-      {% endif %}
-    {#
-      When traefik is enabled, add traefik network for reverse proxy access
-    #}
-    {% if traefik_enabled %}
-    networks:
-      {{ traefik_network }}:
-    {% endif %}
-    {% endif %}
-    {% if swarm_enabled %}
-    configs:
-      - source: {{ service_name }}_config
-        target: /etc/loki/config.yaml
-    {% endif %}
-    {% if swarm_enabled or resources_enabled %}
-    deploy:
-      {% if swarm_enabled %}
-      mode: {{ swarm_placement_mode }}
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      {% endif %}
-      {% if swarm_placement_host %}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif %}
-      restart_policy:
-        condition: on-failure
-      {% endif %}
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        {% if swarm_enabled %}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-        {% endif %}
-      {% endif %}
-      {% if swarm_enabled and traefik_enabled %}
-      labels:
-        - traefik.enable=true
-        - traefik.docker.network={{ traefik_network }}
-        - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=3100
-        - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
-        - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
-        - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
-        {% if authentik_enabled %}
-        - traefik.http.routers.{{ service_name }}-http.middlewares={{ authentik_traefik_middleware }}
-        {% endif %}
-        {% 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_domain }}`)
-        - 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 }}
-        {% if authentik_enabled %}
-        - traefik.http.routers.{{ service_name }}-https.middlewares={{ authentik_traefik_middleware }}
-        {% endif %}
-        {% endif %}
-      {% endif %}
-    {% endif %}
-    {% if traefik_enabled and not swarm_enabled %}
-    labels:
-      - traefik.enable=true
-      - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}-web.loadBalancer.server.port=3100
-      - traefik.http.routers.{{ service_name }}-http.service={{ service_name }}-web
-      - traefik.http.routers.{{ service_name }}-http.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
-      - traefik.http.routers.{{ service_name }}-http.entrypoints={{ traefik_entrypoint }}
-      {% if authentik_enabled %}
-      - traefik.http.routers.{{ service_name }}-http.middlewares={{ authentik_traefik_middleware }}
-      {% endif %}
-      {% 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_domain }}`)
-      - 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 }}
-      {% if authentik_enabled %}
-      - traefik.http.routers.{{ service_name }}-https.middlewares={{ authentik_traefik_middleware }}
-      {% endif %}
-      {% endif %}
-    {% endif %}
-
-volumes:
-{% if volume_mode == 'local' %}
-  {{ service_name }}-data:
-    driver: local
-{% elif volume_mode == 'nfs' %}
-volumes:
-  {{ service_name }}-data:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/data"
-{% endif %}
-
-    {% endif %}
-  {% if traefik_enabled %}
-  {{ traefik_network }}:
-    external: true
-  {% endif %}
-{% endif %}
-
-{% if swarm_enabled %}
-configs:
-  {{ service_name }}_config:
-    file: ./config/config.yaml
-{% endif %}

+ 1 - 3
library/compose/loki/config/config.yaml

@@ -27,9 +27,7 @@ schema_config:
         period: 24h
 
 limits_config:
-  # NOTE: Data Retention is set to 7 days.
-  #       This is the default value and can be changed
-  retention_period: 168h
+  retention_period: {{ data_retention_days + 'd'}}
   ingestion_rate_mb: 4
   ingestion_burst_size_mb: 6
   max_streams_per_user: 10000

+ 11 - 22
library/compose/loki/template.yaml

@@ -1,46 +1,35 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   name: Loki
-  description: >
+  description: |-
     Loki is a horizontally scalable, highly available, multi-tenant log aggregation system inspired by Prometheus.
     This template sets up Loki in a Docker container using Docker Compose.
-
-
-    Project: https://grafana.com/oss/loki/
-
-    Documentation: https://grafana.com/docs/loki/latest/
-
-    GitHub: https://github.com/grafana/loki
+    ## References
+    * **Project:** https://grafana.com/oss/loki/
+    * **Documentation:** https://grafana.com/docs/loki/latest/
+    * **GitHub:** https://github.com/grafana/loki
   version: 3.5.8
   author: Christian Lempa
   date: '2025-11-07'
   tags:
     - traefik
-    - swarm
     - authentik
+schema: "1.2"
 spec:
   general:
     vars:
       service_name:
         default: loki
-      container_name:
-        default: loki
-      container_hostname:
-        default: loki
+      data_retention_days:
+        description: "Number of days to retain logs"
+        type: int
+        default: 7
   ports:
     vars:
       ports_http:
-        description: "Loki HTTP API port"
-        type: int
         default: 3100
-        needs: ["traefik_enabled=false", "network_mode=bridge"]
-  network:
-    vars:
-      network_name:
-        default: "loki_network"
   traefik:
     vars:
       traefik_host:
-        default: "loki.home.arpa"
+        default: "loki"

+ 0 - 46
library/compose/loki/template.yaml.backup

@@ -1,46 +0,0 @@
----
-kind: compose
-schema: "1.2"
-metadata:
-  name: Loki
-  description: >
-    Loki is a horizontally scalable, highly available, multi-tenant log aggregation system inspired by Prometheus.
-    This template sets up Loki in a Docker container using Docker Compose.
-
-
-    Project: https://grafana.com/oss/loki/
-
-    Documentation: https://grafana.com/docs/loki/latest/
-
-    GitHub: https://github.com/grafana/loki
-  version: 3.5.8
-  author: Christian Lempa
-  date: '2025-11-07'
-  tags:
-    - traefik
-    - swarm
-    - authentik
-spec:
-  general:
-    vars:
-      service_name:
-        default: loki
-      container_name:
-        default: loki
-      container_hostname:
-        default: loki
-  ports:
-    vars:
-      ports_http:
-        description: "Loki HTTP API port"
-        type: int
-        default: 3100
-        needs: ["traefik_enabled=false", "network_mode=bridge"]
-  network:
-    vars:
-      network_name:
-        default: "loki_network"
-  traefik:
-    vars:
-      traefik_host:
-        default: "loki.home.arpa"

+ 1 - 0
library/compose/mariadb/.env.j2

@@ -0,0 +1 @@
+MARIADB_ROOT_PASSWORD={{ database_password }}

+ 11 - 143
library/compose/mariadb/compose.yaml.j2

@@ -1,151 +1,19 @@
+
+---
 services:
   {{ service_name }}:
-    image: docker.io/library/mariadb:{{ mariadb_version }}
-    {#
-      If not in swarm mode, apply restart policy and container_name,
-      else swarm mode handles restarts via deploy.restart_policy
-    #}
-    {% if not swarm_enabled %}
+    image: docker.io/library/mariadb:12.0.2
     restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    {#
-      Set container hostname
-    #}
-    hostname: {{ container_hostname }}
-    {#
-      Environment variables for MariaDB configuration
-      - TZ: Timezone
-      - MARIADB_ROOT_PASSWORD: Root password (set, random, or empty)
-      - MARIADB_DATABASE: Default database name
-      - MARIADB_USER: Default user name
-      - MARIADB_PASSWORD: Default user password (from env or secret)
-    #}
     environment:
-      - TZ={{ container_timezone }}
-      {% if mariadb_root_password_mode == 'set' %}
-      - MARIADB_ROOT_PASSWORD={{ mariadb_root_password }}
-      {% elif mariadb_root_password_mode == 'random' %}
-      - MARIADB_RANDOM_ROOT_PASSWORD=true
-      {% else %}
-      - MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=true
-      {% endif %}
-      - MARIADB_DATABASE={{ mariadb_database }}
-      - MARIADB_USER={{ mariadb_user }}
-      {% if swarm_enabled %}
-      - MARIADB_PASSWORD=/run/secrets/mariadb_password
-      {% else %}
-      - MARIADB_PASSWORD={{ mariadb_password }}
-      {% endif %}
-    {#
-      Network configuration:
-      - Databases typically use bridge networking for internal communication
-      - Default to bridge network if not specified
-    #}
-    networks:
-      {{ network_name }}:
-    {#
-      Port mappings (only expose if enabled):
-      - MariaDB default port 3306
-      Note: Swarm mode uses 'host' mode for port publishing
-    #}
-    {% if mariadb_port_enabled %}
+      - MARIADB_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD}
+      - MARIADB_DATABASE={{ database_name }}
+      - MARIADB_USER={{ database_user }}
+      - MARIADB_PASSWORD=${MARIADB_ROOT_PASSWORD}
     ports:
-      {% if swarm_enabled %}
-      - target: 3306
-        published: {{ mariadb_port }}
-        protocol: tcp
-        mode: host
-      {% else %}
-      - "{{ mariadb_port }}:3306"
-      {% endif %}
-    {% endif %}
-    {#
-      Volume configuration for persistent data
-      - When volume_mode is 'mount': bind mount from host path
-      - When volume_mode is 'local', 'nfs', or empty: use docker-managed volumes
-    #}
+      - "{{ ports_mariadb }}:3306"
     volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/{{ service_name }}:/var/lib/mysql:rw
-      {% else %}
-      - {{ service_name }}-data:/var/lib/mysql
-      {% endif %}
-    {#
-      Deploy configuration for Swarm mode:
-      - Single replica (MariaDB doesn't support multi-replica without replication setup)
-      - For HA, use Galera Cluster or MariaDB replication
-      - Uses Docker secrets for password management
-      - Optional resource limits and reservations
-    #}
-    {% if swarm_enabled %}
-    secrets:
-      - mariadb_password
-    deploy:
-      {% if swarm_placement_mode == 'replicated' %}
-      mode: replicated
-      replicas: {{ swarm_replicas }}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% else %}
-      mode: global
-      {% endif %}
-      restart_policy:
-        condition: on-failure
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-      {% endif %}
-    {% endif %}
-{#
-  Volume definitions:
-  - When volume_mode is 'local' (default): use docker-managed local volumes
-  - When volume_mode is 'nfs': configure NFS-backed volumes
-  - When volume_mode is 'mount': no volume definition needed (bind mounts used directly)
-#}
-{% if volume_mode == 'local' %}
-volumes:
-  {{ service_name }}-data:
-    driver: local
-{% elif volume_mode == 'nfs' %}
+      - {{ service_name }}_data:/var/lib/mysql
+
 volumes:
-  {{ service_name }}-data:
+  {{ service_name }}_data:
     driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/{{ service_name }}"
-{% endif %}
-
-{#
-  Network definitions:
-  - Bridge network for service communication
-  - Use overlay network in Swarm mode for multi-host communication
-#}
-networks:
-  {{ network_name }}:
-    {% if network_external %}
-    external: true
-    {% else %}
-    {% if swarm_enabled %}
-    driver: overlay
-    attachable: true
-    {% else %}
-    driver: bridge
-    {% endif %}
-    {% endif %}
-
-{#
-  Docker Swarm secrets (external secrets managed via docker secret create)
-#}
-{% if swarm_enabled %}
-secrets:
-  mariadb_password:
-    external: true
-{% endif %}

+ 16 - 56
library/compose/mariadb/template.yaml

@@ -3,72 +3,32 @@ kind: compose
 schema: "1.2"
 metadata:
   name: MariaDB
-  description: |
+  description: |-
     MariaDB is a community-developed, commercially supported fork of the MySQL relational database management system.
     It's designed to remain free and open-source software under the GNU General Public License.
-    
-    Project: https://mariadb.org/
-    Documentation: https://mariadb.com/kb/en/documentation/
-    GitHub: https://github.com/MariaDB/server
+    ## References
+    * **Project:** https://mariadb.org/
+    * **Documentation:** https://mariadb.com/kb/en/documentation/
+    * **GitHub:** https://github.com/MariaDB/server
+  next_steps: |-
+    Log in with your initial admin user:
+    ```bash
+    Username: `root` or `{{ database_user }}`
+    Password: {{ database_password }}
+    ```
   version: 12.0.2
   author: Christian Lempa
   date: '2025-09-28'
-  tags:
-    - swarm
-    - volume_mode
+  tags: []
 spec:
   general:
     vars:
       service_name:
         default: mariadb
-      container_name:
-        default: mariadb
-      container_hostname:
-        default: mariadb
-      mariadb_version:
-        type: str
-        description: MariaDB version
-        default: "12.0.2"
-  mariadb:
-    title: MariaDB Configuration
-    required: true
-    vars:
-      mariadb_root_password_mode:
-        type: enum
-        description: Root password mode
-        default: set
-        options:
-          - set
-          - random
-          - empty
-      mariadb_root_password:
-        type: str
-        description: Root password (only used if mode is 'set')
-        sensitive: true
-        default: changeme
-        needs: "mariadb_root_password_mode=set"
-      mariadb_database:
-        type: str
-        description: Default database name
-        default: mydb
-      mariadb_user:
-        type: str
-        description: Default user name
-        default: myuser
-      mariadb_password:
-        type: str
-        description: Default user password
-        sensitive: true
-        default: changeme
-  mariadb_port:
-    title: MariaDB Port
-    toggle: mariadb_port_enabled
+  ports:
     vars:
-      mariadb_port_enabled:
-        type: bool
-        description: Enable external port exposure
-        default: false
-      mariadb_port:
+      ports_mariadb:
+        description: "Host port for MariaDB"
         type: int
-        description: External port for MariaDB
         default: 3306
+        required: true

+ 0 - 116
library/compose/n8n-worker/compose.yaml.j2

@@ -1,116 +0,0 @@
-services:
-  {{ service_name }}:
-    image: n8nio/n8n:1.118.1
-    command: worker
-    {#
-      If not in swarm mode, apply container name and hostname
-    #}
-    {% if not swarm_enabled -%}
-    container_name: {{ container_name }}
-    hostname: {{ container_hostname }}
-    {% endif -%}
-    {#
-      Environment variables for n8n worker configuration:
-      - Database connection (must match n8n-server)
-      - Encryption key (must match n8n-server)
-      - Redis queue configuration
-      - Optional metrics
-    #}
-    environment:
-      - N8N_LOG_LEVEL={{ container_loglevel }}
-      - GENERIC_TIMEZONE={{ container_timezone }}
-      - TZ={{ container_timezone }}
-      {% if database_enabled -%}
-      {% if database_type == 'postgres' -%}
-      - DB_TYPE=postgresdb
-      - DB_POSTGRESDB_HOST={{ database_host }}
-      - DB_POSTGRESDB_PORT={{ database_port }}
-      - DB_POSTGRESDB_DATABASE={{ database_name }}
-      - DB_POSTGRESDB_USER={{ database_user }}
-      {% if swarm_enabled -%}
-      - DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/{{ service_name }}_database_password
-      {% else -%}
-      - DB_POSTGRESDB_PASSWORD={{ database_password }}
-      {% endif -%}
-      {% elif database_type == 'mysql' -%}
-      - DB_TYPE=mysqldb
-      - DB_MYSQLDB_HOST={{ database_host }}
-      - DB_MYSQLDB_PORT={{ database_port }}
-      - DB_MYSQLDB_DATABASE={{ database_name }}
-      - DB_MYSQLDB_USER={{ database_user }}
-      {% if swarm_enabled -%}
-      - DB_MYSQLDB_PASSWORD_FILE=/run/secrets/{{ service_name }}_database_password
-      {% else -%}
-      - DB_MYSQLDB_PASSWORD={{ database_password }}
-      {% endif -%}
-      {% endif -%}
-      {% endif -%}
-      {% if swarm_enabled -%}
-      - N8N_ENCRYPTION_KEY_FILE=/run/secrets/{{ service_name }}_encryption_key
-      {% else -%}
-      - N8N_ENCRYPTION_KEY={{ encryption_key }}
-      {% endif -%}
-      - EXECUTIONS_MODE=queue
-      - QUEUE_BULL_REDIS_HOST={{ redis_host }}
-      - QUEUE_BULL_REDIS_PORT={{ redis_port }}
-      - QUEUE_HEALTH_CHECK_ACTIVE=true
-      {% if metrics_enabled -%}
-      - N8N_METRICS=true
-      {% if metrics_detailed -%}
-      - N8N_METRICS_INCLUDE_WORKFLOW_ID_LABELS=true
-      - N8N_METRICS_INCLUDE_NODE_TYPE_LABEL=true
-      {% endif -%}
-      {% endif -%}
-    {#
-      Volume configuration for persistent data
-    #}
-    volumes:
-      - /etc/localtime:/etc/localtime:ro
-      - data:/home/node/.n8n
-    {#
-      If not in swarm mode, apply restart policy
-    #}
-    {% if not swarm_enabled -%}
-    restart: {{ restart_policy }}
-    {% endif -%}
-    {#
-      Deploy configuration for Swarm mode:
-      - Multiple worker replicas for scaling
-      - Uses Docker secrets for sensitive data
-    #}
-    {% if swarm_enabled -%}
-    deploy:
-      replicas: {{ swarm_replicas }}
-      {% if swarm_placement_host -%}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif -%}
-    secrets:
-      - {{ service_name }}_encryption_key
-      {% if database_enabled -%}
-      - {{ service_name }}_database_password
-      {% endif -%}
-    {% endif -%}
-
-{#
-  Volume definitions:
-  - data: Persistent storage for n8n worker data
-#}
-volumes:
-  data:
-    driver: local
-
-{#
-  Docker Swarm secrets (only when swarm_enabled is set):
-  - Encryption key and database password (external secrets)
-#}
-{% if swarm_enabled -%}
-secrets:
-  {{ service_name }}_encryption_key:
-    external: true
-  {% if database_enabled -%}
-  {{ service_name }}_database_password:
-    external: true
-  {% endif -%}
-{% endif -%}

+ 0 - 143
library/compose/n8n-worker/template.yaml

@@ -1,143 +0,0 @@
----
-kind: compose
-schema: "1.2"
-metadata:
-  name: N8N Worker
-  description: >
-    N8n Worker component for scaled queue-based deployments.
-    This template provides standalone worker nodes that process workflows
-    from the queue (Redis). Workers connect to the same database and Redis
-    as the n8n server but do not serve the web UI.
-
-
-    Use this template in combination with n8n-server configured with queue mode.
-    Multiple workers can be deployed to scale workflow execution.
-
-
-    Project: https://n8n.io/
-
-    Documentation: https://docs.n8n.io/
-
-    GitHub: https://github.com/n8n-io/n8n
-  version: 1.118.1
-  author: Christian Lempa
-  date: '2025-11-02'
-  tags:
-    - swarm
-  next_steps: |
-    {% if swarm_enabled -%}
-    1. Ensure secrets are created (shared with n8n-server):
-       echo "your-encryption-key" | docker secret create {{ service_name }}_encryption_key -
-       {% if database_enabled and database_type == 'postgres' -%}
-       echo "your-db-password" | docker secret create {{ service_name }}_database_password -
-       {%- endif %}
-    2. Deploy to Docker Swarm:
-       docker stack deploy -c compose.yaml {{ service_name }}
-    {% else -%}
-    1. Start n8n worker with Docker Compose:
-       docker compose up -d
-    {% endif -%}
-    2. Workers will automatically connect to Redis and begin processing queued workflows
-spec:
-  general:
-    vars:
-      service_name:
-        default: "n8n-worker"
-      container_name:
-        default: "n8n-worker"
-      container_hostname:
-        default: "n8n-worker"
-  redis:
-    title: "Redis Connection"
-    description: "Redis queue connection (must match n8n-server configuration)"
-    required: true
-    vars:
-      redis_host:
-        type: str
-        description: "Redis host"
-        default: "redis"
-      redis_port:
-        type: int
-        description: "Redis port"
-        default: 6379
-  database:
-    title: "Database"
-    description: "External database configuration (must match n8n-server)"
-    required: true
-    vars:
-      database_enabled:
-        type: bool
-        description: "Use external database"
-        default: true
-      database_type:
-        type: enum
-        description: "Database type"
-        options:
-          - "postgres"
-          - "mysql"
-        default: "postgres"
-        needs: "database_enabled"
-      database_host:
-        type: str
-        description: "Database host"
-        default: "postgres"
-        needs: "database_enabled"
-      database_port:
-        type: int
-        description: "Database port"
-        default: 5432
-        needs: "database_enabled"
-      database_name:
-        type: str
-        description: "Database name"
-        default: "n8n"
-        needs: "database_enabled"
-      database_user:
-        type: str
-        description: "Database username"
-        default: "n8n"
-        needs: "database_enabled"
-      database_password:
-        type: str
-        description: "Database password"
-        sensitive: true
-        needs: "database_enabled"
-  security:
-    title: "Security"
-    required: true
-    vars:
-      encryption_key:
-        type: str
-        description: "N8N encryption key (must match server)"
-        sensitive: true
-        autogenerated: true
-        extra: "CRITICAL: Must be identical to the n8n-server encryption key!"
-  metrics:
-    title: "Metrics"
-    description: "Prometheus metrics configuration"
-    vars:
-      metrics_enabled:
-        type: bool
-        description: "Enable Prometheus metrics"
-        default: false
-      metrics_detailed:
-        type: bool
-        description: "Include detailed metrics (workflows, nodes)"
-        default: false
-        needs: "metrics_enabled"
-  network:
-    vars:
-      network_mode:
-        extra: "Use 'bridge' with shared network to connect to Redis and database. Swarm only supports 'bridge'."
-      network_name:
-        default: "n8n_network"
-  swarm:
-    vars:
-      swarm_enabled:
-        needs: "network_mode=bridge"
-      swarm_replicas:
-        type: int
-        description: "Number of worker replicas"
-        default: 1
-        needs: "swarm_enabled"
-        extra: "Scale workers based on workload (typically 1-5)"

+ 296 - 0
library/compose/n8n/compose.yaml.j2

@@ -7,3 +7,299 @@ services:
     {% endif -%}
     volumes:
       - redis_data:/data
+    {% if network_mode == 'bridge' -%}
+    networks:
+      - {{ network_name }}
+    {% else -%}
+    network_mode: {{ network_mode }}
+    {% endif -%}
+    {% if not swarm_enabled -%}
+    restart: {{ restart_policy }}
+    {% endif -%}
+    healthcheck:
+      test: ["CMD", "redis-cli", "ping"]
+      interval: 5s
+      timeout: 3s
+      retries: 5
+    {% if swarm_enabled -%}
+    deploy:
+      replicas: 1
+      placement:
+        constraints:
+          - node.role == manager
+    {% endif -%}
+
+  {% endif -%}
+  {{ service_name }}:
+    image: n8nio/n8n:1.118.1
+    {% if not swarm_enabled -%}
+    container_name: {{ container_name }}
+    hostname: {{ container_hostname }}
+    {% endif -%}
+    environment:
+      - N8N_LOG_LEVEL={{ container_loglevel }}
+      - GENERIC_TIMEZONE={{ container_timezone }}
+      - TZ={{ container_timezone }}
+      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
+      - N8N_RUNNERS_ENABLED=true
+      {% if traefik_enabled -%}
+      - N8N_HOST={{ traefik_host }}
+      {% if traefik_tls_enabled -%}
+      - N8N_PROTOCOL=https
+      - N8N_EDITOR_BASE_URL=https://{{ traefik_host }}
+      {% else -%}
+      - N8N_PROTOCOL=http
+      - N8N_EDITOR_BASE_URL=http://{{ traefik_host }}
+      {% endif -%}
+      {% endif -%}
+      - NODE_ENV=production
+      {% if database_enabled -%}
+      {% if database_type == 'postgres' -%}
+      - DB_TYPE=postgresdb
+      - DB_POSTGRESDB_HOST={{ database_host }}
+      - DB_POSTGRESDB_PORT={{ database_port }}
+      - DB_POSTGRESDB_DATABASE={{ database_name }}
+      - DB_POSTGRESDB_USER={{ database_user }}
+      {% if swarm_enabled -%}
+      - DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/{{ database_password_secret_name }}
+      {% else -%}
+      - DB_POSTGRESDB_PASSWORD={{ database_password }}
+      {% endif -%}
+      {% elif database_type == 'mysql' -%}
+      - DB_TYPE=mysqldb
+      - DB_MYSQLDB_HOST={{ database_host }}
+      - DB_MYSQLDB_PORT={{ database_port }}
+      - DB_MYSQLDB_DATABASE={{ database_name }}
+      - DB_MYSQLDB_USER={{ database_user }}
+      {% if swarm_enabled -%}
+      - DB_MYSQLDB_PASSWORD_FILE=/run/secrets/{{ database_password_secret_name }}
+      {% else -%}
+      - DB_MYSQLDB_PASSWORD={{ database_password }}
+      {% endif -%}
+      {% endif -%}
+      {% endif -%}
+      {% if swarm_enabled -%}
+      - N8N_ENCRYPTION_KEY_FILE=/run/secrets/{{ encryption_key_secret_name }}
+      {% else -%}
+      - N8N_ENCRYPTION_KEY={{ encryption_key }}
+      {% endif -%}
+      {% if webhook_url -%}
+      - WEBHOOK_URL={{ webhook_url }}
+      {% endif -%}
+      - N8N_PROXY_HOPS={{ proxy_hops }}
+      {% if metrics_enabled -%}
+      - N8N_METRICS=true
+      {% if metrics_detailed -%}
+      - N8N_METRICS_INCLUDE_WORKFLOW_ID_LABELS=true
+      - N8N_METRICS_INCLUDE_NODE_TYPE_LABEL=true
+      - N8N_METRICS_INCLUDE_API_ENDPOINTS=true
+      - N8N_METRICS_INCLUDE_API_STATUS_CODE_LABELS=true
+      - N8N_METRICS_INCLUDE_CREDENTIAL_TYPE_LABEL=true
+      {% endif -%}
+      {% endif -%}
+      - EXECUTIONS_DATA_SAVE_ON_ERROR={{ execution_save_on_error }}
+      - EXECUTIONS_DATA_SAVE_ON_SUCCESS={{ execution_save_on_success }}
+      {% if queue_enabled -%}
+      - EXECUTIONS_MODE=queue
+      - QUEUE_BULL_REDIS_HOST={{ queue_redis_host }}
+      - QUEUE_BULL_REDIS_PORT={{ queue_redis_port }}
+      - QUEUE_HEALTH_CHECK_ACTIVE=true
+      {% if metrics_enabled -%}
+      - N8N_METRICS_INCLUDE_QUEUE_METRICS=true
+      {% endif -%}
+      {% endif -%}
+    volumes:
+      - /etc/localtime:/etc/localtime:ro
+      - data:/home/node/.n8n
+    {% if network_mode == 'bridge' -%}
+    networks:
+      {% if traefik_enabled -%}
+      - {{ traefik_network }}
+      {% endif -%}
+      - {{ network_name }}
+    {% else -%}
+    network_mode: {{ network_mode }}
+    {% endif -%}
+    {% if queue_enabled and not queue_redis_external -%}
+    depends_on:
+      redis:
+        condition: service_healthy
+    {% endif -%}
+    {% if traefik_enabled -%}
+    labels:
+      - traefik.enable=true
+      {% if network_mode == 'bridge' -%}
+      - traefik.docker.network={{ traefik_network }}
+      {% endif -%}
+      {% if traefik_webhook_host -%}
+      - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`) || Host(`{{ traefik_webhook_host }}`)
+      {% else -%}
+      - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
+      {% endif -%}
+      {% if traefik_tls_enabled -%}
+      - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_tls_entrypoint }}
+      - traefik.http.routers.{{ service_name }}.tls=true
+      - traefik.http.routers.{{ service_name }}.tls.certresolver={{ traefik_tls_certresolver }}
+      {% else -%}
+      - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_entrypoint }}
+      {% endif -%}
+      - 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 }}
+    {% endif -%}
+    {% if swarm_enabled -%}
+    deploy:
+      replicas: {{ swarm_replicas }}
+      {% if swarm_placement_host -%}
+      placement:
+        constraints:
+          - node.hostname == {{ swarm_placement_host }}
+      {% endif -%}
+      {% if traefik_enabled -%}
+      labels:
+        - traefik.enable=true
+        - traefik.docker.network={{ traefik_network }}
+        {% if traefik_webhook_host -%}
+        - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`) || Host(`{{ traefik_webhook_host }}`)
+        {% else -%}
+        - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
+        {% endif -%}
+        {% if traefik_tls_enabled -%}
+        - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_tls_entrypoint }}
+        - traefik.http.routers.{{ service_name }}.tls=true
+        - traefik.http.routers.{{ service_name }}.tls.certresolver={{ traefik_tls_certresolver }}
+        {% else -%}
+        - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_entrypoint }}
+        {% endif -%}
+        - 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 }}
+      {% if database_enabled -%}
+      - {{ database_password_secret_name }}
+      {% endif -%}
+    {% endif -%}
+{% if queue_enabled and queue_embedded_worker -%}
+
+  {{ service_name }}-worker:
+    image: n8nio/n8n:1.118.1
+    command: worker
+    {% if not swarm_enabled -%}
+    container_name: {{ container_name }}-worker
+    {% endif -%}
+    environment:
+      - N8N_LOG_LEVEL={{ container_loglevel }}
+      - GENERIC_TIMEZONE={{ container_timezone }}
+      - TZ={{ container_timezone }}
+      {% if database_enabled -%}
+      {% if database_type == 'postgres' -%}
+      - DB_TYPE=postgresdb
+      - DB_POSTGRESDB_HOST={{ database_host }}
+      - DB_POSTGRESDB_PORT={{ database_port }}
+      - DB_POSTGRESDB_DATABASE={{ database_name }}
+      - DB_POSTGRESDB_USER={{ database_user }}
+      {% if swarm_enabled -%}
+      - DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/{{ database_password_secret_name }}
+      {% else -%}
+      - DB_POSTGRESDB_PASSWORD={{ database_password }}
+      {% endif -%}
+      {% elif database_type == 'mysql' -%}
+      - DB_TYPE=mysqldb
+      - DB_MYSQLDB_HOST={{ database_host }}
+      - DB_MYSQLDB_PORT={{ database_port }}
+      - DB_MYSQLDB_DATABASE={{ database_name }}
+      - DB_MYSQLDB_USER={{ database_user }}
+      {% if swarm_enabled -%}
+      - DB_MYSQLDB_PASSWORD_FILE=/run/secrets/{{ database_password_secret_name }}
+      {% else -%}
+      - DB_MYSQLDB_PASSWORD={{ database_password }}
+      {% endif -%}
+      {% endif -%}
+      {% endif -%}
+      {% if swarm_enabled -%}
+      - N8N_ENCRYPTION_KEY_FILE=/run/secrets/{{ encryption_key_secret_name }}
+      {% else -%}
+      - N8N_ENCRYPTION_KEY={{ encryption_key }}
+      {% endif -%}
+      - EXECUTIONS_MODE=queue
+      - QUEUE_BULL_REDIS_HOST={{ queue_redis_host }}
+      - QUEUE_BULL_REDIS_PORT={{ queue_redis_port }}
+      - QUEUE_HEALTH_CHECK_ACTIVE=true
+      {% if metrics_enabled -%}
+      - N8N_METRICS=true
+      {% if metrics_detailed -%}
+      - N8N_METRICS_INCLUDE_WORKFLOW_ID_LABELS=true
+      - N8N_METRICS_INCLUDE_NODE_TYPE_LABEL=true
+      {% endif -%}
+      {% endif -%}
+    volumes:
+      - /etc/localtime:/etc/localtime:ro
+      - data:/home/node/.n8n
+    {% if network_mode == 'bridge' -%}
+    networks:
+      - {{ network_name }}
+    {% else -%}
+    network_mode: {{ network_mode }}
+    {% endif -%}
+    {% if not queue_redis_external -%}
+    depends_on:
+      redis:
+        condition: service_healthy
+    {% endif -%}
+    {% if not swarm_enabled -%}
+    restart: {{ restart_policy }}
+    {% endif -%}
+    {% if swarm_enabled -%}
+    deploy:
+      replicas: 1
+      {% if swarm_placement_host -%}
+      placement:
+        constraints:
+          - node.hostname == {{ swarm_placement_host }}
+      {% endif -%}
+    secrets:
+      - {{ encryption_key_secret_name }}
+      {% if database_enabled -%}
+      - {{ database_password_secret_name }}
+      {% endif -%}
+    {% endif -%}
+{% endif -%}
+
+volumes:
+  data:
+    driver: local
+{% if queue_enabled and not queue_redis_external -%}
+  redis_data:
+    driver: local
+{% endif -%}
+
+{% if network_mode == 'bridge' -%}
+networks:
+  {{ network_name }}:
+  {% if network_external -%}
+    external: true
+  {% else -%}
+    driver: bridge
+  {% endif -%}
+  {% if traefik_enabled -%}
+  {{ traefik_network }}:
+  {% if traefik_network_external -%}
+    external: true
+  {% else -%}
+    driver: bridge
+  {% endif -%}
+  {% endif -%}
+{% endif -%}
+
+{% if swarm_enabled -%}
+secrets:
+  {{ encryption_key_secret_name }}:
+    external: true
+  {% if database_enabled -%}
+  {{ database_password_secret_name }}:
+    external: true
+  {% endif -%}
+{% endif -%}

+ 0 - 305
library/compose/n8n/compose.yaml.j2.bak3

@@ -1,305 +0,0 @@
-{% if queue_enabled and not queue_redis_external -%}
-services:
-  redis:
-    image: redis:7-alpine
-    {% if not swarm_enabled -%}
-    container_name: {{ service_name }}-redis
-    {% endif -%}
-    volumes:
-      - redis_data:/data
-    {% if network_mode == 'bridge' -%}
-    networks:
-      - {{ network_name }}
-    {% else -%}
-    network_mode: {{ network_mode }}
-    {% endif -%}
-    {% if not swarm_enabled -%}
-    restart: {{ restart_policy }}
-    {% endif -%}
-    healthcheck:
-      test: ["CMD", "redis-cli", "ping"]
-      interval: 5s
-      timeout: 3s
-      retries: 5
-    {% if swarm_enabled -%}
-    deploy:
-      replicas: 1
-      placement:
-        constraints:
-          - node.role == manager
-    {% endif -%}
-
-  {% endif -%}
-  {{ service_name }}:
-    image: n8nio/n8n:1.118.1
-    {% if not swarm_enabled -%}
-    container_name: {{ container_name }}
-    hostname: {{ container_hostname }}
-    {% endif -%}
-    environment:
-      - N8N_LOG_LEVEL={{ container_loglevel }}
-      - GENERIC_TIMEZONE={{ container_timezone }}
-      - TZ={{ container_timezone }}
-      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
-      - N8N_RUNNERS_ENABLED=true
-      {% if traefik_enabled -%}
-      - N8N_HOST={{ traefik_host }}
-      {% if traefik_tls_enabled -%}
-      - N8N_PROTOCOL=https
-      - N8N_EDITOR_BASE_URL=https://{{ traefik_host }}
-      {% else -%}
-      - N8N_PROTOCOL=http
-      - N8N_EDITOR_BASE_URL=http://{{ traefik_host }}
-      {% endif -%}
-      {% endif -%}
-      - NODE_ENV=production
-      {% if database_enabled -%}
-      {% if database_type == 'postgres' -%}
-      - DB_TYPE=postgresdb
-      - DB_POSTGRESDB_HOST={{ database_host }}
-      - DB_POSTGRESDB_PORT={{ database_port }}
-      - DB_POSTGRESDB_DATABASE={{ database_name }}
-      - DB_POSTGRESDB_USER={{ database_user }}
-      {% if swarm_enabled -%}
-      - DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/{{ database_password_secret_name }}
-      {% else -%}
-      - DB_POSTGRESDB_PASSWORD={{ database_password }}
-      {% endif -%}
-      {% elif database_type == 'mysql' -%}
-      - DB_TYPE=mysqldb
-      - DB_MYSQLDB_HOST={{ database_host }}
-      - DB_MYSQLDB_PORT={{ database_port }}
-      - DB_MYSQLDB_DATABASE={{ database_name }}
-      - DB_MYSQLDB_USER={{ database_user }}
-      {% if swarm_enabled -%}
-      - DB_MYSQLDB_PASSWORD_FILE=/run/secrets/{{ database_password_secret_name }}
-      {% else -%}
-      - DB_MYSQLDB_PASSWORD={{ database_password }}
-      {% endif -%}
-      {% endif -%}
-      {% endif -%}
-      {% if swarm_enabled -%}
-      - N8N_ENCRYPTION_KEY_FILE=/run/secrets/{{ encryption_key_secret_name }}
-      {% else -%}
-      - N8N_ENCRYPTION_KEY={{ encryption_key }}
-      {% endif -%}
-      {% if webhook_url -%}
-      - WEBHOOK_URL={{ webhook_url }}
-      {% endif -%}
-      - N8N_PROXY_HOPS={{ proxy_hops }}
-      {% if metrics_enabled -%}
-      - N8N_METRICS=true
-      {% if metrics_detailed -%}
-      - N8N_METRICS_INCLUDE_WORKFLOW_ID_LABELS=true
-      - N8N_METRICS_INCLUDE_NODE_TYPE_LABEL=true
-      - N8N_METRICS_INCLUDE_API_ENDPOINTS=true
-      - N8N_METRICS_INCLUDE_API_STATUS_CODE_LABELS=true
-      - N8N_METRICS_INCLUDE_CREDENTIAL_TYPE_LABEL=true
-      {% endif -%}
-      {% endif -%}
-      - EXECUTIONS_DATA_SAVE_ON_ERROR={{ execution_save_on_error }}
-      - EXECUTIONS_DATA_SAVE_ON_SUCCESS={{ execution_save_on_success }}
-      {% if queue_enabled -%}
-      - EXECUTIONS_MODE=queue
-      - QUEUE_BULL_REDIS_HOST={{ queue_redis_host }}
-      - QUEUE_BULL_REDIS_PORT={{ queue_redis_port }}
-      - QUEUE_HEALTH_CHECK_ACTIVE=true
-      {% if metrics_enabled -%}
-      - N8N_METRICS_INCLUDE_QUEUE_METRICS=true
-      {% endif -%}
-      {% endif -%}
-    volumes:
-      - /etc/localtime:/etc/localtime:ro
-      - data:/home/node/.n8n
-    {% if network_mode == 'bridge' -%}
-    networks:
-      {% if traefik_enabled -%}
-      - {{ traefik_network }}
-      {% endif -%}
-      - {{ network_name }}
-    {% else -%}
-    network_mode: {{ network_mode }}
-    {% endif -%}
-    {% if queue_enabled and not queue_redis_external -%}
-    depends_on:
-      redis:
-        condition: service_healthy
-    {% endif -%}
-    {% if traefik_enabled -%}
-    labels:
-      - traefik.enable=true
-      {% if network_mode == 'bridge' -%}
-      - traefik.docker.network={{ traefik_network }}
-      {% endif -%}
-      {% if traefik_webhook_host -%}
-      - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`) || Host(`{{ traefik_webhook_host }}`)
-      {% else -%}
-      - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
-      {% endif -%}
-      {% if traefik_tls_enabled -%}
-      - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_tls_entrypoint }}
-      - traefik.http.routers.{{ service_name }}.tls=true
-      - traefik.http.routers.{{ service_name }}.tls.certresolver={{ traefik_tls_certresolver }}
-      {% else -%}
-      - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_entrypoint }}
-      {% endif -%}
-      - 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 }}
-    {% endif -%}
-    {% if swarm_enabled -%}
-    deploy:
-      replicas: {{ swarm_replicas }}
-      {% if swarm_placement_host -%}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif -%}
-      {% if traefik_enabled -%}
-      labels:
-        - traefik.enable=true
-        - traefik.docker.network={{ traefik_network }}
-        {% if traefik_webhook_host -%}
-        - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`) || Host(`{{ traefik_webhook_host }}`)
-        {% else -%}
-        - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
-        {% endif -%}
-        {% if traefik_tls_enabled -%}
-        - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_tls_entrypoint }}
-        - traefik.http.routers.{{ service_name }}.tls=true
-        - traefik.http.routers.{{ service_name }}.tls.certresolver={{ traefik_tls_certresolver }}
-        {% else -%}
-        - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_entrypoint }}
-        {% endif -%}
-        - 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 }}
-      {% if database_enabled -%}
-      - {{ database_password_secret_name }}
-      {% endif -%}
-    {% endif -%}
-{% if queue_enabled and queue_embedded_worker -%}
-
-  {{ service_name }}-worker:
-    image: n8nio/n8n:1.118.1
-    command: worker
-    {% if not swarm_enabled -%}
-    container_name: {{ container_name }}-worker
-    {% endif -%}
-    environment:
-      - N8N_LOG_LEVEL={{ container_loglevel }}
-      - GENERIC_TIMEZONE={{ container_timezone }}
-      - TZ={{ container_timezone }}
-      {% if database_enabled -%}
-      {% if database_type == 'postgres' -%}
-      - DB_TYPE=postgresdb
-      - DB_POSTGRESDB_HOST={{ database_host }}
-      - DB_POSTGRESDB_PORT={{ database_port }}
-      - DB_POSTGRESDB_DATABASE={{ database_name }}
-      - DB_POSTGRESDB_USER={{ database_user }}
-      {% if swarm_enabled -%}
-      - DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/{{ database_password_secret_name }}
-      {% else -%}
-      - DB_POSTGRESDB_PASSWORD={{ database_password }}
-      {% endif -%}
-      {% elif database_type == 'mysql' -%}
-      - DB_TYPE=mysqldb
-      - DB_MYSQLDB_HOST={{ database_host }}
-      - DB_MYSQLDB_PORT={{ database_port }}
-      - DB_MYSQLDB_DATABASE={{ database_name }}
-      - DB_MYSQLDB_USER={{ database_user }}
-      {% if swarm_enabled -%}
-      - DB_MYSQLDB_PASSWORD_FILE=/run/secrets/{{ database_password_secret_name }}
-      {% else -%}
-      - DB_MYSQLDB_PASSWORD={{ database_password }}
-      {% endif -%}
-      {% endif -%}
-      {% endif -%}
-      {% if swarm_enabled -%}
-      - N8N_ENCRYPTION_KEY_FILE=/run/secrets/{{ encryption_key_secret_name }}
-      {% else -%}
-      - N8N_ENCRYPTION_KEY={{ encryption_key }}
-      {% endif -%}
-      - EXECUTIONS_MODE=queue
-      - QUEUE_BULL_REDIS_HOST={{ queue_redis_host }}
-      - QUEUE_BULL_REDIS_PORT={{ queue_redis_port }}
-      - QUEUE_HEALTH_CHECK_ACTIVE=true
-      {% if metrics_enabled -%}
-      - N8N_METRICS=true
-      {% if metrics_detailed -%}
-      - N8N_METRICS_INCLUDE_WORKFLOW_ID_LABELS=true
-      - N8N_METRICS_INCLUDE_NODE_TYPE_LABEL=true
-      {% endif -%}
-      {% endif -%}
-    volumes:
-      - /etc/localtime:/etc/localtime:ro
-      - data:/home/node/.n8n
-    {% if network_mode == 'bridge' -%}
-    networks:
-      - {{ network_name }}
-    {% else -%}
-    network_mode: {{ network_mode }}
-    {% endif -%}
-    {% if not queue_redis_external -%}
-    depends_on:
-      redis:
-        condition: service_healthy
-    {% endif -%}
-    {% if not swarm_enabled -%}
-    restart: {{ restart_policy }}
-    {% endif -%}
-    {% if swarm_enabled -%}
-    deploy:
-      replicas: 1
-      {% if swarm_placement_host -%}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif -%}
-    secrets:
-      - {{ encryption_key_secret_name }}
-      {% if database_enabled -%}
-      - {{ database_password_secret_name }}
-      {% endif -%}
-    {% endif -%}
-{% endif -%}
-
-volumes:
-  data:
-    driver: local
-{% if queue_enabled and not queue_redis_external -%}
-  redis_data:
-    driver: local
-{% endif -%}
-
-{% if network_mode == 'bridge' -%}
-networks:
-  {{ network_name }}:
-  {% if network_external -%}
-    external: true
-  {% else -%}
-    driver: bridge
-  {% endif -%}
-  {% if traefik_enabled -%}
-  {{ traefik_network }}:
-  {% if traefik_network_external -%}
-    external: true
-  {% else -%}
-    driver: bridge
-  {% endif -%}
-  {% endif -%}
-{% endif -%}
-
-{% if swarm_enabled -%}
-secrets:
-  {{ encryption_key_secret_name }}:
-    external: true
-  {% if database_enabled -%}
-  {{ database_password_secret_name }}:
-    external: true
-  {% endif -%}
-{% endif -%}

+ 0 - 302
library/compose/n8n/compose.yaml.j2.final

@@ -1,302 +0,0 @@
-{% if queue_enabled and not queue_redis_external -%}
-services:
-  redis:
-    image: redis:7-alpine
-    {% if not swarm_enabled -%}
-    container_name: {{ service_name }}-redis
-    {% endif -%}
-    volumes:
-      - redis_data:/data
-    {% if network_mode == 'bridge' -%}
-    networks:
-      - {{ network_name }}
-    {% else -%}
-    {% endif -%}
-    {% if not swarm_enabled -%}
-    restart: {{ restart_policy }}
-    {% endif -%}
-    healthcheck:
-      test: ["CMD", "redis-cli", "ping"]
-      interval: 5s
-      timeout: 3s
-      retries: 5
-    {% if swarm_enabled -%}
-    deploy:
-      replicas: 1
-      placement:
-        constraints:
-          - node.role == manager
-    {% endif -%}
-
-  {% endif -%}
-  {{ service_name }}:
-    image: n8nio/n8n:1.118.1
-    {% if not swarm_enabled -%}
-    container_name: {{ container_name }}
-    hostname: {{ container_hostname }}
-    {% endif -%}
-    environment:
-      - N8N_LOG_LEVEL={{ container_loglevel }}
-      - GENERIC_TIMEZONE={{ container_timezone }}
-      - TZ={{ container_timezone }}
-      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
-      - N8N_RUNNERS_ENABLED=true
-      {% if traefik_enabled -%}
-      - N8N_HOST={{ traefik_host }}
-      {% if traefik_tls_enabled -%}
-      - N8N_PROTOCOL=https
-      - N8N_EDITOR_BASE_URL=https://{{ traefik_host }}
-      {% else -%}
-      - N8N_PROTOCOL=http
-      - N8N_EDITOR_BASE_URL=http://{{ traefik_host }}
-      {% endif -%}
-      {% endif -%}
-      - NODE_ENV=production
-      {% if database_enabled -%}
-      {% if database_type == 'postgres' -%}
-      - DB_TYPE=postgresdb
-      - DB_POSTGRESDB_HOST={{ database_host }}
-      - DB_POSTGRESDB_PORT={{ database_port }}
-      - DB_POSTGRESDB_DATABASE={{ database_name }}
-      - DB_POSTGRESDB_USER={{ database_user }}
-      {% if swarm_enabled -%}
-      - DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/{{ database_password_secret_name }}
-      {% else -%}
-      - DB_POSTGRESDB_PASSWORD={{ database_password }}
-      {% endif -%}
-      {% elif database_type == 'mysql' -%}
-      - DB_TYPE=mysqldb
-      - DB_MYSQLDB_HOST={{ database_host }}
-      - DB_MYSQLDB_PORT={{ database_port }}
-      - DB_MYSQLDB_DATABASE={{ database_name }}
-      - DB_MYSQLDB_USER={{ database_user }}
-      {% if swarm_enabled -%}
-      - DB_MYSQLDB_PASSWORD_FILE=/run/secrets/{{ database_password_secret_name }}
-      {% else -%}
-      - DB_MYSQLDB_PASSWORD={{ database_password }}
-      {% endif -%}
-      {% endif -%}
-      {% endif -%}
-      {% if swarm_enabled -%}
-      - N8N_ENCRYPTION_KEY_FILE=/run/secrets/{{ encryption_key_secret_name }}
-      {% else -%}
-      - N8N_ENCRYPTION_KEY={{ encryption_key }}
-      {% endif -%}
-      {% if webhook_url -%}
-      - WEBHOOK_URL={{ webhook_url }}
-      {% endif -%}
-      - N8N_PROXY_HOPS={{ proxy_hops }}
-      {% if metrics_enabled -%}
-      - N8N_METRICS=true
-      {% if metrics_detailed -%}
-      - N8N_METRICS_INCLUDE_WORKFLOW_ID_LABELS=true
-      - N8N_METRICS_INCLUDE_NODE_TYPE_LABEL=true
-      - N8N_METRICS_INCLUDE_API_ENDPOINTS=true
-      - N8N_METRICS_INCLUDE_API_STATUS_CODE_LABELS=true
-      - N8N_METRICS_INCLUDE_CREDENTIAL_TYPE_LABEL=true
-      {% endif -%}
-      {% endif -%}
-      - EXECUTIONS_DATA_SAVE_ON_ERROR={{ execution_save_on_error }}
-      - EXECUTIONS_DATA_SAVE_ON_SUCCESS={{ execution_save_on_success }}
-      {% if queue_enabled -%}
-      - EXECUTIONS_MODE=queue
-      - QUEUE_BULL_REDIS_HOST={{ queue_redis_host }}
-      - QUEUE_BULL_REDIS_PORT={{ queue_redis_port }}
-      - QUEUE_HEALTH_CHECK_ACTIVE=true
-      {% if metrics_enabled -%}
-      - N8N_METRICS_INCLUDE_QUEUE_METRICS=true
-      {% endif -%}
-      {% endif -%}
-    volumes:
-      - /etc/localtime:/etc/localtime:ro
-      - data:/home/node/.n8n
-    {% if network_mode == 'bridge' -%}
-    networks:
-      {% if traefik_enabled -%}
-      - {{ traefik_network }}
-      {% endif -%}
-      - {{ network_name }}
-    {% else -%}
-    {% endif -%}
-    {% if queue_enabled and not queue_redis_external -%}
-    depends_on:
-      redis:
-        condition: service_healthy
-    {% endif -%}
-    {% if traefik_enabled -%}
-    labels:
-      - traefik.enable=true
-      {% if network_mode == 'bridge' -%}
-      - traefik.docker.network={{ traefik_network }}
-      {% endif -%}
-      {% if traefik_webhook_host -%}
-      - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`) || Host(`{{ traefik_webhook_host }}`)
-      {% else -%}
-      - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
-      {% endif -%}
-      {% if traefik_tls_enabled -%}
-      - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_tls_entrypoint }}
-      - traefik.http.routers.{{ service_name }}.tls=true
-      - traefik.http.routers.{{ service_name }}.tls.certresolver={{ traefik_tls_certresolver }}
-      {% else -%}
-      - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_entrypoint }}
-      {% endif -%}
-      - 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 }}
-    {% endif -%}
-    {% if swarm_enabled -%}
-    deploy:
-      replicas: {{ swarm_replicas }}
-      {% if swarm_placement_host -%}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif -%}
-      {% if traefik_enabled -%}
-      labels:
-        - traefik.enable=true
-        - traefik.docker.network={{ traefik_network }}
-        {% if traefik_webhook_host -%}
-        - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`) || Host(`{{ traefik_webhook_host }}`)
-        {% else -%}
-        - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik_host }}.{{ traefik_domain }}`)
-        {% endif -%}
-        {% if traefik_tls_enabled -%}
-        - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_tls_entrypoint }}
-        - traefik.http.routers.{{ service_name }}.tls=true
-        - traefik.http.routers.{{ service_name }}.tls.certresolver={{ traefik_tls_certresolver }}
-        {% else -%}
-        - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik_entrypoint }}
-        {% endif -%}
-        - 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 }}
-      {% if database_enabled -%}
-      - {{ database_password_secret_name }}
-      {% endif -%}
-    {% endif -%}
-{% if queue_enabled and queue_embedded_worker -%}
-
-  {{ service_name }}-worker:
-    image: n8nio/n8n:1.118.1
-    command: worker
-    {% if not swarm_enabled -%}
-    container_name: {{ container_name }}-worker
-    {% endif -%}
-    environment:
-      - N8N_LOG_LEVEL={{ container_loglevel }}
-      - GENERIC_TIMEZONE={{ container_timezone }}
-      - TZ={{ container_timezone }}
-      {% if database_enabled -%}
-      {% if database_type == 'postgres' -%}
-      - DB_TYPE=postgresdb
-      - DB_POSTGRESDB_HOST={{ database_host }}
-      - DB_POSTGRESDB_PORT={{ database_port }}
-      - DB_POSTGRESDB_DATABASE={{ database_name }}
-      - DB_POSTGRESDB_USER={{ database_user }}
-      {% if swarm_enabled -%}
-      - DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/{{ database_password_secret_name }}
-      {% else -%}
-      - DB_POSTGRESDB_PASSWORD={{ database_password }}
-      {% endif -%}
-      {% elif database_type == 'mysql' -%}
-      - DB_TYPE=mysqldb
-      - DB_MYSQLDB_HOST={{ database_host }}
-      - DB_MYSQLDB_PORT={{ database_port }}
-      - DB_MYSQLDB_DATABASE={{ database_name }}
-      - DB_MYSQLDB_USER={{ database_user }}
-      {% if swarm_enabled -%}
-      - DB_MYSQLDB_PASSWORD_FILE=/run/secrets/{{ database_password_secret_name }}
-      {% else -%}
-      - DB_MYSQLDB_PASSWORD={{ database_password }}
-      {% endif -%}
-      {% endif -%}
-      {% endif -%}
-      {% if swarm_enabled -%}
-      - N8N_ENCRYPTION_KEY_FILE=/run/secrets/{{ encryption_key_secret_name }}
-      {% else -%}
-      - N8N_ENCRYPTION_KEY={{ encryption_key }}
-      {% endif -%}
-      - EXECUTIONS_MODE=queue
-      - QUEUE_BULL_REDIS_HOST={{ queue_redis_host }}
-      - QUEUE_BULL_REDIS_PORT={{ queue_redis_port }}
-      - QUEUE_HEALTH_CHECK_ACTIVE=true
-      {% if metrics_enabled -%}
-      - N8N_METRICS=true
-      {% if metrics_detailed -%}
-      - N8N_METRICS_INCLUDE_WORKFLOW_ID_LABELS=true
-      - N8N_METRICS_INCLUDE_NODE_TYPE_LABEL=true
-      {% endif -%}
-      {% endif -%}
-    volumes:
-      - /etc/localtime:/etc/localtime:ro
-      - data:/home/node/.n8n
-    {% if network_mode == 'bridge' -%}
-    networks:
-      - {{ network_name }}
-    {% else -%}
-    {% endif -%}
-    {% if not queue_redis_external -%}
-    depends_on:
-      redis:
-        condition: service_healthy
-    {% endif -%}
-    {% if not swarm_enabled -%}
-    restart: {{ restart_policy }}
-    {% endif -%}
-    {% if swarm_enabled -%}
-    deploy:
-      replicas: 1
-      {% if swarm_placement_host -%}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif -%}
-    secrets:
-      - {{ encryption_key_secret_name }}
-      {% if database_enabled -%}
-      - {{ database_password_secret_name }}
-      {% endif -%}
-    {% endif -%}
-{% endif -%}
-
-volumes:
-  data:
-    driver: local
-{% if queue_enabled and not queue_redis_external -%}
-  redis_data:
-    driver: local
-{% endif -%}
-
-{% if network_mode == 'bridge' -%}
-networks:
-  {{ network_name }}:
-  {% if network_external -%}
-    external: true
-  {% else -%}
-    driver: bridge
-  {% endif -%}
-  {% if traefik_enabled -%}
-  {{ traefik_network }}:
-  {% if traefik_network_external -%}
-    external: true
-  {% else -%}
-    driver: bridge
-  {% endif -%}
-  {% endif -%}
-{% endif -%}
-
-{% if swarm_enabled -%}
-secrets:
-  {{ encryption_key_secret_name }}:
-    external: true
-  {% if database_enabled -%}
-  {{ database_password_secret_name }}:
-    external: true
-  {% endif -%}
-{% endif -%}

+ 6 - 26
library/compose/n8n/template.yaml

@@ -1,6 +1,5 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   name: N8N
   description: |
@@ -8,17 +7,12 @@ metadata:
     various apps and services to automate repetitive tasks without coding.
     With its user-friendly interface, you can create complex workflows by simply dragging
     and dropping nodes that represent different actions and triggers.
-
-    This template provides the n8n server component with optional queue mode support.
-    For scaled deployments, combine with the n8n-worker template.
-
-    ## Database Backend
-    By default, n8n uses SQLite as its database, which is suitable for small-scale or
+    ## Prerequisites
+    - :info: By default, n8n uses SQLite as its database, which is suitable for small-scale or
     personal use. For production environments, it is recommended to use an external
     database like PostgreSQL or MySQL for better performance and reliability. This is also
     required when using Queue Mode.
-    ## Queue Mode
-    Queue mode allows n8n to handle a large number of workflows and tasks efficiently, by using
+    - :info: Queue mode allows n8n to handle a large number of workflows and tasks efficiently, by using
     multiple Workers, and Redis as the queue backend. This is essential for high-availability
     setups where multiple n8n instances work together. It also supports Queue Monitoring
     (when Prometheus metrics are enabled).
@@ -31,23 +25,9 @@ metadata:
   date: '2025-11-02'
   tags:
     - traefik
-    - swarm
-  next_steps: |
-    {% if swarm_enabled -%}
-    1. Create required secrets:
-       echo "your-encryption-key" | docker secret create {{ service_name }}_encryption_key -
-       {% if database_enabled and database_type == 'postgres' -%}
-       echo "your-db-password" | docker secret create {{ service_name }}_database_password -
-       {%- endif %}
-    2. Deploy to Docker Swarm:
-       docker stack deploy -c compose.yaml {{ service_name }}
-    {% else -%}
-    1. Start n8n with Docker Compose:
-       docker compose up -d
-    {% endif -%}
-    2. Access the Web Interface:
-       {%- if traefik_enabled %} https://{{ traefik_host }}
-       {%- else %} http://localhost:{{ ports_http }}{%- endif %}
+    - database
+  next_steps:
+schema: "1.2"
 spec:
   general:
     vars:

+ 0 - 1
library/compose/netbox/.env.secret.database_password.j2

@@ -1 +0,0 @@
-{{ database_password }}

+ 0 - 1
library/compose/netbox/.env.secret.email_password.j2

@@ -1 +0,0 @@
-{% if email_enabled %}{{ email_password }}{% endif %}

+ 0 - 1
library/compose/netbox/.env.secret.netbox_secret_key.j2

@@ -1 +0,0 @@
-{{ netbox_secret_key }}

+ 0 - 1
library/compose/netbox/.env.secret.redis_password.j2

@@ -1 +0,0 @@
-{{ redis_password }}

+ 0 - 18
library/compose/netbox/common/networks.yaml.j2

@@ -1,18 +0,0 @@
-{#
-  Network definitions:
-  - Backend network: Internal communication between NetBox services
-  - Traefik network: External access via reverse proxy (always external)
-  Note: In Swarm mode, backend network uses overlay driver for multi-host communication
-#}
-networks:
-  {{ service_name }}_backend:
-    {% if swarm_enabled %}
-    driver: overlay
-    attachable: true
-    {% else %}
-    driver: bridge
-    {% endif %}
-  {% if traefik_enabled %}
-  {{ traefik_network }}:
-    external: true
-  {% endif %}

+ 0 - 15
library/compose/netbox/common/secrets.yaml.j2

@@ -1,15 +0,0 @@
-{#
-  Docker Swarm secrets definitions
-  Secrets are stored in separate files and referenced in service environment variables
-#}
-secrets:
-  {{ service_name }}_database_password:
-    file: ./.env.secret.database_password
-  {{ service_name }}_redis_password:
-    file: ./.env.secret.redis_password
-  {{ service_name }}_secret_key:
-    file: ./.env.secret.netbox_secret_key
-  {% if email_enabled %}
-  {{ service_name }}_email_password:
-    file: ./.env.secret.email_password
-  {% endif %}

+ 0 - 64
library/compose/netbox/common/volumes.yaml.j2

@@ -1,64 +0,0 @@
-{#
-  Volume definitions:
-  - When volume_mode is 'local' (default): use docker-managed local volumes
-  - When volume_mode is 'nfs': configure NFS-backed volumes
-  - When volume_mode is 'mount': no volume definition needed (bind mounts used directly)
-  Note: Postgres volumes only defined in Compose mode (Swarm uses external database)
-#}
-{% if volume_mode == 'local' %}
-volumes:
-  {% if not database_external and not swarm_enabled %}
-  {{ service_name }}-postgres:
-    driver: local
-  {% endif %}
-  {{ service_name }}-redis:
-    driver: local
-  {{ service_name }}-redis-cache:
-    driver: local
-  {{ service_name }}-media:
-    driver: local
-  {{ service_name }}-reports:
-    driver: local
-  {{ service_name }}-scripts:
-    driver: local
-{% elif volume_mode == 'nfs' %}
-volumes:
-  {% if not database_external and not swarm_enabled %}
-  {{ service_name }}-postgres:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/postgres"
-  {% endif %}
-  {{ service_name }}-redis:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/redis"
-  {{ service_name }}-redis-cache:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/redis-cache"
-  {{ service_name }}-media:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/media"
-  {{ service_name }}-reports:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/reports"
-  {{ service_name }}-scripts:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/scripts"
-{% endif %}

+ 202 - 23
library/compose/netbox/compose.yaml.j2

@@ -1,25 +1,204 @@
-{#
-  NetBox Docker Compose Configuration
-  
-  This is the main orchestration file that includes all service definitions.
-  Services are split into separate files for better maintainability:
-  - services/netbox.yaml: Main NetBox web application
-  - services/worker.yaml: Background task worker
-  - services/redis.yaml: Redis cache and queue services
-  - services/postgres.yaml: PostgreSQL database (Compose mode only)
-  - common/networks.yaml: Network definitions
-  - common/volumes.yaml: Volume definitions
-  - common/secrets.yaml: Secrets (Swarm mode only)
-#}
-include:
-  - services/netbox.yaml
-  - services/worker.yaml
-  - services/redis.yaml
-  {% if not database_external and not swarm_enabled %}
-  - services/postgres.yaml
+---
+services:
+  {{ service_name }}:
+    image: docker.io/netboxcommunity/netbox:v4.2.3
+    restart: {{ restart_policy }}
+    environment:
+      {% if traefik_enabled %}
+      - ALLOWED_HOSTS={{ traefik_host }}.{{ traefik_domain }}
+      {% else %}
+      - ALLOWED_HOSTS=*
+      {% endif %}
+      {% if database_external %}
+      - DB_HOST={{ database_host }}
+      {% else %}
+      - DB_HOST={{ service_name }}_postgres
+      {% endif %}
+      - DB_NAME={{ database_name }}
+      - DB_USER={{ database_user }}
+      - DB_PASSWORD=${DATABASE_PASSWORD}
+      - REDIS_HOST={{ service_name }}_redis
+      - REDIS_PASSWORD=${REDIS_PASSWORD}
+      - REDIS_CACHE_HOST={{ service_name }}_redis-cache
+      - REDIS_CACHE_PASSWORD=${REDIS_PASSWORD}
+      - SECRET_KEY=${NETBOX_SECRET_KEY}
+      {% if netbox_metrics_enabled %}
+      - METRICS_ENABLED=true
+      {% endif %}
+      {% if email_enabled %}
+      - EMAIL_SERVER={{ email_server }}
+      - EMAIL_PORT={{ email_port }}
+      - EMAIL_FROM={{ email_from }}
+      - EMAIL_USERNAME={{ email_username }}
+      - EMAIL_PASSWORD=${EMAIL_PASSWORD}
+      {% if email_encryption == "ssl" %}
+      - EMAIL_USE_SSL=True
+      {% elif email_encryption == "starttls" %}
+      - EMAIL_USE_TLS=True
+      {% endif %}
+      {% endif %}
+    networks:
+      {% if traefik_enabled %}
+      {{ traefik_network }}:
+      {% endif %}
+      {{ service_name }}_backend:
+    {% if not traefik_enabled %}
+    ports:
+      - "{{ ports_http }}:8080"
+    {% endif %}
+    volumes:
+      - {{ service_name }}_media:/opt/netbox/netbox/media
+      - {{ service_name }}_reports:/opt/netbox/netbox/reports
+      - {{ service_name }}_scripts:/opt/netbox/netbox/scripts
+    {% 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_domain }}`)
+      - traefik.http.routers.{{ service_name }}_http.entrypoints=web
+      {% 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_domain }}`)
+      - traefik.http.routers.{{ service_name }}_https.entrypoints=websecure
+      - traefik.http.routers.{{ service_name }}_https.tls=true
+      - traefik.http.routers.{{ service_name }}_https.tls.certresolver={{ traefik_tls_certresolver }}
+      {% endif %}
+    {% endif %}
+    depends_on:
+      {% if not database_external %}
+      - {{ service_name }}_postgres
+      {% endif %}
+      - {{ service_name }}_redis
+      - {{ service_name }}_redis-cache
+
+  {{ service_name }}_worker:
+    image: docker.io/netboxcommunity/netbox:v4.2.3
+    restart: {{ restart_policy }}
+    command:
+      - /opt/netbox/venv/bin/python
+      - /opt/netbox/netbox/manage.py
+      - rqworker
+    environment:
+      {% if database_external %}
+      - DB_HOST={{ database_host }}
+      {% else %}
+      - DB_HOST={{ service_name }}_postgres
+      {% endif %}
+      - DB_NAME={{ database_name }}
+      - DB_USER={{ database_user }}
+      - DB_PASSWORD=${DATABASE_PASSWORD}
+      - REDIS_HOST={{ service_name }}_redis
+      - REDIS_PASSWORD=${REDIS_PASSWORD}
+      - REDIS_CACHE_HOST={{ service_name }}_redis-cache
+      - REDIS_CACHE_PASSWORD=${REDIS_PASSWORD}
+      - SECRET_KEY=${NETBOX_SECRET_KEY}
+      {% if netbox_metrics_enabled %}
+      - METRICS_ENABLED=true
+      {% endif %}
+      {% if email_enabled %}
+      - EMAIL_SERVER={{ email_server }}
+      - EMAIL_PORT={{ email_port }}
+      - EMAIL_FROM={{ email_from }}
+      - EMAIL_USERNAME={{ email_username }}
+      - EMAIL_PASSWORD=${EMAIL_PASSWORD}
+      {% if email_encryption == "ssl" %}
+      - EMAIL_USE_SSL=True
+      {% elif email_encryption == "starttls" %}
+      - EMAIL_USE_TLS=True
+      {% endif %}
+      {% endif %}
+    networks:
+      {{ service_name }}_backend:
+    volumes:
+      - {{ service_name }}_media:/opt/netbox/netbox/media
+      - {{ service_name }}_reports:/opt/netbox/netbox/reports
+      - {{ service_name }}_scripts:/opt/netbox/netbox/scripts
+    depends_on:
+      {% if not database_external %}
+      - {{ service_name }}_postgres
+      {% endif %}
+      - {{ service_name }}
+      - {{ service_name }}_redis
+      - {{ service_name }}_redis-cache
+
+  {{ service_name }}_redis:
+    image: docker.io/library/redis:8.4.0-alpine
+    restart: {{ restart_policy }}
+    command:
+      - sh
+      - -c
+      - # FIXME: NEED TO REPAIR
+    environment:
+      - REDIS_PASSWORD=${REDIS_PASSWORD}
+    networks:
+      {{ service_name }}_backend:
+    volumes:
+      - {{ service_name }}_redis:/data
+
+  {{ service_name }}_redis-cache:
+    image: docker.io/library/redis:8.4.0-alpine
+    restart: {{ restart_policy }}
+    command:
+      - sh
+      - -c
+      - # FIXME: NEED TO REPAIR
+    environment:
+      - REDIS_PASSWORD=${REDIS_PASSWORD}
+    networks:
+      {{ service_name }}_backend:
+    volumes:
+      - {{ service_name }}_redis-cache:/data
+
+  {% if not database_external %}
+  {{ service_name }}_postgres:
+    image: docker.io/library/postgres:17.6
+    restart: {{ restart_policy }}
+    environment:
+      {% if container_timezone %}
+      - TZ={{ container_timezone }}
+      {% endif %}
+      - 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
+    networks:
+      - {{ service_name }}_backend
+    volumes:
+      {% if volume_mode == 'mount' %}
+      - {{ volume_mount_path }}/postgres:/var/lib/postgresql/data
+      {% elif volume_mode == 'local' or volume_mode == 'nfs' %}
+      - {{ service_name }}_postgres:/var/lib/postgresql/data
+      {% endif %}
   {% endif %}
-  - common/networks.yaml
-  - common/volumes.yaml
-  {% if swarm_enabled %}
-  - common/secrets.yaml
+
+
+networks:
+  {{ service_name }}_backend:
+    driver: bridge
+  {% if traefik_enabled %}
+  {{ traefik_network }}:
+    external: true
   {% endif %}
+
+volumes:
+  {% if not database_external %}
+  {{ service_name }}_postgres:
+    driver: local
+  {% endif %}
+  {{ service_name }}_redis:
+    driver: local
+  {{ service_name }}_redis-cache:
+    driver: local
+  {{ service_name }}_media:
+    driver: local
+  {{ service_name }}_reports:
+    driver: local
+  {{ service_name }}_scripts:
+    driver: local

+ 0 - 205
library/compose/netbox/services/netbox.yaml.j2

@@ -1,205 +0,0 @@
-{#
-  NetBox main web application service
-  Handles web UI, API, and application logic
-#}
-services:
-  {{ service_name }}:
-    image: docker.io/netboxcommunity/netbox:v4.2.3
-    {#
-      If not in swarm mode, apply restart policy and optional container name/hostname
-      Swarm mode handles restarts via deploy.restart_policy
-    #}
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    {% if container_name %}
-    container_name: {{ container_name }}
-    {% endif %}
-    {% endif %}
-    {#
-      Set container hostname if specified
-    #}
-    {% if container_hostname %}
-    hostname: {{ container_hostname }}
-    {% endif %}
-    {#
-      Service dependencies:
-      - Postgres: Only when using bundled database (Compose mode)
-      - Redis: Required for caching and task queue
-      - Redis-cache: Required for session caching
-    #}
-    {% if not swarm_enabled %}
-    depends_on:
-      {% if not database_external %}
-      - {{ service_name }}-postgres
-      {% endif %}
-      - {{ service_name }}-redis
-      - {{ service_name }}-redis-cache
-    {% endif %}
-    {#
-      Environment variables for NetBox configuration:
-      - TZ: Container timezone
-      - ALLOWED_HOSTS: Restrict access to specific hostnames
-      - DB_*: Database connection settings (external or bundled)
-      - REDIS_*: Redis connection settings for caching and task queue
-      - SECRET_KEY: Cryptographic signing key
-      - METRICS_ENABLED: Prometheus metrics (optional)
-      - EMAIL_*: Email configuration (optional)
-      Note: In Swarm mode, passwords are loaded from Docker secrets
-    #}
-    environment:
-      {% if container_timezone %}
-      - TZ={{ container_timezone }}
-      {% endif %}
-      {% if traefik_enabled %}
-      - ALLOWED_HOSTS={{ traefik_host }}.{{ traefik_domain }}
-      {% else %}
-      - ALLOWED_HOSTS=*
-      {% endif %}
-      - DB_HOST={{ database_host }}
-      - DB_NAME={{ database_name }}
-      - DB_USER={{ database_user }}
-      {% if swarm_enabled %}
-      - DB_PASSWORD_FILE=/run/secrets/{{ service_name }}_database_password
-      {% else %}
-      - DB_PASSWORD=${DATABASE_PASSWORD}
-      {% endif %}
-      - REDIS_HOST={{ service_name }}-redis
-      {% if swarm_enabled %}
-      - REDIS_PASSWORD_FILE=/run/secrets/{{ service_name }}_redis_password
-      {% else %}
-      - REDIS_PASSWORD=${REDIS_PASSWORD}
-      {% endif %}
-      - REDIS_CACHE_HOST={{ service_name }}-redis-cache
-      {% if swarm_enabled %}
-      - REDIS_CACHE_PASSWORD_FILE=/run/secrets/{{ service_name }}_redis_password
-      {% else %}
-      - REDIS_CACHE_PASSWORD=${REDIS_PASSWORD}
-      {% endif %}
-      {% if swarm_enabled %}
-      - SECRET_KEY_FILE=/run/secrets/{{ service_name }}_secret_key
-      {% else %}
-      - SECRET_KEY=${NETBOX_SECRET_KEY}
-      {% endif %}
-      {% if netbox_metrics_enabled %}
-      - METRICS_ENABLED=true
-      {% endif %}
-      {% if email_enabled %}
-      - EMAIL_SERVER={{ email_server }}
-      - EMAIL_PORT={{ email_port }}
-      - EMAIL_FROM={{ email_from }}
-      - EMAIL_USERNAME={{ email_username }}
-      {% if swarm_enabled %}
-      - EMAIL_PASSWORD_FILE=/run/secrets/{{ service_name }}_email_password
-      {% else %}
-      - EMAIL_PASSWORD=${EMAIL_PASSWORD}
-      {% endif %}
-      {% if email_encryption == "ssl" %}
-      - EMAIL_USE_SSL=True
-      {% elif email_encryption == "starttls" %}
-      - EMAIL_USE_TLS=True
-      {% endif %}
-      {% endif %}
-    {#
-      Network configuration:
-      - Traefik network: For external access via reverse proxy
-      - Backend network: Internal communication between NetBox services
-    #}
-    networks:
-      {% if traefik_enabled %}
-      {{ traefik_network }}:
-      {% endif %}
-      {{ service_name }}_backend:
-    {#
-      Port mappings when Traefik is disabled:
-      - HTTP: Web interface access
-      Note: Swarm mode uses 'host' mode for port publishing
-    #}
-    {% if not traefik_enabled %}
-    ports:
-      {% if swarm_enabled %}
-      - target: 8080
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      {% else %}
-      - "{{ ports_http }}:8080"
-      {% endif %}
-    {% endif %}
-    {#
-      Volume configuration for persistent data:
-      - media: Uploaded files and images
-      - reports: Custom reports
-      - scripts: Custom scripts
-    #}
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/media:/opt/netbox/netbox/media
-      - {{ volume_mount_path }}/reports:/opt/netbox/netbox/reports
-      - {{ volume_mount_path }}/scripts:/opt/netbox/netbox/scripts
-      {% elif volume_mode == 'local' or volume_mode == 'nfs' %}
-      - {{ service_name }}-media:/opt/netbox/netbox/media
-      - {{ service_name }}-reports:/opt/netbox/netbox/reports
-      - {{ service_name }}-scripts:/opt/netbox/netbox/scripts
-      {% endif %}
-    {#
-      Docker Swarm secrets for sensitive data
-    #}
-    {% if swarm_enabled %}
-    secrets:
-      - {{ service_name }}_database_password
-      - {{ service_name }}_redis_password
-      - {{ service_name }}_secret_key
-      {% if email_enabled %}
-      - {{ service_name }}_email_password
-      {% endif %}
-    {% endif %}
-    {#
-      Deploy configuration for Swarm mode:
-      - Can run on any node (stateless with external database)
-      - Traefik: Labels for reverse proxy integration
-    #}
-    {% if swarm_enabled %}
-    deploy:
-      mode: replicated
-      replicas: 1
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      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_domain }}`)
-        - 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_domain }}`)
-        - 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 %}
-    {#
-      Traefik labels for Compose mode
-    #}
-    {% if traefik_enabled and not swarm_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_domain }}`)
-      - 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_domain }}`)
-      - 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 %}

+ 0 - 28
library/compose/netbox/services/postgres.yaml.j2

@@ -1,28 +0,0 @@
-{#
-  PostgreSQL database service
-  Only deployed in Compose mode with internal database
-  Swarm mode requires external PostgreSQL database
-#}
-services:
-  {{ service_name }}-postgres:
-    image: docker.io/library/postgres:17.2-alpine
-    restart: {{ restart_policy }}
-    {% if container_name %}
-    container_name: {{ container_name }}-postgres
-    {% endif %}
-    {% if container_hostname %}
-    hostname: {{ container_hostname }}-postgres
-    {% endif %}
-    environment:
-      - TZ={{ container_timezone }}
-      - POSTGRES_USER={{ database_user }}
-      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
-      - POSTGRES_DB={{ database_name }}
-    networks:
-      - {{ service_name }}_backend
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/postgres:/var/lib/postgresql/data
-      {% elif volume_mode == 'local' or volume_mode == 'nfs' %}
-      - {{ service_name }}-postgres:/var/lib/postgresql/data
-      {% endif %}

+ 0 - 88
library/compose/netbox/services/redis.yaml.j2

@@ -1,88 +0,0 @@
-{#
-  Redis services for NetBox
-  - redis: Task queue and caching backend
-  - redis-cache: Session caching
-  Both must be pinned to specific node in Swarm mode (persistent data)
-#}
-services:
-  {{ service_name }}-redis:
-    image: docker.io/library/redis:8.4.0-alpine
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    {% if container_name %}
-    container_name: {{ container_name }}-redis
-    {% endif %}
-    {% endif %}
-    {% if container_hostname %}
-    hostname: {{ container_hostname }}-redis
-    {% endif %}
-    command:
-      - sh
-      - -c
-      - {% if swarm_enabled %}redis-server --appendonly yes --requirepass $$(cat /run/secrets/{{ service_name }}_redis_password){% else %}redis-server --appendonly yes --requirepass $$REDIS_PASSWORD{% endif %}
-
-    {% if not swarm_enabled %}
-    environment:
-      - REDIS_PASSWORD=${REDIS_PASSWORD}
-    {% endif %}
-    networks:
-      {{ service_name }}_backend:
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/redis:/data
-      {% elif volume_mode == 'local' or volume_mode == 'nfs' %}
-      - {{ service_name }}-redis:/data
-      {% endif %}
-    {% if swarm_enabled %}
-    secrets:
-      - {{ service_name }}_redis_password
-    deploy:
-      mode: replicated
-      replicas: 1
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      restart_policy:
-        condition: on-failure
-    {% endif %}
-
-  {{ service_name }}-redis-cache:
-    image: docker.io/library/redis:8.4.0-alpine
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    {% if container_name %}
-    container_name: {{ container_name }}-redis-cache
-    {% endif %}
-    {% endif %}
-    {% if container_hostname %}
-    hostname: {{ container_hostname }}-redis-cache
-    {% endif %}
-    command:
-      - sh
-      - -c
-      - {% if swarm_enabled %}redis-server --requirepass $$(cat /run/secrets/{{ service_name }}_redis_password){% else %}redis-server --requirepass $$REDIS_PASSWORD{% endif %}
-
-    {% if not swarm_enabled %}
-    environment:
-      - REDIS_PASSWORD=${REDIS_PASSWORD}
-    {% endif %}
-    networks:
-      {{ service_name }}_backend:
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/redis-cache:/data
-      {% elif volume_mode == 'local' or volume_mode == 'nfs' %}
-      - {{ service_name }}-redis-cache:/data
-      {% endif %}
-    {% if swarm_enabled %}
-    secrets:
-      - {{ service_name }}_redis_password
-    deploy:
-      mode: replicated
-      replicas: 1
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      restart_policy:
-        condition: on-failure
-    {% endif %}

+ 0 - 101
library/compose/netbox/services/worker.yaml.j2

@@ -1,101 +0,0 @@
-{#
-  NetBox worker service
-  Processes background tasks from Redis queue
-  Can scale horizontally in Swarm mode for better queue processing
-#}
-services:
-  {{ service_name }}-worker:
-    image: docker.io/netboxcommunity/netbox:v4.2.3
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    {% if container_name %}
-    container_name: {{ container_name }}-worker
-    {% endif %}
-    {% endif %}
-    {% if container_hostname %}
-    hostname: {{ container_hostname }}-worker
-    {% endif %}
-    command:
-      - /opt/netbox/venv/bin/python
-      - /opt/netbox/netbox/manage.py
-      - rqworker
-    {% if not swarm_enabled %}
-    depends_on:
-      - {{ service_name }}
-      - {{ service_name }}-redis
-      - {{ service_name }}-redis-cache
-    {% endif %}
-    environment:
-      - TZ={{ container_timezone }}
-      - DB_HOST={{ database_host }}
-      - DB_NAME={{ database_name }}
-      - DB_USER={{ database_user }}
-      {% if swarm_enabled %}
-      - DB_PASSWORD_FILE=/run/secrets/{{ service_name }}_database_password
-      {% else %}
-      - DB_PASSWORD=${DATABASE_PASSWORD}
-      {% endif %}
-      - REDIS_HOST={{ service_name }}-redis
-      {% if swarm_enabled %}
-      - REDIS_PASSWORD_FILE=/run/secrets/{{ service_name }}_redis_password
-      {% else %}
-      - REDIS_PASSWORD=${REDIS_PASSWORD}
-      {% endif %}
-      - REDIS_CACHE_HOST={{ service_name }}-redis-cache
-      {% if swarm_enabled %}
-      - REDIS_CACHE_PASSWORD_FILE=/run/secrets/{{ service_name }}_redis_password
-      {% else %}
-      - REDIS_CACHE_PASSWORD=${REDIS_PASSWORD}
-      {% endif %}
-      {% if swarm_enabled %}
-      - SECRET_KEY_FILE=/run/secrets/{{ service_name }}_secret_key
-      {% else %}
-      - SECRET_KEY=${NETBOX_SECRET_KEY}
-      {% endif %}
-      {% if email_enabled %}
-      - EMAIL_SERVER={{ email_server }}
-      - EMAIL_PORT={{ email_port }}
-      - EMAIL_FROM={{ email_from }}
-      - EMAIL_USERNAME={{ email_username }}
-      {% if swarm_enabled %}
-      - EMAIL_PASSWORD_FILE=/run/secrets/{{ service_name }}_email_password
-      {% else %}
-      - EMAIL_PASSWORD=${EMAIL_PASSWORD}
-      {% endif %}
-      {% if email_encryption == "ssl" %}
-      - EMAIL_USE_SSL=True
-      {% elif email_encryption == "starttls" %}
-      - EMAIL_USE_TLS=True
-      {% endif %}
-      {% endif %}
-    networks:
-      {{ service_name }}_backend:
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/media:/opt/netbox/netbox/media
-      - {{ volume_mount_path }}/reports:/opt/netbox/netbox/reports
-      - {{ volume_mount_path }}/scripts:/opt/netbox/netbox/scripts
-      {% elif volume_mode == 'local' or volume_mode == 'nfs' %}
-      - {{ service_name }}-media:/opt/netbox/netbox/media
-      - {{ service_name }}-reports:/opt/netbox/netbox/reports
-      - {{ service_name }}-scripts:/opt/netbox/netbox/scripts
-      {% endif %}
-    {% if swarm_enabled %}
-    secrets:
-      - {{ service_name }}_database_password
-      - {{ service_name }}_redis_password
-      - {{ service_name }}_secret_key
-      {% if email_enabled %}
-      - {{ service_name }}_email_password
-      {% endif %}
-    deploy:
-      mode: {{ swarm_worker_placement_mode }}
-      {% if swarm_worker_placement_mode == 'replicated' %}
-      replicas: {{ swarm_worker_replicas }}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_worker_placement_host }}
-      {% endif %}
-      restart_policy:
-        condition: on-failure
-    {% endif %}

+ 0 - 4
library/compose/netbox/template.yaml

@@ -3,8 +3,6 @@ metadata:
     name: NetBox
     description: |-
         Network infrastructure management (IPAM/DCIM) and network automation source of truth. Provides comprehensive API for managing IP addresses, circuits, devices, racks, cables, and other network infrastructure components with powerful automation capabilities.
-        ## :warning: Swarm Deployment
-        NetBox in Swarm mode requires an external PostgreSQL database (`database_external=true`).
         ## References
         * **Project:** https://netbox.dev/
         * **Documentation:** https://docs.netbox.dev/
@@ -14,8 +12,6 @@ metadata:
     date: "2025-11-13"
     tags:
         - traefik
-        - swarm
-        - volume_modes
     draft: false
 schema: "1.2"
 spec:

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

@@ -19,6 +19,7 @@ metadata:
   date: '2025-10-31'
   tags:
     - traefik
+  draft: true
 spec:
   database:
     required: true

+ 0 - 148
library/compose/nginxproxymanager/compose.yaml.j2

@@ -1,148 +0,0 @@
-services:
-  {{ service_name }}:
-    image: docker.io/jc21/nginx-proxy-manager:2.12.6
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
-    environment:
-      - TZ={{ container_timezone }}
-      - DB_MYSQL_HOST={{ service_name }}-db
-      - DB_MYSQL_PORT={{ database_port }}
-      - DB_MYSQL_USER={{ database_user }}
-      {% if swarm_enabled %}
-      - DB_MYSQL_PASSWORD=/run/secrets/database_password
-      {% else %}
-      - DB_MYSQL_PASSWORD={{ database_password }}
-      {% endif %}
-      - DB_MYSQL_NAME={{ database_name }}
-    ports:
-      - {{ ports_http }}:80
-      - 81:81
-      - {{ ports_https }}:443
-    volumes:
-      {% if volume_mode == 'local' %}
-      - {{ service_name }}_data:/data
-      - {{ service_name }}_ssl:/etc/letsencrypt
-      {% elif volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/{{ service_name }}/data:/data
-      - {{ volume_mount_path }}/{{ service_name }}/ssl:/etc/letsencrypt
-      {% elif volume_mode == 'nfs' %}
-      - type: volume
-        source: {{ service_name }}_data
-        target: /data
-        volume:
-          nocopy: true
-      - type: volume
-        source: {{ service_name }}_ssl
-        target: /etc/letsencrypt
-        volume:
-          nocopy: true
-      {% endif %}
-    {% endif %}
-    depends_on:
-      - {{ service_name }}-db
-    {% if swarm_enabled %}
-    secrets:
-      - database_password
-    deploy:
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% else %}
-      mode: global
-      {% endif %}
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-      {% endif %}
-    {% endif %}
-  {{ service_name }}-db:
-    image: docker.io/jc21/mariadb-aria:10.11.5
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}-db
-    {% endif %}
-    hostname: {{ container_hostname }}-db
-    environment:
-      - TZ={{ container_timezone }}
-      {% if swarm_enabled %}
-      - MYSQL_ROOT_PASSWORD=/run/secrets/database_root_password
-      - MYSQL_PASSWORD=/run/secrets/database_password
-      {% else %}
-      - MYSQL_ROOT_PASSWORD={{ database_root_password }}
-      - MYSQL_PASSWORD={{ database_password }}
-      {% endif %}
-      - MYSQL_DATABASE={{ database_name }}
-      - MYSQL_USER={{ database_user }}
-    volumes:
-      {% if volume_mode == 'local' %}
-      - {{ service_name }}_db:/var/lib/mysql
-      {% elif volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/{{ service_name }}/db:/var/lib/mysql
-      {% elif volume_mode == 'nfs' %}
-      - type: volume
-        source: {{ service_name }}_db
-        target: /var/lib/mysql
-        volume:
-          nocopy: true
-      {% endif %}
-    {% endif %}
-    {% if swarm_enabled %}
-    secrets:
-      - database_root_password
-      - database_password
-    deploy:
-      replicas: 1
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-      {% endif %}
-    {% endif %}
-    {% endif %}
-{% endif %}
-volumes:
-  {{ service_name }}_data:
-    {% if volume_mode == 'nfs' %}
-    driver: local
-    driver_opts:
-      type: nfs
-      o: {{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/{{ service_name }}/data"
-    {% endif %}
-  {{ service_name }}_ssl:
-    {% if volume_mode == 'nfs' %}
-    driver: local
-    driver_opts:
-      type: nfs
-      o: {{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/{{ service_name }}/ssl"
-    {% endif %}
-  {{ service_name }}_db:
-    {% if volume_mode == 'nfs' %}
-    driver: local
-    driver_opts:
-      type: nfs
-      o: {{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/{{ service_name }}/db"
-    {% endif %}
-{% if swarm_enabled %}
-secrets:
-  database_root_password:
-    external: true
-  database_password:
-    external: true
-{% endif %}

+ 0 - 188
library/compose/nginxproxymanager/compose.yaml.j2.bak3

@@ -1,188 +0,0 @@
-services:
-  {{ service_name }}:
-    image: docker.io/jc21/nginx-proxy-manager:2.12.6
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
-    environment:
-      - TZ={{ container_timezone }}
-      - DB_MYSQL_HOST={{ service_name }}-db
-      - DB_MYSQL_PORT={{ database_port }}
-      - DB_MYSQL_USER={{ database_user }}
-      {% if swarm_enabled %}
-      - DB_MYSQL_PASSWORD=/run/secrets/database_password
-      {% else %}
-      - DB_MYSQL_PASSWORD={{ database_password }}
-      {% endif %}
-      - DB_MYSQL_NAME={{ database_name }}
-    ports:
-      - {{ ports_http }}:80
-      - 81:81
-      - {{ ports_https }}:443
-    volumes:
-      {% if volume_mode == 'local' %}
-      - {{ service_name }}_data:/data
-      - {{ service_name }}_ssl:/etc/letsencrypt
-      {% elif volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/{{ service_name }}/data:/data
-      - {{ volume_mount_path }}/{{ service_name }}/ssl:/etc/letsencrypt
-      {% elif volume_mode == 'nfs' %}
-      - type: volume
-        source: {{ service_name }}_data
-        target: /data
-        volume:
-          nocopy: true
-      - type: volume
-        source: {{ service_name }}_ssl
-        target: /etc/letsencrypt
-        volume:
-          nocopy: true
-      {% endif %}
-    {% 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 %}
-    {% endif %}
-    depends_on:
-      - {{ service_name }}-db
-    {% if swarm_enabled %}
-    secrets:
-      - database_password
-    deploy:
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% else %}
-      mode: global
-      {% endif %}
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-      {% endif %}
-    {% endif %}
-  {{ service_name }}-db:
-    image: docker.io/jc21/mariadb-aria:10.11.5
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}-db
-    {% endif %}
-    hostname: {{ container_hostname }}-db
-    environment:
-      - TZ={{ container_timezone }}
-      {% if swarm_enabled %}
-      - MYSQL_ROOT_PASSWORD=/run/secrets/database_root_password
-      - MYSQL_PASSWORD=/run/secrets/database_password
-      {% else %}
-      - MYSQL_ROOT_PASSWORD={{ database_root_password }}
-      - MYSQL_PASSWORD={{ database_password }}
-      {% endif %}
-      - MYSQL_DATABASE={{ database_name }}
-      - MYSQL_USER={{ database_user }}
-    volumes:
-      {% if volume_mode == 'local' %}
-      - {{ service_name }}_db:/var/lib/mysql
-      {% elif volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/{{ service_name }}/db:/var/lib/mysql
-      {% elif volume_mode == 'nfs' %}
-      - type: volume
-        source: {{ service_name }}_db
-        target: /var/lib/mysql
-        volume:
-          nocopy: true
-      {% endif %}
-    {% if network_mode == 'host' %}
-    network_mode: host
-    {% else %}
-    networks:
-      {% if network_mode == 'macvlan' %}
-      {{ network_name }}:
-      {% elif network_mode == 'bridge' %}
-      {{ network_name }}:
-      {% endif %}
-    {% endif %}
-    {% if swarm_enabled %}
-    secrets:
-      - database_root_password
-      - database_password
-    deploy:
-      replicas: 1
-      {% if resources_enabled %}
-      resources:
-        limits:
-          cpus: '{{ resources_cpu_limit }}'
-          memory: {{ resources_memory_limit }}
-        reservations:
-          cpus: '{{ resources_cpu_reservation }}'
-          memory: {{ resources_memory_reservation }}
-      {% endif %}
-    {% endif %}
-{% 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 %}
-{% endif %}
-volumes:
-  {{ service_name }}_data:
-    {% if volume_mode == 'nfs' %}
-    driver: local
-    driver_opts:
-      type: nfs
-      o: {{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/{{ service_name }}/data"
-    {% endif %}
-  {{ service_name }}_ssl:
-    {% if volume_mode == 'nfs' %}
-    driver: local
-    driver_opts:
-      type: nfs
-      o: {{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/{{ service_name }}/ssl"
-    {% endif %}
-  {{ service_name }}_db:
-    {% if volume_mode == 'nfs' %}
-    driver: local
-    driver_opts:
-      type: nfs
-      o: {{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/{{ service_name }}/db"
-    {% endif %}
-{% if swarm_enabled %}
-secrets:
-  database_root_password:
-    external: true
-  database_password:
-    external: true
-{% endif %}

+ 0 - 51
library/compose/nginxproxymanager/template.yaml

@@ -1,51 +0,0 @@
----
-kind: compose
-schema: "1.2"
-metadata:
-  name: Nginx Proxy Manager
-  description: |
-    Nginx Proxy Manager is a lightweight, easy-to-use reverse proxy management tool with a beautiful web interface.
-    It enables you to easily forward traffic to your websites running at home or in the cloud, complete with
-    free SSL certificates via Let's Encrypt.
-    
-    Project: https://nginxproxymanager.com/
-    Documentation: https://nginxproxymanager.com/guide/
-    GitHub: https://github.com/NginxProxyManager/nginx-proxy-manager
-  version: 2.12.6
-  author: Christian Lempa
-  date: '2025-09-28'
-  tags:
-    - proxy
-    - nginx
-    - ssl
-spec:
-  general:
-    vars:
-      service_name:
-        default: npm
-      container_name:
-        default: nginx-proxy-manager
-      container_hostname:
-        default: nginx-proxy-manager
-  database:
-    required: true
-    vars:
-      database_name:
-        default: npm
-      database_user:
-        default: npm
-      database_password:
-        default: changeme
-      database_port:
-        default: 3306
-      database_root_password:
-        type: str
-        description: Database root password
-        sensitive: true
-        default: changeme
-  ports:
-    vars:
-      ports_http:
-        default: 80
-      ports_https:
-        default: 443

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

@@ -20,6 +20,7 @@ metadata:
     - swarm
     - proxy
     - wireguard
+  draft: true
   next_steps: |
     ### 1. Configure Database
     {% if postgres_enabled -%}

+ 0 - 11
library/compose/traefik/common/configs.yaml.j2

@@ -1,11 +0,0 @@
----
-{#
-  When swarm mode is enabled, define the necessary configs for Traefik.
-  These configs will be used to store Traefik's main configuration as well as
-  additional dynamic configuration files for routers, services, and middlewares.
-#}
-{% if swarm_enabled %}
-configs:
-  traefik_config:
-    file: ../config/traefik.yaml
-{% endif %}

+ 0 - 17
library/compose/traefik/common/networks.yaml.j2

@@ -1,17 +0,0 @@
----
-{#
-  Always define the traefik network, but if it's not external, set it up according to swarm mode or bridge mode.
-#}
-networks:
-  {{ traefik_network }}:
-    {% if traefik_network_external %}
-    external: true
-    {% else %}
-    {% if swarm_enabled %}
-    driver: overlay
-    attachable: true
-    {% else %}
-    driver: bridge
-    {% endif %}
-    name: {{ traefik_network }}
-    {% endif %}

+ 0 - 12
library/compose/traefik/common/secrets.yaml.j2

@@ -1,12 +0,0 @@
----
-{#
-  Define secrets for Swarm mode.
-  All ACME providers use token (primary credential) and token_key (secondary credential if needed).
-#}
-{% if traefik_tls_enabled and swarm_enabled %}
-secrets:
-  {{ service_name }}_token:
-    file: ../.env.secret.token
-  {{ service_name }}_token_key:
-    file: ../.env.secret.token_key
-{% endif %}

+ 0 - 17
library/compose/traefik/common/volumes.yaml.j2

@@ -1,17 +0,0 @@
----
-{#
-  
-#}
-{% if volume_mode == 'local' %}
-volumes:
-  {{ service_name }}_certs:
-    driver: local
-{% elif volume_mode == 'nfs' %}
-volumes:
-  {{ service_name }}_certs:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}"
-{% endif %}

+ 185 - 6
library/compose/traefik/compose.yaml.j2

@@ -1,11 +1,190 @@
 ---
-include:
-  - services/traefik.yaml
-  - common/networks.yaml
-  - common/volumes.yaml
+services:
+  {{ service_name }}:
+    image: docker.io/library/traefik:v3.5.4
+    {% if not swarm_enabled %}
+    {% if container_name %}
+    container_name: {{ container_name }}
+    {% endif %}
+    security_opt:
+      - no-new-privileges:true
+    {% endif %}
+    {% if container_hostname %}
+    hostname: {{ container_hostname }}
+    {% endif %}
+    ports:
+      - "{{ ports_http }}:80"
+      - "{{ ports_https }}:443"
+      {% if dashboard_enabled %}
+      - "{{ ports_dashboard }}:8080"
+      {% endif %}
+    {#
+      We always need volumes, because of the docker socket and certs storage
+      - certs storage for ACME
+      - docker socket for automatic service discovery
+      - Traefik configuration in non-swarm mode
+    #}
+    volumes:
+      - /var/run/docker.sock:/var/run/docker.sock:ro
+      {% if not swarm_enabled %}
+      {% if volume_mode == 'mount' and volume_mount_path%}
+      - {{ volume_mount_path }}/config/:/etc/traefik/:ro
+      {% else %}
+      - ./config/:/etc/traefik/:ro
+      {% endif %}
+      {% endif %}
+      {% if volume_mode == 'mount' %}
+      - {{ volume_mount_path }}:/var/traefik/certs/:rw
+      {% elif volume_mode == 'local' or volume_mode == 'nfs' %}
+      - {{ service_name }}_certs:/var/traefik/certs/:rw
+      {% endif %}
+    environment:
+      - TZ={{ container_timezone }}
+      {% if traefik_tls_enabled %}
+      {% if traefik_tls_certresolver == 'cloudflare' %}
+      {% if swarm_enabled %}
+      - CF_DNS_API_TOKEN_FILE=/run/secrets/{{ service_name }}_token
+      {% else %}
+      - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
+      {% endif %}
+      {% elif traefik_tls_certresolver == 'porkbun' %}
+      {% if swarm_enabled %}
+      - PORKBUN_API_KEY_FILE=/run/secrets/{{ service_name }}_token
+      - PORKBUN_SECRET_API_KEY_FILE=/run/secrets/{{ service_name }}_token_key
+      {% else %}
+      - PORKBUN_API_KEY=${PORKBUN_API_KEY}
+      - PORKBUN_SECRET_API_KEY=${PORKBUN_SECRET_API_KEY}
+      {% endif %}
+      {% elif traefik_tls_certresolver == 'route53' %}
+      {% if swarm_enabled %}
+      - AWS_ACCESS_KEY_ID_FILE=/run/secrets/{{ service_name }}_token
+      - AWS_SECRET_ACCESS_KEY_FILE=/run/secrets/{{ service_name }}_token_key
+      {% else %}
+      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
+      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
+      {% endif %}
+      - AWS_REGION={{ traefik_tls_acme_region }}
+      {% elif traefik_tls_certresolver == 'digitalocean' %}
+      {% if swarm_enabled %}
+      - DO_AUTH_TOKEN_FILE=/run/secrets/{{ service_name }}_token
+      {% else %}
+      - DO_AUTH_TOKEN=${DO_AUTH_TOKEN}
+      {% endif %}
+      {% elif traefik_tls_certresolver == 'godaddy' %}
+      {% if swarm_enabled %}
+      - GODADDY_API_KEY_FILE=/run/secrets/{{ service_name }}_token
+      - GODADDY_API_SECRET_FILE=/run/secrets/{{ service_name }}_token_key
+      {% else %}
+      - GODADDY_API_KEY=${GODADDY_API_KEY}
+      - GODADDY_API_SECRET=${GODADDY_API_SECRET}
+      {% endif %}
+      {% elif traefik_tls_certresolver == 'azure' %}
+      {% if swarm_enabled %}
+      - AZURE_CLIENT_ID_FILE=/run/secrets/{{ service_name }}_token
+      - AZURE_CLIENT_SECRET_FILE=/run/secrets/{{ service_name }}_token_key
+      {% else %}
+      - AZURE_CLIENT_ID=${AZURE_CLIENT_ID}
+      - AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET}
+      {% endif %}
+      - AZURE_TENANT_ID={{ traefik_tls_acme_tenant_id }}
+      - AZURE_SUBSCRIPTION_ID={{ traefik_tls_acme_subscription_id }}
+      - AZURE_RESOURCE_GROUP={{ traefik_tls_acme_resource_group }}
+      {% elif traefik_tls_certresolver == 'namecheap' %}
+      {% if swarm_enabled %}
+      - NAMECHEAP_API_KEY_FILE=/run/secrets/{{ service_name }}_token
+      {% else %}
+      - NAMECHEAP_API_KEY=${NAMECHEAP_API_KEY}
+      {% endif %}
+      - NAMECHEAP_API_USER={{ traefik_tls_acme_username }}
+      {% endif %}
+      {% endif %}
+    {% if swarm_enabled %}
+    configs:
+      - source: traefik_config
+        target: /etc/traefik/traefik.yaml
+    {% endif %}
+    healthcheck:
+      test: ["CMD", "traefik", "healthcheck", "--ping"]
+      interval: 30s
+      timeout: 5s
+      retries: 3
+      start_period: 10s
+    networks:
+      - {{ traefik_network }}
+    {% if swarm_enabled %}
+    {% if traefik_tls_enabled %}
+    secrets:
+      - {{ service_name }}_token
+      {% if traefik_tls_acme_secret_key %}
+      - {{ service_name }}_token_key
+      {% endif %}
+    {% endif %}
+    deploy:
+      mode: {{ swarm_placement_mode }}
+      {% if swarm_placement_mode == 'replicated' %}
+      replicas: {{ swarm_replicas }}
+      {% endif %}
+      {% if swarm_placement_host %}
+      placement:
+        constraints:
+          - node.hostname == {{ swarm_placement_host }}
+      {% endif %}
+    {% else %}
+    restart: {{ restart_policy }}
+    {% endif %}
+
+{#
+  When swarm mode is enabled, define the necessary configs for Traefik.
+  These configs will be used to store Traefik's main configuration as well as
+  additional dynamic configuration files for routers, services, and middlewares.
+  If Traefik TLS is enabled, also define the necessary secrets for ACME DNS challenge.
+#}
 {% if swarm_enabled %}
+configs:
+  traefik_config:
+    file: ./config/traefik.yaml
 {% if traefik_tls_enabled %}
-  - common/secrets.yaml
+secrets:
+  {{ service_name }}_token:
+    file: ./.env.secret.token
+  {% if traefik_tls_acme_secret_key %}
+  {{ service_name }}_token_key:
+    file: ./.env.secret.token_key
+  {% endif %}
 {% endif %}
-  - common/configs.yaml
+{% endif %}
+
+{#
+  Always define the traefik network, but if it's not external, set it up according to
+  swarm mode or bridge mode.
+#}
+networks:
+  {{ traefik_network }}:
+    {% if traefik_network_external %}
+    external: true
+    {% else %}
+    {% if swarm_enabled %}
+    driver: overlay
+    attachable: true
+    {% else %}
+    driver: bridge
+    {% endif %}
+    name: {{ traefik_network }}
+    {% endif %}
+
+{#
+  Always define volumes for certs based on volume_mode
+#}
+{% if volume_mode == 'local' %}
+volumes:
+  {{ service_name }}_certs:
+    driver: local
+{% elif volume_mode == 'nfs' %}
+volumes:
+  {{ service_name }}_certs:
+    driver: local
+    driver_opts:
+      type: nfs
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}"
 {% endif %}

+ 0 - 132
library/compose/traefik/services/traefik.yaml.j2

@@ -1,132 +0,0 @@
----
-services:
-  {{ service_name }}:
-    image: docker.io/library/traefik:v3.5.4
-    {% if not swarm_enabled %}
-    {% if container_name %}
-    container_name: {{ container_name }}
-    {% endif %}
-    security_opt:
-      - no-new-privileges:true
-    {% endif %}
-    {% if container_hostname %}
-    hostname: {{ container_hostname }}
-    {% endif %}
-    ports:
-      - "{{ ports_http }}:80"
-      - "{{ ports_https }}:443"
-      {% if dashboard_enabled %}
-      - "{{ ports_dashboard }}:8080"
-      {% endif %}
-    {#
-      We always need volumes, because of the docker socket and certs storage
-      - certs storage for ACME
-      - docker socket for automatic service discovery
-      - Traefik configuration in non-swarm mode
-    #}
-    volumes:
-      - /var/run/docker.sock:/var/run/docker.sock:ro
-      {% if not swarm_enabled %}
-      {% if volume_mode == 'mount' and volume_mount_path%}
-      - {{ volume_mount_path }}/config/:/etc/traefik/:ro
-      {% else %}
-      - ../config/:/etc/traefik/:ro
-      {% endif %}
-      {% endif %}
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}:/var/traefik/certs/:rw
-      {% elif volume_mode == 'local' or volume_mode == 'nfs' %}
-      - {{ service_name }}_certs:/var/traefik/certs/:rw
-      {% endif %}
-    environment:
-      - TZ={{ container_timezone }}
-      {% if traefik_tls_enabled %}
-      {% if traefik_tls_certresolver == 'cloudflare' %}
-      {% if swarm_enabled %}
-      - CF_DNS_API_TOKEN_FILE=/run/secrets/{{ service_name }}_token
-      {% else %}
-      - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
-      {% endif %}
-      {% elif traefik_tls_certresolver == 'porkbun' %}
-      {% if swarm_enabled %}
-      - PORKBUN_API_KEY_FILE=/run/secrets/{{ service_name }}_token
-      - PORKBUN_SECRET_API_KEY_FILE=/run/secrets/{{ service_name }}_token_key
-      {% else %}
-      - PORKBUN_API_KEY=${PORKBUN_API_KEY}
-      - PORKBUN_SECRET_API_KEY=${PORKBUN_SECRET_API_KEY}
-      {% endif %}
-      {% elif traefik_tls_certresolver == 'route53' %}
-      {% if swarm_enabled %}
-      - AWS_ACCESS_KEY_ID_FILE=/run/secrets/{{ service_name }}_token
-      - AWS_SECRET_ACCESS_KEY_FILE=/run/secrets/{{ service_name }}_token_key
-      {% else %}
-      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
-      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
-      {% endif %}
-      - AWS_REGION={{ traefik_tls_acme_region }}
-      {% elif traefik_tls_certresolver == 'digitalocean' %}
-      {% if swarm_enabled %}
-      - DO_AUTH_TOKEN_FILE=/run/secrets/{{ service_name }}_token
-      {% else %}
-      - DO_AUTH_TOKEN=${DO_AUTH_TOKEN}
-      {% endif %}
-      {% elif traefik_tls_certresolver == 'godaddy' %}
-      {% if swarm_enabled %}
-      - GODADDY_API_KEY_FILE=/run/secrets/{{ service_name }}_token
-      - GODADDY_API_SECRET_FILE=/run/secrets/{{ service_name }}_token_key
-      {% else %}
-      - GODADDY_API_KEY=${GODADDY_API_KEY}
-      - GODADDY_API_SECRET=${GODADDY_API_SECRET}
-      {% endif %}
-      {% elif traefik_tls_certresolver == 'azure' %}
-      {% if swarm_enabled %}
-      - AZURE_CLIENT_ID_FILE=/run/secrets/{{ service_name }}_token
-      - AZURE_CLIENT_SECRET_FILE=/run/secrets/{{ service_name }}_token_key
-      {% else %}
-      - AZURE_CLIENT_ID=${AZURE_CLIENT_ID}
-      - AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET}
-      {% endif %}
-      - AZURE_TENANT_ID={{ traefik_tls_acme_tenant_id }}
-      - AZURE_SUBSCRIPTION_ID={{ traefik_tls_acme_subscription_id }}
-      - AZURE_RESOURCE_GROUP={{ traefik_tls_acme_resource_group }}
-      {% elif traefik_tls_certresolver == 'namecheap' %}
-      {% if swarm_enabled %}
-      - NAMECHEAP_API_KEY_FILE=/run/secrets/{{ service_name }}_token
-      {% else %}
-      - NAMECHEAP_API_KEY=${NAMECHEAP_API_KEY}
-      {% endif %}
-      - NAMECHEAP_API_USER={{ traefik_tls_acme_username }}
-      {% endif %}
-      {% endif %}
-    {% if swarm_enabled %}
-    configs:
-      - source: traefik_config
-        target: /etc/traefik/traefik.yaml
-    {% endif %}
-    healthcheck:
-      test: ["CMD", "traefik", "healthcheck", "--ping"]
-      interval: 30s
-      timeout: 5s
-      retries: 3
-      start_period: 10s
-    networks:
-      - {{ traefik_network }}
-    {% if swarm_enabled %}
-    {% if traefik_tls_enabled %}
-    secrets:
-      - {{ service_name }}_token
-      - {{ service_name }}_token_key
-    {% endif %}
-    deploy:
-      mode: {{ swarm_placement_mode }}
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      {% endif %}
-      {% if swarm_placement_host %}
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      {% endif %}
-    {% else %}
-    restart: {{ restart_policy }}
-    {% endif %}

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

@@ -21,7 +21,7 @@ metadata:
     Start the `{{ service_name }}` project
     {% if swarm_enabled %}
     1. Deploy Traefik to Docker Swarm:
-      `docker stack deploy -c docker-compose.yaml traefik
+      `docker stack deploy -c docker-compose.yaml {{ service_name }}`
     {% else %}
     1. Copy the project directory for `{{ service_name }}` to the host.
     2. Start Traefik with Docker Compose from the project directory:
@@ -145,10 +145,8 @@ spec:
         needs: ["traefik_tls_certresolver=azure"]
       traefik_tls_acme_email:
         type: "str"
-        description: "Email address for ACME (Let's Encrypt) registration"
-        default: "admin@example.com"
+        description: "Email address for ACME"
         required: true
-        extra: "Required for Let's Encrypt certificate requests"
       traefik_tls_redirect:
         type: "bool"
         description: "Redirect all HTTP traffic to HTTPS"