xcad 2 сар өмнө
parent
commit
7bf03312f1
70 өөрчлөгдсөн 864 нэмэгдсэн , 2516 устгасан
  1. 1 0
      CHANGELOG.md
  2. 9 2
      cli/core/display/display_icons.py
  3. 93 6
      cli/core/display/display_status.py
  4. 5 3
      cli/core/schema/compose/v1.2.json
  5. 2 1
      cli/core/template/variable_collection.py
  6. 25 13
      library/compose/adguardhome/compose.yaml.j2
  7. 3 0
      library/compose/alloy/template.yaml
  8. 1 2
      library/compose/bind9/template.yaml
  9. 5 1
      library/compose/checkmk/compose.yaml.j2
  10. 8 1
      library/compose/checkmk/template.yaml
  11. 1 1
      library/compose/dockge/template.yaml
  12. 42 35
      library/compose/gitea/compose.yaml.j2
  13. 11 9
      library/compose/gitea/template.yaml
  14. 1 1
      library/compose/gitlab-runner/template.yaml
  15. 2 2
      library/compose/gitlab/.env.j2
  16. 0 0
      library/compose/gitlab/.env.password.j2
  17. 0 0
      library/compose/gitlab/.env.registry.j2
  18. 65 131
      library/compose/gitlab/compose.yaml.j2
  19. 3 16
      library/compose/gitlab/template.yaml
  20. 41 5
      library/compose/grafana/compose.yaml.j2
  21. 16 22
      library/compose/grafana/template.yaml
  22. 10 26
      library/compose/homepage/template.yaml
  23. 1 0
      library/compose/influxdb/template.yaml
  24. 2 1
      library/compose/mariadb/template.yaml
  25. 2 2
      library/compose/n8n/template.yaml
  26. 7 5
      library/compose/netbox/template.yaml
  27. 1 0
      library/compose/nginx/template.yaml
  28. 2 1
      library/compose/openwebui/template.yaml
  29. 0 137
      library/compose/pangolin/compose.yaml.j2.final
  30. 0 113
      library/compose/pangolin/template.yaml.backup
  31. 25 20
      library/compose/passbolt/compose.yaml.j2
  32. 12 116
      library/compose/passbolt/template.yaml
  33. 0 38
      library/compose/pihole/common/networks.yaml.j2
  34. 0 3
      library/compose/pihole/common/secrets.yaml.j2
  35. 0 21
      library/compose/pihole/common/volumes.yaml.j2
  36. 262 8
      library/compose/pihole/compose.yaml.j2
  37. 0 266
      library/compose/pihole/compose.yaml.j2.backup
  38. 0 133
      library/compose/pihole/services/pihole.yaml.j2
  39. 15 30
      library/compose/pihole/template.yaml
  40. 42 98
      library/compose/portainer/compose.yaml.j2
  41. 0 124
      library/compose/portainer/compose.yaml.j2.final
  42. 0 118
      library/compose/portainer/compose.yaml.j2.portfix
  43. 11 17
      library/compose/portainer/template.yaml
  44. 2 2
      library/compose/postgres/template.yaml
  45. 2 23
      library/compose/renovate/template.yaml
  46. 2 15
      library/compose/semaphoreui/.env.j2
  47. 48 147
      library/compose/semaphoreui/compose.yaml.j2
  48. 0 264
      library/compose/semaphoreui/compose.yaml.j2.bak3
  49. 0 222
      library/compose/semaphoreui/compose.yaml.j2.final
  50. 0 204
      library/compose/semaphoreui/compose.yaml.j2.portfix
  51. 60 88
      library/compose/semaphoreui/template.yaml
  52. 1 1
      library/compose/traefik/compose.yaml.j2
  53. 2 3
      library/compose/traefik/template.yaml
  54. 2 1
      library/compose/twingate-connector/template.yaml
  55. 2 1
      library/compose/uptimekuma/template.yaml
  56. 1 1
      library/compose/whoami/template.yaml
  57. 2 2
      library/helm/certmanager/template.yaml
  58. 1 1
      library/kubernetes/certmanager-certificate/template.yaml
  59. 1 1
      library/kubernetes/certmanager-clusterissuer/template.yaml
  60. 1 1
      library/kubernetes/certmanager-issuer/template.yaml
  61. 1 1
      library/kubernetes/traefik-ingressroute/template.yaml
  62. 1 1
      library/kubernetes/traefik-ingressroutetcp/template.yaml
  63. 2 2
      library/kubernetes/traefik-middleware/template.yaml
  64. 1 1
      library/terraform/netbox-cluster-type/template.yaml
  65. 1 1
      library/terraform/netbox-cluster/template.yaml
  66. 1 1
      library/terraform/netbox-device-role/template.yaml
  67. 1 1
      library/terraform/netbox-device-type/template.yaml
  68. 1 1
      library/terraform/netbox-device/template.yaml
  69. 1 1
      library/terraform/netbox-manufacturer/template.yaml
  70. 1 1
      library/terraform/netbox-vm/template.yaml

+ 1 - 0
CHANGELOG.md

@@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Markdown formatting support for template descriptions and next steps (#1471)
 - Output directory flag `--output`/`-o` for `generate` command (#1534) - Replaces positional directory argument
 - Variable property `autogenerated_length` to specify custom length for auto-generated values (default: 32 characters)
+- Nerd Font icon support with shortcode replacement in template descriptions - Rich visual feedback using standardized icon system
 
 ### Changed
 - Schema is now managed in JSON for better standardization and clarity (#1555)

+ 9 - 2
cli/core/display/display_icons.py

@@ -49,10 +49,17 @@ class IconManager:
 
     # Shortcode Mappings (emoji-style codes to Nerd Font icons)
     # Format: ":code:" -> "\uf000"
+    #
+    # Usage:
+    # 1. In regular text: ":mycode: Some text" - icon replaces shortcode inline
+    # 2. In markdown lists: "- :mycode: List item" - icon replaces bullet with color
+    #
     # To add new shortcodes:
     # 1. Add entry to this dict: ":mycode:": "\uf000"
-    # 2. Use in template descriptions: ":mycode: Some text"
-    # 3. Shortcode will be automatically replaced when markdown is rendered
+    # 2. Use in template descriptions or markdown content
+    # 3. Shortcodes are automatically replaced when markdown is rendered
+    # 4. List items starting with shortcodes get colored icons instead of bullets
+    #
     # Find Nerd Font codes at: https://www.nerdfonts.com/cheat-sheet
     SHORTCODES: ClassVar[dict[str, str]] = {
         ":warning:": "\uf071",  #  (exclamation-triangle)

+ 93 - 6
cli/core/display/display_status.py

@@ -1,12 +1,16 @@
 from __future__ import annotations
 
 import logging
+import re
 from typing import TYPE_CHECKING
 
 from rich import box
+from rich._loop import loop_first
 from rich.console import Console, ConsoleOptions, RenderResult
-from rich.markdown import Heading, Markdown
+from rich.markdown import Heading, ListItem, Markdown
 from rich.panel import Panel
+from rich.segment import Segment
+from rich.text import Text
 
 from .display_icons import IconManager
 from .display_settings import DisplaySettings
@@ -36,16 +40,85 @@ class LeftAlignedHeading(Heading):
             yield text
 
 
+class IconListItem(ListItem):
+    """Custom list item that replaces bullets with colored icons from shortcodes."""
+
+    def render_bullet(self, console: Console, options: ConsoleOptions) -> RenderResult:
+        """Render list item with icon replacement if text starts with :shortcode:."""
+        # Get the text content from elements
+        text_content = ""
+        for element in self.elements:
+            if hasattr(element, "text"):
+                text_content = element.text.plain
+                break
+
+        icon_used = None
+        icon_color = "cyan"  # Default color for icons
+        shortcode_found = None
+
+        # Scan for shortcode at the beginning
+        for shortcode, icon in IconManager.SHORTCODES.items():
+            if text_content.strip().startswith(shortcode):
+                icon_used = icon
+                shortcode_found = shortcode
+
+                # Map shortcodes to colors
+                shortcode_colors = {
+                    ":warning:": "yellow",
+                    ":error:": "red",
+                    ":check:": "green",
+                    ":success:": "green",
+                    ":info:": "blue",
+                    ":docker:": "blue",
+                    ":kubernetes:": "blue",
+                    ":rocket:": "magenta",
+                    ":star:": "yellow",
+                    ":lightning:": "yellow",
+                }
+                icon_color = shortcode_colors.get(shortcode, "cyan")
+                break
+
+        if icon_used and shortcode_found:
+            # Remove the shortcode from the text in all elements
+            for element in self.elements:
+                if hasattr(element, "text"):
+                    # Replace the shortcode in the Text object
+                    plain_text = element.text.plain
+                    new_text = plain_text.replace(shortcode_found, "", 1).lstrip()
+                    # Reconstruct the Text object with the same style
+                    element.text = Text(new_text, style=element.text.style)
+
+            # Render with custom colored icon instead of bullet
+            render_options = options.update(width=options.max_width - 3)
+            lines = console.render_lines(self.elements, render_options, style=self.style)
+            bullet_style = console.get_style(icon_color, default="none")
+
+            bullet = Segment(f" {icon_used} ", bullet_style)
+            padding = Segment(" " * 3)
+            new_line = Segment("\n")
+
+            for first, line in loop_first(lines):
+                yield bullet if first else padding
+                yield from line
+                yield new_line
+        else:
+            # No icon found, use default list item rendering
+            yield from super().render_bullet(console, options)
+
+
 class LeftAlignedMarkdown(Markdown):
-    """Custom Markdown renderer with left-aligned headings."""
+    """Custom Markdown renderer with left-aligned headings and icon list items."""
 
     def __init__(self, markup: str, **kwargs):
-        """Initialize with custom heading element."""
+        """Initialize with custom heading and list item elements."""
         super().__init__(markup, **kwargs)
 
         # Replace heading element to use left alignment
         self.elements["heading_open"] = LeftAlignedHeading
 
+        # Replace list item element to use icon replacement
+        self.elements["list_item_open"] = IconListItem
+
 
 class StatusDisplay:
     """Status messages and error display.
@@ -205,12 +278,26 @@ class StatusDisplay:
         """Render markdown content with left-aligned headings.
 
         Replaces emoji-style shortcodes (e.g., :warning:, :info:) with Nerd Font icons
-        before rendering.
+        before rendering, EXCEPT for shortcodes at the start of list items which are
+        handled by IconListItem to replace the bullet.
 
         Args:
             content: Markdown-formatted text to render (may contain shortcodes)
         """
         if not self.quiet:
-            # Replace shortcodes with Nerd Font icons before rendering
-            processed_content = IconManager.replace_shortcodes(content)
+            # Replace shortcodes with Nerd Font icons, but preserve list item shortcodes
+            # Pattern: "- :shortcode:" at start of line should NOT be replaced
+            lines = content.split("\n")
+            processed_lines = []
+
+            for line in lines:
+                # Check if line is a list item starting with a shortcode
+                if re.match(r"^\s*-\s+:[a-z]+:", line):
+                    # Keep the line as-is, IconListItem will handle it
+                    processed_lines.append(line)
+                else:
+                    # Replace shortcodes normally
+                    processed_lines.append(IconManager.replace_shortcodes(line))
+
+            processed_content = "\n".join(processed_lines)
             self.base._print_markdown(LeftAlignedMarkdown(processed_content))

+ 5 - 3
cli/core/schema/compose/v1.2.json

@@ -390,33 +390,35 @@
         "name": "database_host",
         "description": "Database host",
         "type": "str",
-        "needs": ["database_external=true"],
-        "default": "database",
+        "needs": ["database_external=true;database_type=postgres,mysql"],
         "required": true
       },
       {
         "name": "database_port",
         "description": "Database port",
         "type": "int",
+        "needs": ["database_external=true;database_type=postgres,mysql"],
         "required": true
       },
       {
         "name": "database_name",
         "description": "Database name",
         "type": "str",
+        "needs": ["database_type=postgres,mysql"],
         "required": true
       },
       {
         "name": "database_user",
         "description": "Database user",
         "type": "str",
+        "needs": ["database_type=postgres,mysql"],
         "required": true
       },
       {
         "name": "database_password",
         "description": "Database password",
         "type": "str",
-        "default": "",
+        "needs": ["database_type=postgres,mysql"],
         "sensitive": true,
         "autogenerated": true,
         "required": true

+ 2 - 1
cli/core/template/variable_collection.py

@@ -813,7 +813,8 @@ class VariableCollection:
                 logger.error(error_msg)
 
         if errors:
-            logger.warning(f"Some defaults failed to apply: {'; '.join(errors)}")
+            # Raise exception to halt execution on validation errors
+            raise ValueError(f"Variable validation failed: {'; '.join(errors)}")
 
         return successful
 

+ 25 - 13
library/compose/adguardhome/compose.yaml.j2

@@ -17,6 +17,13 @@ services:
       {{ network_name }}:
       {% endif %}
     {% endif %}
+    {#
+      Port mappings (only in bridge mode or default network):
+      - HTTP/HTTPS (80/443) ports are only exposed when Traefik is disabled
+      - Initial setup port 3000 is exposed during first-time setup
+      - DNS and related ports (53, 853, 5443) are always exposed
+      - In host or macvlan mode, ports are bound directly to host network
+    #}
     {% if network_mode == '' or network_mode == 'bridge' or traefik_enabled %}
     ports:
       {% if not traefik_enabled %}
@@ -32,7 +39,6 @@ services:
       - "{{ ports_tls }}:853/tcp"
       - "{{ ports_dnscrypt }}:5443/tcp"
       - "{{ ports_dnscrypt }}:5443/udp"
-      {% endif %}
     {% endif %}
     volumes:
       {% if volume_mode == 'mount' %}
@@ -61,6 +67,11 @@ services:
       - traefik.http.routers.{{ service_name }}_https.tls=true
       - traefik.http.routers.{{ service_name }}_https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
+      {#
+        Initial setup routing (port 3000):
+        Routes setup wizard through separate Traefik service.
+        Note: Setup wizard is available at http://<host>.<domain>/setup during initial configuration.
+      #}
       {% if initial_setup %}
       - traefik.http.services.{{ service_name }}_setup.loadBalancer.server.port=3000
       - traefik.http.routers.{{ service_name }}_setup.service={{ service_name }}_setup
@@ -71,16 +82,17 @@ services:
       {% endif %}
     {% endif %}
 
+{% if network_mode == 'bridge' or network_mode == 'macvlan' or traefik_enabled %}
 {#
-  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)
+  Network definitions:
+  - 'bridge' mode: creates custom bridge network
+  - 'macvlan' mode: creates macvlan network with static IP assignment
+    (requires manual network creation in Swarm mode)
+  - Swarm overlay: used when swarm_enabled=true with bridge mode
+  - Traefik network: always external (managed separately by Traefik stack)
+  - Default mode (network_mode=''): uses Docker's default bridge (no definition needed)
+  - Host mode: no network definition (container uses host network stack directly)
 #}
-{% if network_mode == 'bridge' or network_mode == 'macvlan' or traefik_enabled %}
 networks:
   {% if network_mode == 'bridge' or network_mode == 'macvlan'%}
   {{ network_name }}:
@@ -110,13 +122,13 @@ networks:
   {% endif %}
 {% endif %}
 
+{% if volume_mode == 'local' %}
 {#
   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)
+  - 'local' mode: Docker-managed local volumes
+  - 'nfs' mode: NFS-backed volumes for shared storage
+  - 'mount' mode: bind mounts (no volume definition needed)
 #}
-{% if volume_mode == 'local' %}
 volumes:
   {{ service_name }}_work:
     driver: local

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

@@ -4,6 +4,9 @@ metadata:
   name: Grafana Alloy
   description: |-
     Grafana Alloy is an open telemetry collector that collects, processes, and exports metrics to various backends.
+    ## Prerequisites
+    - :warning: When alloy runs in a container, you should set the `container_hostname` to a unique value to identify
+    the instance in your monitoring backend. Otherwise it defaults to the container ID.
     ## Resources
     - **Project**: https://grafana.com/oss/alloy/
     - **Documentation**: https://grafana.com/docs/alloy/latest/

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

@@ -18,8 +18,7 @@ metadata:
   icon:
     provider: selfh
     id: bind-9
-  draft: false
-  next_steps: ""
+  draft: true
 schema: "1.2"
 spec:
   dns_security:

+ 5 - 1
library/compose/checkmk/compose.yaml.j2

@@ -1,16 +1,20 @@
 ---
 services:
   {{ service_name }}:
-    image: checkmk/check-mk-raw:2.4.0-p1
+    image: checkmk/check-mk-raw:2.4.0p17
     restart: {{ restart_policy }}
     environment:
+      {% if container_timezone %}
       - TZ={{ container_timezone }}
+      {% endif %}
       - CMK_PASSWORD=${CMK_PASSWORD}
       - CMK_SITE_ID={{ cmk_site_id }}
     tmpfs:
       - /opt/omd/sites/{{ cmk_site_id }}/tmp:uid={{ user_uid }},gid={{ user_gid }}
     volumes:
+      {% if not container_timezone %}
       - /etc/localtime:/etc/localtime:ro
+      {% endif %}
       - {{ service_name }}_data:/omd/sites:rw
     {% if traefik_enabled %}
     networks:

+ 8 - 1
library/compose/checkmk/template.yaml

@@ -14,12 +14,18 @@ metadata:
     * **Project:** https://checkmk.com/
     * **Documentation:** https://docs.checkmk.com/latest/en/
     * **GitHub:** https://github.com/tribe29/checkmk
+  next_steps: |-
+    Log in with your initial admin user:
+    ```bash
+    Username: cmkadmin
+    Password: {{ cmk_password }}
+    ```
   version: 2.4.0-latest
   author: Christian Lempa
   date: '2025-09-28'
   tags:
     - traefik
-schema: "1.2"
+schema: 1.2
 spec:
   general:
     vars:
@@ -48,3 +54,4 @@ spec:
         description: Agent port
         type: int
         default: 5000
+        required: true

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

@@ -17,7 +17,7 @@ metadata:
   date: '2025-09-28'
   tags:
     - traefik
-schema: "1.2"
+schema: 1.2
 spec:
   general:
     vars:

+ 42 - 35
library/compose/gitea/compose.yaml.j2

@@ -3,30 +3,35 @@ services:
     image: docker.io/gitea/gitea:1.25.1
     restart: {{ restart_policy }}
     environment:
-      - TZ={{ container_timezone }}
       - USER_UID={{ user_uid }}
       - USER_GID={{ user_gid }}
-      - GITEA__database__DB_TYPE=postgres
-      - GITEA__database__HOST={{ service_name }}-postgres:5432
+      {% if database_type != 'sqlite' %}
+      - GITEA__database__DB_TYPE={{ database_type }}
+      {% if database_external %}
+      - GITEA__database__HOST={{ database_host }}
+      {% else %}
+      - GITEA__database__HOST={{ service_name }}_db
+      {% endif %}
       - GITEA__database__NAME={{ database_name }}
       - GITEA__database__USER={{ database_user }}
       - GITEA__database__PASSWD=${DATABASE_PASSWORD}
+      {% endif %}
       - 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 %}
+    {% if not database_external and (database_type == "postgres" or database_type == "mysql") or traefik_enabled %}
     networks:
-    {% if not database_external %}
+    {% if not database_external and database_type != 'sqlite' %}
       - {{ service_name }}_backend
     {% endif %}
     {% if traefik_enabled %}
       - {{ traefik_network }}
     {% endif %}
     {% endif %}
-    ports:
+    ports:  
       {% if not traefik_enabled %}
       - "{{ ports_http }}:3000"
       {% endif %}
@@ -39,29 +44,25 @@ services:
     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=web
+      - 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=websecure
-      - 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 }}
       {% endif %}
     {% endif %}
-    {% if not database_external %}
+    {% if not database_external and database_type != 'sqlite' %}
     depends_on:
-      - {{ service_name }}-postgres
+      - {{ service_name }}_db
     {% endif %}
 
-  {#
-    PostgreSQL: Main database for Gitea (only if not using external database)
-    Stores repositories metadata, users, issues, and pull requests
-  #}
-  {% if not database_external %}
-  {{ service_name }}-postgres:
+  {% if not database_external and database_type == "postgres" %}
+  {{ service_name }}_db:
     image: docker.io/library/postgres:17.6
     restart: {{ restart_policy }}
     environment:
@@ -78,27 +79,33 @@ services:
       timeout: 10s
       retries: 5
     volumes:
-      - {{ service_name }}_postgres:/var/lib/postgresql/data
+      - {{ service_name }}_db:/var/lib/postgresql/data
+  {% elif not database_external and database_type == "mysql" %}
+  {{ service_name }}_db:
+    image: docker.io/library/mysql:8.1
+    restart: {{ restart_policy }}
+    environment:
+      - MYSQL_USER={{ database_user }}
+      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
+      - MYSQL_DATABASE={{ database_name }}
+      - MYSQL_ROOT_PASSWORD=${DATABASE_PASSWORD}
+    networks:
+      - {{ service_name }}_backend
+    volumes:
+      - {{ service_name }}_db:/var/lib/mysql
   {% endif %}
 
-{#
-  Volume definitions:
-  - {{ service_name }}_data: Persistent storage for repositories and configuration
-  - {{ service_name }}_postgres: PostgreSQL database storage (only if not using external database)
-#}
 volumes:
   {{ service_name }}_data:
     driver: local
   {% if not database_external %}
-  {{ service_name }}_postgres:
+  {% if database_type == "postgres" or database_type == "mysql" %}
+  {{ service_name }}_db:
     driver: local
   {% endif %}
+  {% endif %}
 
-{#
-  Network definitions (only when Traefik is enabled):
-  - Traefik network: always external (managed by Traefik)
-#}
-{% if not database_external or traefik_enabled %}
+{% if not database_external and (database_type == "postgres" or database_type == "mysql") or traefik_enabled %}
 networks:
   {% if not database_external %}
   {{ service_name }}_backend:

+ 11 - 9
library/compose/gitea/template.yaml

@@ -7,9 +7,13 @@ metadata:
     written in Go. It's similar to GitHub, Bitbucket, and GitLab, providing Git repository
     hosting, code review, team collaboration, and more.
     ## Prerequisites
-    * **Project:** https://gitea.io/
-    * **Documentation:** https://docs.gitea.io/
-    * **GitHub:** https://github.com/go-gitea/gitea
+    - :info: Gitea supports multiple database backends. You can choose between SQLite (default),
+    PostgreSQL, or MySQL. SQLite is suitable for small deployments, while PostgreSQL and MySQL
+    are recommended for larger installations.
+    ## References
+    - **Project:** https://gitea.io/
+    - **Documentation:** https://docs.gitea.io/
+    - **GitHub:** https://github.com/go-gitea/gitea
   icon:
     provider: selfh
     id: gitea
@@ -18,12 +22,12 @@ metadata:
   date: '2025-11-05'
   tags:
     - traefik
-schema: "1.2"
+schema: 1.2
 spec:
   general:
     vars:
       service_name:
-        default: "gitea"
+        default: gitea
       gitea_url:
         description: "Public URL"
         type: str
@@ -31,12 +35,10 @@ spec:
         default: "https://git.example.com"
   database:
     vars:
-      database_type:
-        default: "postgres"
       database_name:
-        default: "gitea"
+        default: gitea
       database_user:
-        default: "gitea"
+        default: gitea
   ports:
     vars:
       ports_http:

+ 1 - 1
library/compose/gitlab-runner/template.yaml

@@ -15,7 +15,7 @@ metadata:
   icon:
     provider: selfh
     id: gitlab
-  draft: false
+  draft: true
   next_steps: ""
 schema: "1.2"
 spec: {}

+ 2 - 2
library/compose/gitlab/.env.j2

@@ -1,3 +1,3 @@
-# GitLab Environment Variables
-# Note: Initial root password only applies on FIRST initialization
+{% if not swarm_enabled %}
 GITLAB_ROOT_PASSWORD={{ root_password }}
+{% endif %}

+ 0 - 0
library/compose/gitlab/.env.password.j2


+ 0 - 0
library/compose/gitlab/.env.registry.j2


+ 65 - 131
library/compose/gitlab/compose.yaml.j2

@@ -1,121 +1,79 @@
 services:
   {{ service_name }}:
     image: docker.io/gitlab/gitlab-ce:18.5.1-ce.0
-    {#
-      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 GitLab identification
-    #}
-    hostname: {{ container_hostname }}
-    {#
-      Shared memory size for GitLab (required for proper operation)
-    #}
     shm_size: '256m'
-    {#
-      When traefik is enabled, add traefik network for reverse proxy access
-    #}
+    environment:
+      - 
     {% if traefik_enabled %}
     networks:
       {{ traefik_network }}:
     {% endif %}
-    {#
-      Environment file containing GitLab configuration variables
-    #}
-    env_file:
-      - ./.env
-    {#
-      When swarm_enabled is set, use Docker configs for GitLab configuration
-    #}
-    {% if swarm_enabled %}
-    configs:
-      - source: gitlab_config
-        target: /etc/gitlab/gitlab.rb
-    {% endif %}
-    {#
-      Port mappings:
-      - HTTP: Web interface (only if Traefik is disabled)
-      - SSH: Git SSH access (always exposed, cannot be proxied)
-      - Registry: Container registry (if enabled)
-      Note: Swarm mode uses 'host' mode for port publishing to avoid port conflicts
-    #}
     ports:
       {% if not traefik_enabled %}
-      {% if swarm_enabled %}
-      - target: 80
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      {% else %}
       - "{{ ports_http }}:80"
       {% endif %}
-      {% endif %}
       - "{{ ports_ssh }}:22"
       {% if registry_enabled %}
       - "{{ ports_registry }}:5000"
       {% endif %}
-    {#
-      Volume configuration:
-      - config: GitLab configuration files
-      - logs: GitLab log files
-      - data: GitLab data (repositories, uploads, etc.)
-    #}
     volumes:
+      {% if volume_mode == 'mount' %}
       {% if not swarm_enabled %}
-      - ./config/gitlab.rb:/etc/gitlab/gitlab.rb:ro
+      - {{ volume_mount_path }}/config/gitlab.rb:/etc/gitlab/gitlab.rb:ro
       {% endif %}
-      {% if volume_mode == 'mount' %}
       - {{ volume_mount_path }}/config:/etc/gitlab
       - {{ volume_mount_path }}/logs:/var/log/gitlab
       - {{ volume_mount_path }}/data:/var/opt/gitlab
       {% else %}
-      - gitlab-config:/etc/gitlab
-      - gitlab-logs:/var/log/gitlab
-      - gitlab-data:/var/opt/gitlab
+      {% if not swarm_enabled %}
+      - ./config/gitlab.rb:/etc/gitlab/gitlab.rb:ro
+      {% endif %}
+      - {{ service_name }}_config:/etc/gitlab
+      - {{ service_name }}_logs:/var/log/gitlab
+      - {{ service_name }}_data:/var/opt/gitlab
       {% endif %}
-    {#
-      When traefik_enabled is set, and not running in swarm mode, add traefik labels
-      (optionally enable TLS if traefik_tls_enabled is set)
-      Also configure registry routing if registry is enabled
-    #}
     {% 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 }}
+      - 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={{ 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 }}
       {% endif %}
       {% if registry_enabled %}
-      - traefik.http.services.{{ service_name }}-registry.loadBalancer.server.port=5000
-      - traefik.http.routers.{{ service_name }}-registry-http.service={{ service_name }}-registry
-      - traefik.http.routers.{{ service_name }}-registry-http.rule=Host(`{{ traefik_registry_host }}`)
-      - traefik.http.routers.{{ service_name }}-registry-http.entrypoints={{ traefik_entrypoint }}
+      - traefik.http.services.{{ service_name }}_registry.loadBalancer.server.port=5000
+      - traefik.http.routers.{{ service_name }}_registry-http.service={{ service_name }}_registry
+      - traefik.http.routers.{{ service_name }}_registry-http.rule=Host(`{{ traefik_registry_host }}`)
+      - traefik.http.routers.{{ service_name }}_registry-http.entrypoints=web
       {% if traefik_tls_enabled %}
-      - traefik.http.routers.{{ service_name }}-registry-https.service={{ service_name }}-registry
-      - traefik.http.routers.{{ service_name }}-registry-https.rule=Host(`{{ traefik_registry_host }}`)
-      - traefik.http.routers.{{ service_name }}-registry-https.entrypoints={{ traefik_tls_entrypoint }}
-      - traefik.http.routers.{{ service_name }}-registry-https.tls=true
-      - traefik.http.routers.{{ service_name }}-registry-https.tls.certresolver={{ traefik_tls_certresolver }}
+      - traefik.http.routers.{{ service_name }}_registry-https.service={{ service_name }}_registry
+      - traefik.http.routers.{{ service_name }}_registry-https.rule=Host(`{{ traefik_registry_host }}`)
+      - traefik.http.routers.{{ service_name }}_registry-https.entrypoints=websecure
+      - traefik.http.routers.{{ service_name }}_registry-https.tls=true
+      - traefik.http.routers.{{ service_name }}_registry-https.tls.certresolver={{ traefik_tls_certresolver }}
       {% endif %}
       {% endif %}
     {% endif %}
     {#
-      When swarm_enabled is set, use Docker secrets for sensitive data
+      Docker Swarm configuration (only when swarm_enabled is set):
+      - Configs: GitLab configuration file
+      - Secrets: Root password and registry secret (if registry enabled)
+      - Deploy: Replication, placement, restart policy, and Traefik labels
     #}
     {% if swarm_enabled %}
+    configs:
+      - source: gitlab_config
+        target: /etc/gitlab/gitlab.rb
     secrets:
       - source: {{ service_name }}_root_password
         target: /run/secrets/gitlab_root_password
@@ -125,17 +83,9 @@ services:
         target: /run/secrets/gitlab_registry_secret
         mode: 0400
       {% endif %}
-    {#
-      Deploy configuration for Swarm mode:
-      - 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)
-    #}
     deploy:
-      mode: {{ swarm_placement_mode }}
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      {% endif %}
+      mode: replicated
+      replicas: 1
       {% if swarm_placement_host %}
       placement:
         constraints:
@@ -143,48 +93,32 @@ services:
       {% endif %}
       restart_policy:
         condition: on-failure
-      {% 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)
-        Also configure registry routing if registry is enabled
-      #}
-      {% if swarm_enabled and traefik_enabled %}
+      {% 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 }}
+        - 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={{ 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 }}
         {% endif %}
         {% if registry_enabled %}
-        - traefik.http.services.{{ service_name }}-registry.loadBalancer.server.port=5000
-        - traefik.http.routers.{{ service_name }}-registry-http.service={{ service_name }}-registry
-        - traefik.http.routers.{{ service_name }}-registry-http.rule=Host(`{{ traefik_registry_host }}`)
-        - traefik.http.routers.{{ service_name }}-registry-http.entrypoints={{ traefik_entrypoint }}
+        - traefik.http.services.{{ service_name }}_registry.loadBalancer.server.port=5000
+        - traefik.http.routers.{{ service_name }}_registry-http.service={{ service_name }}_registry
+        - traefik.http.routers.{{ service_name }}_registry-http.rule=Host(`{{ traefik_registry_host }}`)
+        - traefik.http.routers.{{ service_name }}_registry-http.entrypoints=web
         {% if traefik_tls_enabled %}
-        - traefik.http.routers.{{ service_name }}-registry-https.service={{ service_name }}-registry
-        - traefik.http.routers.{{ service_name }}-registry-https.rule=Host(`{{ traefik_registry_host }}`)
-        - traefik.http.routers.{{ service_name }}-registry-https.entrypoints={{ traefik_tls_entrypoint }}
-        - traefik.http.routers.{{ service_name }}-registry-https.tls=true
-        - traefik.http.routers.{{ service_name }}-registry-https.tls.certresolver={{ traefik_tls_certresolver }}
+        - traefik.http.routers.{{ service_name }}_registry-https.service={{ service_name }}_registry
+        - traefik.http.routers.{{ service_name }}_registry-https.rule=Host(`{{ traefik_registry_host }}`)
+        - traefik.http.routers.{{ service_name }}_registry-https.entrypoints=websecure
+        - traefik.http.routers.{{ service_name }}_registry-https.tls=true
+        - traefik.http.routers.{{ service_name }}_registry-https.tls.certresolver={{ traefik_tls_certresolver }}
         {% endif %}
         {% endif %}
       {% endif %}
@@ -198,31 +132,31 @@ services:
 #}
 {% if volume_mode == 'local' %}
 volumes:
-  gitlab-config:
+  {{ service_name }}_config:
     driver: local
-  gitlab-logs:
+  {{ service_name }}_logs:
     driver: local
-  gitlab-data:
+  {{ service_name }}_data:
     driver: local
 {% elif volume_mode == 'nfs' %}
 volumes:
-  gitlab-config:
+  {{ service_name }}_config:
     driver: local
     driver_opts:
       type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
       device: ":{{ volume_nfs_path }}/config"
-  gitlab-logs:
+  {{ service_name }}_logs:
     driver: local
     driver_opts:
       type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
       device: ":{{ volume_nfs_path }}/logs"
-  gitlab-data:
+  {{ service_name }}_data:
     driver: local
     driver_opts:
       type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ volume_nfs_options }}
       device: ":{{ volume_nfs_path }}/data"
 {% endif %}
 

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

@@ -6,7 +6,8 @@ metadata:
   description: |
     A **complete DevOps platform** that provides Git repository management, CI/CD pipelines,
     issue tracking, and container registry in a single application.
-    ## Important Configuration Notes
+    ## Prerequisites
+    - ...
     **Performance Presets**:
     - `homelab`: Optimized for low-resource environments (limited workers, reduced PostgreSQL buffers)
     - `default`: Standard server configuration for production use
@@ -29,7 +30,6 @@ metadata:
   tags:
     - traefik
     - swarm
-    - volume_modes
   next_steps: |
     ## Post-Installation Steps
     1. **Start GitLab**:
@@ -60,15 +60,12 @@ metadata:
     ## Additional Resources
     - Documentation: https://docs.gitlab.com/
     - GitLab Runner: https://docs.gitlab.com/runner/
+  draft: true
 spec:
   general:
     vars:
       service_name:
         default: "gitlab"
-      container_name:
-        default: "gitlab"
-      container_hostname:
-        default: "gitlab"
       external_url:
         type: str
         description: External URL for GitLab
@@ -99,17 +96,7 @@ spec:
         default: true
   ports:
     vars:
-      ports_http:
-        type: int
-        description: HTTP port
-        default: 80
-      ports_https:
-        type: int
-        description: HTTPS port
-        default: 443
       ports_ssh:
-        type: int
-        description: SSH port
         default: 2424
       ports_registry:
         type: int

+ 41 - 5
library/compose/grafana/compose.yaml.j2

@@ -2,14 +2,14 @@ services:
   {{ service_name }}:
     image: docker.io/grafana/grafana-oss:12.1.1
     restart: {{ restart_policy }}
-    {% if database_type == 'postgres' or authentik_enabled%}
+    {% if database_type != 'sqlite' or authentik_enabled %}
     environment:
       {% if database_type == 'postgres' %}
       - GF_DATABASE_TYPE=postgres
       {% if database_external %}
       - GF_DATABASE_HOST={{ database_host }}
       {% else %}
-      - GF_DATABASE_HOST={{ service_name }}_postgres:5432
+      - GF_DATABASE_HOST={{ service_name }}_db
       {% endif %}
       - GF_DATABASE_NAME={{ database_name }}
       - GF_DATABASE_USER={{ database_user }}
@@ -34,9 +34,14 @@ services:
       - GF_AUTH_GENERIC_OAUTH_SKIP_ORG_ROLE_SYNC=true
       {% endif %}
     {% endif %}
-    {% if traefik_enabled %}
+    {% if not database_external and (database_type == "postgres") or traefik_enabled %}
     networks:
-      {{ traefik_network }}:
+    {% if not database_external and database_type != 'sqlite' %}
+      - {{ service_name }}_backend
+    {% endif %}
+    {% if traefik_enabled %}
+      - {{ traefik_network }}
+    {% endif %}
     {% endif %}
     {% if not traefik_enabled %}
     ports:
@@ -61,12 +66,43 @@ services:
       {% endif %}
     {% endif %}
 
-{% if traefik_enabled %}
+  {% if not database_external and database_type == "postgres" %}
+  {{ service_name }}_db:
+    image: docker.io/library/postgres:17.6
+    restart: {{ restart_policy }}
+    environment:
+      - POSTGRES_USER={{ database_user }}
+      - POSTGRES_PASSWORD=${GRAFANA_DB_PASSWORD}
+      - POSTGRES_DB={{ database_name }}
+    networks:
+      - {{ service_name }}_backend
+    healthcheck:
+      test: ["CMD-SHELL", "pg_isready -U {{ database_user }}"]
+      start_period: 30s
+      interval: 10s
+      timeout: 10s
+      retries: 5
+    volumes:
+      - {{ service_name }}_db:/var/lib/postgresql/data
+  {% endif %}
+
+{% if not database_external and (database_type == "postgres") or traefik_enabled %}
 networks:
+  {% if not database_external %}
+  {{ service_name }}_backend:
+    driver: bridge
+  {% endif %}
+  {% if traefik_enabled %}
   {{ traefik_network }}:
     external: true
+  {% endif %}
 {% endif %}
 
+
 volumes:
   {{ service_name }}_data:
     driver: local
+  {% if not database_external and database_type == 'postgres' %}
+  {{ service_name }}_db:
+    driver: local
+  {% endif %}

+ 16 - 22
library/compose/grafana/template.yaml

@@ -6,9 +6,9 @@ metadata:
     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.
     ## Prerequisites
-    * **Project:** https://grafana.com/
-    * **Documentation:** https://grafana.com/docs/grafana/latest/
-    * **GitHub:** https://github.com/grafana/grafana
+    - **Project:** https://grafana.com/
+    - **Documentation:** https://grafana.com/docs/grafana/latest/
+    - **GitHub:** https://github.com/grafana/grafana
   icon:
     provider: selfh
     id: grafana
@@ -29,34 +29,28 @@ spec:
   general:
     vars:
       service_name:
-        default: "grafana"
+        default: grafana
   ports:
     vars:
       ports_http:
         default: 3000
-  database:
-    vars:
-      database_type:
-        options: ["sqlite", "postgres"]
-        default: "sqlite"
-      database_host:
-        default: "postgres:5432"
-        needs: "database_type=postgres"
-      database_name:
-        default: "grafana"
-        needs: "database_type=postgres"
-      database_user:
-        default: "grafana"
-        needs: "database_type=postgres"
-      database_password:
-        needs: "database_type=postgres"
   authentik:
     vars:
       authentik_url:
-        default: "https://auth.home.arpa"
+        default: https://auth.home.arpa
       authentik_slug:
-        default: "grafana"
+        default: grafana
   traefik:
     vars:
       traefik_host:
         default: grafana
+  database:
+    vars:
+      database_type:
+        options:
+          - sqlite
+          - postgres
+      database_name:
+        default: grafana
+      database_user:
+        default: grafana

+ 10 - 26
library/compose/homepage/template.yaml

@@ -1,52 +1,36 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
-  icon:
-    provider: selfh
-    id: homepage
   name: Homepage
-  description: |
+  description: |-
     Homepage is a modern, fully static, fast, secure fully customizable application dashboard with integrations
     for over 100 services and translations into multiple languages.
-    
+
     Project: https://gethomepage.dev/
     Documentation: https://gethomepage.dev/latest/
     GitHub: https://github.com/gethomepage/homepage
   version: v1.6.1
   author: Christian Lempa
-  date: '2025-11-05'
+  date: "2025-11-05"
   tags:
     - traefik
     - swarm
     - volume_modes
+  icon:
+    provider: simpleicons
+    id: homepage
   draft: true
+  next_steps: ""
+schema: "1.2"
 spec:
   general:
     vars:
-      service_name:
+      container_hostname:
         default: homepage
       container_name:
         default: homepage
-      container_hostname:
+      service_name:
         default: homepage
-  homepage:
-    title: Homepage Configuration
-    required: true
-    vars:
-      homepage_allowed_hosts:
-        type: str
-        description: Allowed hosts (comma-separated, required for security)
-        default: "*"
-        extra: "Set to specific domains in production (e.g., homepage.home.arpa)"
-      homepage_puid:
-        type: int
-        description: User ID (optional, leave 0 to use container default)
-        default: 0
-      homepage_pgid:
-        type: int
-        description: Group ID (optional, leave 0 to use container default)
-        default: 0
   ports:
     vars:
       ports_http:

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

@@ -23,6 +23,7 @@ metadata:
   date: '2025-09-28'
   tags:
     - traefik
+  draft: true
 schema: "1.2"
 spec:
   ports:

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

@@ -1,6 +1,5 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   icon:
     provider: selfh
@@ -23,6 +22,8 @@ metadata:
   author: Christian Lempa
   date: '2025-09-28'
   tags: []
+  draft: true
+schema: 1.2
 spec:
   general:
     vars:

+ 2 - 2
library/compose/n8n/template.yaml

@@ -29,8 +29,8 @@ metadata:
   tags:
     - traefik
     - database
-  next_steps:
-schema: "1.2"
+  draft: true
+schema: 1.2
 spec:
   general:
     vars:

+ 7 - 5
library/compose/netbox/template.yaml

@@ -3,11 +3,13 @@ kind: compose
 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.
+    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.
     ## References
-    * **Project:** https://netbox.dev/
-    * **Documentation:** https://docs.netbox.dev/
-    * **GitHub:** https://github.com/netbox-community/netbox
+    - **Project:** https://netbox.dev/
+    - **Documentation:** https://docs.netbox.dev/
+    - **GitHub:** https://github.com/netbox-community/netbox
   version: 4.2.3
   author: Christian Lempa
   date: "2025-11-13"
@@ -25,7 +27,7 @@ metadata:
     Username: admin
     Password: admin
     ```
-schema: "1.2"
+schema: 1.2
 spec:
   database:
     vars:

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

@@ -21,6 +21,7 @@ metadata:
   tags:
     - traefik
     - swarm
+  draft: true
 spec:
   general:
     vars:

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

@@ -4,7 +4,7 @@ schema: "1.2"
 metadata:
   icon:
     provider: selfh
-    id: openwebui
+    id: open-webui
   name: Openwebui
   description: >
     OpenWebUI is an open-source web-based user interface for managing and interacting with AI models.
@@ -24,6 +24,7 @@ metadata:
   tags:
     - traefik
     - authentik
+  draft: true
 spec:
   general:
     vars:

+ 0 - 137
library/compose/pangolin/compose.yaml.j2.final

@@ -1,137 +0,0 @@
----
-services:
-  {{ service_name }}:
-    image: docker.io/fosrl/pangolin:latest
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
-    {#
-      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 network_mode == 'bridge' and not traefik_enabled %}
-    ports:
-      {% if swarm_enabled %}
-      - target: 8080
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      {% else %}
-      - "{{ ports_http }}:8080/tcp"
-      {% endif %}
-    {% endif %}
-    {% if environment_enabled or postgres_enabled %}
-    environment:
-      {% if postgres_enabled %}
-      POSTGRES_CONNECTION_STRING: "{{ postgres_connection_string }}"
-      {% endif %}
-      {% if environment_enabled %}
-      {% if environment_log_level %}
-      LOG_LEVEL: "{{ environment_log_level }}"
-      {% endif %}
-      {% if environment_crowdsec_enabled %}
-      CROWDSEC_ENABLED: "true"
-      {% endif %}
-      {% endif %}
-    {% endif %}
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/data:/app/data:rw
-      - {{ volume_mount_path }}/config:/app/config:rw
-      {% elif volume_mode in ['local', 'nfs'] %}
-      - {{ service_name }}-data:/app/data
-      - {{ service_name }}-config:/app/config
-      {% endif %}
-    {% if swarm_enabled or resources_enabled %}
-    deploy:
-      {% if swarm_enabled %}
-      mode: replicated
-      replicas: 1
-      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=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 %}
-    {% 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 %}
-
-{% if volume_mode == 'local' %}
-volumes:
-  {{ service_name }}-data:
-    driver: local
-  {{ service_name }}-config:
-    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"
-  {{ service_name }}-config:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/config"
-{% endif %}
-
-    {% endif %}
-  {% if traefik_enabled %}
-  {{ traefik_network }}:
-    external: true
-  {% endif %}
-{% endif %}

+ 0 - 113
library/compose/pangolin/template.yaml.backup

@@ -1,113 +0,0 @@
----
-kind: compose
-schema: "1.2"
-metadata:
-  name: Pangolin
-  description: |
-    Self-hosted reverse proxy server that securely exposes private resources on distributed networks through
-    encrypted WireGuard tunnels. Pangolin enables access from anywhere without opening ports, using a custom
-    user-space WireGuard client (Newt) for secure connectivity. Features include automatic tunnel management,
-    integrated CrowdSec security, and support for both PostgreSQL and SQLite databases.
-    ## References
-    * **Project:** https://github.com/fosrl/pangolin
-    * **Documentation:** https://github.com/fosrl/pangolin/blob/main/README.md
-    * **Docker Hub:** https://hub.docker.com/r/fosrl/pangolin
-  version: latest
-  author: Christian Lempa
-  date: '2025-11-13'
-  tags:
-    - traefik
-    - swarm
-    - proxy
-    - wireguard
-  next_steps: |
-    ### 1. Configure Database
-    {% if postgres_enabled -%}
-    Make sure PostgreSQL is running and accessible at:
-    * Connection string: {{ postgres_connection_string }}
-    {% else -%}
-    Pangolin will use SQLite database stored in the data volume.
-    {% endif -%}
-    ### 2. Deploy the Service
-    {% if swarm_enabled -%}
-    Deploy to Docker Swarm:
-    ```bash
-    docker stack deploy -c compose.yaml pangolin
-    ```
-    {% else -%}
-    Start Pangolin using Docker Compose:
-    ```bash
-    docker compose up -d
-    ```
-    {% endif -%}
-    ### 3. Access the Web Interface
-    {% if traefik_enabled -%}
-    * Navigate to: **https://{{ traefik_host }}.{{ traefik_domain }}**
-    {% else -%}
-    * Navigate to: **http://localhost:{{ ports_http }}**
-    {% endif -%}
-    ### 4. Configure WireGuard Clients
-    * Use the Pangolin web interface to create and manage WireGuard tunnels
-    * Deploy Newt client on remote machines to establish secure connections
-spec:
-  general:
-    vars:
-      service_name:
-        default: "pangolin"
-      container_name:
-        default: "pangolin"
-      container_hostname:
-        default: "pangolin"
-  traefik:
-    vars:
-      traefik_host:
-        default: "pangolin"
-  network:
-    vars:
-      network_name:
-        default: "pangolin_network"
-  ports:
-    vars:
-      ports_http:
-        description: "External HTTP port (web interface)"
-        type: int
-        default: 8080
-        needs: ["traefik_enabled=false", "network_mode=bridge"]
-  volume:
-    vars:
-      volume_mount_path:
-        default: "/mnt/storage/pangolin"
-  postgres:
-    title: "PostgreSQL Configuration"
-    toggle: postgres_enabled
-    needs: null
-    vars:
-      postgres_enabled:
-        type: bool
-        default: false
-        description: "Use PostgreSQL database (SQLite is default)"
-      postgres_connection_string:
-        type: str
-        default: "postgresql://postgres:postgres@localhost:5432"
-        description: "PostgreSQL connection string"
-        needs: "postgres_enabled=true"
-  environment:
-    title: "Environment Variables"
-    toggle: environment_enabled
-    needs: null
-    vars:
-      environment_enabled:
-        type: bool
-        default: false
-        description: "Configure additional environment variables"
-      environment_crowdsec_enabled:
-        type: bool
-        default: false
-        description: "Enable CrowdSec integration"
-        needs: "environment_enabled=true"
-      environment_log_level:
-        type: enum
-        default: "info"
-        options: ["debug", "info", "warn", "error"]
-        description: "Log level"
-        needs: "environment_enabled=true"

+ 25 - 20
library/compose/passbolt/compose.yaml.j2

@@ -1,25 +1,12 @@
-volumes:
-  passbolt-db:
-  passbolt-data-gpg:
-  passbolt-data-jwt:
+---
 services:
-  {{ service_name }}-db:
-    container_name: {{ container_name }}-db
-    image: docker.io/library/mariadb:11.3
-    environment:
-      - MYSQL_RANDOM_ROOT_PASSWORD=true
-      - MYSQL_DATABASE=$PASSBOLT_DB_NAME
-      - MYSQL_USER=$PASSBOLT_DB_USER
-      - MYSQL_PASSWORD=$PASSBOLT_DB_PASS
-    volumes:
-      - passbolt-db:/var/lib/mysql
-    restart: unless-stopped
   {{ service_name }}:
-    container_name: {{ container_name }}
     image: docker.io/passbolt/passbolt:5.6.1-1-ce
-    depends_on:
-      - {{ service_name }}-db
+    restart: unless-stopped
     environment:
+      {% if container_timezone%}
+      - APP_DEFAULT_TIMEZONE={{ container_timezone }}
+      {% endif %}
       - APP_FULL_BASE_URL=https://passbolt.domain.tld
       - DATASOURCES_DEFAULT_HOST={{ service_name }}-db
       - DATASOURCES_DEFAULT_USERNAME=$PASSBOLT_DB_USER
@@ -32,7 +19,25 @@ services:
       - EMAIL_TRANSPORT_DEFAULT_TLS=true
       - EMAIL_DEFAULT_FROM=no-reply@domain.tld
     volumes:
-      - passbolt-data-gpg:/etc/passbolt/gpg
-      - passbolt-data-jwt:/etc/passbolt/jwt
+      - {{ service_name }}-gpg:/etc/passbolt/gpg
+      - {{ service_name }}-jwt:/etc/passbolt/jwt
     command: ["/usr/bin/wait-for.sh", "-t", "0", "passbolt-db:3306", "--", "/docker-entrypoint.sh"]
+    depends_on:
+      - {{ service_name }}-db
+
+  {{ service_name }}-db:
+    image: docker.io/library/mariadb:11.3.0
     restart: unless-stopped
+    environment:
+      - MYSQL_RANDOM_ROOT_PASSWORD=true
+      - MYSQL_DATABASE=$PASSBOLT_DB_NAME
+      - MYSQL_USER=$PASSBOLT_DB_USER
+      - MYSQL_PASSWORD=$PASSBOLT_DB_PASS
+    volumes:
+      - passbolt-db:/var/lib/mysql
+
+
+volumes:
+  passbolt-db:
+  {{ service_name }}-gpg:
+  {{ service_name }}-jwt:

+ 12 - 116
library/compose/passbolt/template.yaml

@@ -1,141 +1,37 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   icon:
     provider: selfh
     id: passbolt
   name: Passbolt
   description: |-
-    Passbolt is an open-source password manager designed for teams and businesses. It provides a secure way to store, share, and manage passwords and sensitive
-    information collaboratively.
-
-    ## Prerequisites
-    * **Project:** https://www.passbolt.com/
-    * **Documentation:** https://help.passbolt.com/
-    * **GitHub:** https://github.com/passbolt/passbolt
-
+    Passbolt is an open-source password manager designed for teams and businesses. It provides a secure way to store,
+    share, and manage passwords and sensitive information collaboratively.
+    ## References
+    - **Project:** https://www.passbolt.com/
+    - **Documentation:** https://help.passbolt.com/
+    - **GitHub:** https://github.com/passbolt/passbolt
   version: 11.3
   author: Christian Lempa
   date: '2025-09-28'
   tags:
     - traefik
     - database
+  draft: true
+schema: 1.2
 spec:
   general:
     vars:
       service_name:
-        default: "passbolt"
-      container_name:
-        default: "passbolt"
-      container_timezone:
-        default: "UTC"
-  ports:
-    vars:
-      ports_http:
-        description: HTTP port for Passbolt
-        type: int
-        default: 80
-      ports_https:
-        description: HTTPS port for Passbolt
-        type: int
-        default: 443
+        default: passbolt
   traefik:
     vars:
-      traefik_enabled:
-        type: bool
-        default: false
-      traefik_network:
-        default: "traefik"
       traefik_host:
-        default: "passbolt"
-      traefik_domain:
-        default: "home.arpa"
-      traefik_entrypoint:
-        default: "web"
-      traefik_tls_entrypoint:
-        default: "websecure"
-      traefik_tls_enabled:
-        type: bool
-        default: true
-      traefik_tls_certresolver:
-        default: "cloudflare"
+        default: passbolt
   database:
     vars:
-      database_type:
-        options: ["sqlite", "postgres", "mysql"]
-        default: "mysql"
-      database_external:
-        description: Use an external database server?
-        extra: skips creation of internal database container
-        type: bool
-        needs: "database_type=mysql,postgres"
-        default: false
-      database_host:
-        description: Database host
-        type: str
-        needs: "database_external=true"
-        default: "passbolt-db"
-        required: true
-      database_port:
-        description: Database port
-        type: int
-        required: true
       database_name:
-        description: Database name
-        type: str
-        required: true
+        default: passbolt
       database_user:
-        description: Database user
-        type: str
-        required: true
-      database_password:
-        description: Database password
-        type: str
-        sensitive: true
-        required: true
-  volume:
-    vars:
-      volume_mode:
-        type: enum
-        options: ["local", "mount", "nfs"]
-        default: "local"
-      volume_mount_path:
-        description: Host path for bind mounts
-        type: str
-        default: "/mnt/storage"
-        needs: "volume_mode=mount"
-        required: true
-      volume_nfs_server:
-        description: NFS server address
-        type: str
-        default: "192.168.1.1"
-        needs: "volume_mode=nfs"
-        required: true
-      volume_nfs_path:
-        description: NFS export path
-        type: str
-        default: "/export"
-        needs: "volume_mode=nfs"
-        required: true
-      volume_nfs_options:
-        description: NFS mount options (comma-separated)
-        type: str
-        default: "rw,nolock,soft"
-        needs: "volume_mode=nfs"
-        required: true
-  resources:
-    vars:
-      resources_enabled:
-        type: bool
-        default: false
-      resources_cpu_limit:
-        description: Maximum CPU cores (e.g., 0.5, 1.0, 2.0)
-        type: str
-        default: "1.0"
-        required: true
-      resources_memory_limit:
-        description: Maximum memory (e.g., 512M, 1G, 2G)
-        type: str
-        default: "1G"
-        required: true
+        default: passbolt

+ 0 - 38
library/compose/pihole/common/networks.yaml.j2

@@ -1,38 +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 - 3
library/compose/pihole/common/secrets.yaml.j2

@@ -1,3 +0,0 @@
-secrets:
-  {{ service_name }}_webpassword:
-    file: ./.env.secret.webpassword

+ 0 - 21
library/compose/pihole/common/volumes.yaml.j2

@@ -1,21 +0,0 @@
-{% if volume_mode == 'local' %}
-volumes:
-  {{ service_name }}-dnsmasq:
-    driver: local
-  {{ service_name }}-pihole:
-    driver: local
-{% elif volume_mode == 'nfs' %}
-volumes:
-  {{ service_name }}-dnsmasq:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/dnsmasq"
-  {{ service_name }}-pihole:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/pihole"
-{% endif %}

+ 262 - 8
library/compose/pihole/compose.yaml.j2

@@ -1,12 +1,266 @@
+---
+services:
+  {{ service_name }}:
+    image: docker.io/pihole/pihole:2025.11.0
+    {#
+      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 }}
+    {% if container_name %}
+    container_name: {{ container_name }}
+    {% endif %}
+    {% endif %}
+    {#
+      Set container hostname (Pi-hole requires this for proper operation)
+    #}
+    {% if container_hostname %}
+    hostname: {{ container_hostname }}
+    {% endif %}
+    {#
+      Environment variables for Pi-hole configuration
+      - TZ: Timezone for proper log rotation
+      - PIHOLE_UID/GID: User/group for file permissions
+      - WEBPASSWORD: Admin password (from env file in compose mode, from secret in swarm mode)
+      - FTLCONF_dns_listeningMode: In bridge mode, listen on all interfaces
+    #}
+    environment:
+      - TZ={{ container_timezone }}
+      - PIHOLE_UID={{ user_uid }}
+      - PIHOLE_GID={{ user_gid }}
+      {% if swarm_enabled %}
+      - WEBPASSWORD_FILE={{ service_name }}_webpassword
+      {% else %}
+      - FTLCONF_webserver_api_password=${WEBPASSWORD}
+      {% endif %}
+      {% if network_mode == 'bridge' %}
+      - FTLCONF_dns_listeningMode=all
+      {% endif %}
+    {#
+      Network configuration based on network_mode:
+      - 'host': Use host networking (direct access to host network stack, not supported in Swarm)
+      - 'bridge': Custom bridge network (default Docker networking)
+      - 'macvlan': Container gets its own MAC/IP on physical network (requires external macvlan network setup in Swarm)
+      - '' (empty): Uses Docker's default bridge network
+      When traefik is enabled, always add traefik network
+    #}
+    {% 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 %}
+    {#
+      Port mappings when in bridge mode (or empty, which defaults to bridge)
+      - HTTP/HTTPS: Web interface (only if Traefik is disabled)
+      - DNS: Port 53 TCP/UDP (always exposed, even with Traefik, since DNS can't be proxied)
+      - NTP: Port 123 UDP (network time protocol, always exposed)
+      Note: Swarm mode uses 'host' mode for port publishing to avoid port conflicts
+      Note: Host and macvlan modes don't need port mappings (direct network access)
+    #}
+    {% 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
+      {% else %}
+      - "{{ ports_http }}:80/tcp"
+      - "{{ ports_https }}:443/tcp"
+      {% endif %}
+      {% endif %}
+      {% if swarm_enabled %}
+      - target: 53
+        published: {{ ports_dns }}
+        protocol: tcp
+        mode: host
+      - target: 53
+        published: {{ ports_dns }}
+        protocol: udp
+        mode: host
+      - target: 123
+        published: {{ ports_ntp }}
+        protocol: udp
+        mode: host
+      {% else %}
+      - "{{ ports_dns }}:53/tcp"
+      - "{{ ports_dns }}:53/udp"
+      - "{{ ports_ntp }}:123/udp"
+      {% 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
+    #}
+    volumes:
+      {% if volume_mode == 'mount' %}
+      - {{ volume_mount_path }}/dnsmasq:/etc/dnsmasq.d:rw
+      - {{ volume_mount_path }}/pihole:/etc/pihole:rw
+      {% else %}
+      - {{ service_name }}-dnsmasq:/etc/dnsmasq.d
+      - {{ service_name }}-pihole:/etc/pihole
+      {% endif %}
+    {#
+      Required capabilities:
+      - NET_ADMIN: For DHCP and routing table operations
+      - SYS_TIME: For NTP functionality
+    #}
+    cap_add:
+      - NET_ADMIN
+      - SYS_TIME
+    {#
+      When swarm_enabled is set, use Docker secrets for admin password
+    #}
+    {% if swarm_enabled %}
+    secrets:
+      - {{ service_name }}_webpassword
+    {% endif %}
+    {#
+      Deploy configuration for Swarm mode:
+      - Single replica with node placement constraint (Pi-hole doesn't support multi-replica)
+      - Multiple instances would conflict with DNS/DHCP services
+      - Traefik: Labels for reverse proxy integration (Swarm mode)
+      Note: For macvlan in Swarm, create config-only networks on each node first, then use --scope swarm
+    #}
+    {% if swarm_enabled %}
+    deploy:
+      mode: replicated
+      replicas: 1
+      placement:
+        constraints:
+          - node.hostname == {{ swarm_placement_host }}
+      restart_policy:
+        condition: on-failure
+      {#
+        When traefik_enabled is set in swarm mode, add traefik labels
+        (optionally enable TLS if traefik_tls_enabled is set)
+      #}
+      {% 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 %}
+      {% 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)
+    #}
+    {% 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=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 %}
+
 {#
-  Pi-hole Docker Compose Configuration
+  When swarm_enabled is set, define Docker secret for admin password
 #}
-include:
-  - services/pihole.yaml
-  {% if network_mode == 'bridge' or network_mode == 'macvlan' or traefik_enabled %}
-  - common/networks.yaml
+{% if swarm_enabled %}
+secrets:
+  {{ service_name }}_webpassword:
+    file: ./.env.secret.webpassword
+{% 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 }}-dnsmasq:
+    driver: local
+  {{ service_name }}-pihole:
+    driver: local
+{% elif volume_mode == 'nfs' %}
+volumes:
+  {{ service_name }}-dnsmasq:
+    driver: local
+    driver_opts:
+      type: nfs
+      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}/dnsmasq"
+  {{ service_name }}-pihole:
+    driver: local
+    driver_opts:
+      type: nfs
+      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
+      device: ":{{ volume_nfs_path }}/pihole"
+{% endif %}
+
+{#
+  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 %}
-  - common/volumes.yaml
-  {% if swarm_enabled %}
-  - common/secrets.yaml
+  {% if traefik_enabled %}
+  {{ traefik_network }}:
+    external: true
   {% endif %}
+{% endif %}

+ 0 - 266
library/compose/pihole/compose.yaml.j2.backup

@@ -1,266 +0,0 @@
----
-services:
-  {{ service_name }}:
-    image: docker.io/pihole/pihole:2025.11.0
-    {#
-      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 }}
-    {% if container_name %}
-    container_name: {{ container_name }}
-    {% endif %}
-    {% endif %}
-    {#
-      Set container hostname (Pi-hole requires this for proper operation)
-    #}
-    {% if container_hostname %}
-    hostname: {{ container_hostname }}
-    {% endif %}
-    {#
-      Environment variables for Pi-hole configuration
-      - TZ: Timezone for proper log rotation
-      - PIHOLE_UID/GID: User/group for file permissions
-      - WEBPASSWORD: Admin password (from env file in compose mode, from secret in swarm mode)
-      - FTLCONF_dns_listeningMode: In bridge mode, listen on all interfaces
-    #}
-    environment:
-      - TZ={{ container_timezone }}
-      - PIHOLE_UID={{ user_uid }}
-      - PIHOLE_GID={{ user_gid }}
-      {% if swarm_enabled %}
-      - WEBPASSWORD_FILE={{ service_name }}_webpassword
-      {% else %}
-      - FTLCONF_webserver_api_password=${WEBPASSWORD}
-      {% endif %}
-      {% if network_mode == 'bridge' %}
-      - FTLCONF_dns_listeningMode=all
-      {% endif %}
-    {#
-      Network configuration based on network_mode:
-      - 'host': Use host networking (direct access to host network stack, not supported in Swarm)
-      - 'bridge': Custom bridge network (default Docker networking)
-      - 'macvlan': Container gets its own MAC/IP on physical network (requires external macvlan network setup in Swarm)
-      - '' (empty): Uses Docker's default bridge network
-      When traefik is enabled, always add traefik network
-    #}
-    {% 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 %}
-    {#
-      Port mappings when in bridge mode (or empty, which defaults to bridge)
-      - HTTP/HTTPS: Web interface (only if Traefik is disabled)
-      - DNS: Port 53 TCP/UDP (always exposed, even with Traefik, since DNS can't be proxied)
-      - NTP: Port 123 UDP (network time protocol, always exposed)
-      Note: Swarm mode uses 'host' mode for port publishing to avoid port conflicts
-      Note: Host and macvlan modes don't need port mappings (direct network access)
-    #}
-    {% 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
-      {% else %}
-      - "{{ ports_http }}:80/tcp"
-      - "{{ ports_https }}:443/tcp"
-      {% endif %}
-      {% endif %}
-      {% if swarm_enabled %}
-      - target: 53
-        published: {{ ports_dns }}
-        protocol: tcp
-        mode: host
-      - target: 53
-        published: {{ ports_dns }}
-        protocol: udp
-        mode: host
-      - target: 123
-        published: {{ ports_ntp }}
-        protocol: udp
-        mode: host
-      {% else %}
-      - "{{ ports_dns }}:53/tcp"
-      - "{{ ports_dns }}:53/udp"
-      - "{{ ports_ntp }}:123/udp"
-      {% 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
-    #}
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/dnsmasq:/etc/dnsmasq.d:rw
-      - {{ volume_mount_path }}/pihole:/etc/pihole:rw
-      {% else %}
-      - {{ service_name }}-dnsmasq:/etc/dnsmasq.d
-      - {{ service_name }}-pihole:/etc/pihole
-      {% endif %}
-    {#
-      Required capabilities:
-      - NET_ADMIN: For DHCP and routing table operations
-      - SYS_TIME: For NTP functionality
-    #}
-    cap_add:
-      - NET_ADMIN
-      - SYS_TIME
-    {#
-      When swarm_enabled is set, use Docker secrets for admin password
-    #}
-    {% if swarm_enabled %}
-    secrets:
-      - {{ service_name }}_webpassword
-    {% endif %}
-    {#
-      Deploy configuration for Swarm mode:
-      - Single replica with node placement constraint (Pi-hole doesn't support multi-replica)
-      - Multiple instances would conflict with DNS/DHCP services
-      - Traefik: Labels for reverse proxy integration (Swarm mode)
-      Note: For macvlan in Swarm, create config-only networks on each node first, then use --scope swarm
-    #}
-    {% if swarm_enabled %}
-    deploy:
-      mode: replicated
-      replicas: 1
-      placement:
-        constraints:
-          - node.hostname == {{ swarm_placement_host }}
-      restart_policy:
-        condition: on-failure
-      {#
-        When traefik_enabled is set in swarm mode, add traefik labels
-        (optionally enable TLS if traefik_tls_enabled is set)
-      #}
-      {% 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 %}
-      {% 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)
-    #}
-    {% 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 %}
-    {% endif %}
-
-{#
-  When swarm_enabled is set, define Docker secret for admin password
-#}
-{% if swarm_enabled %}
-secrets:
-  {{ service_name }}_webpassword:
-    file: ./.env.secret.webpassword
-{% 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 }}-dnsmasq:
-    driver: local
-  {{ service_name }}-pihole:
-    driver: local
-{% elif volume_mode == 'nfs' %}
-volumes:
-  {{ service_name }}-dnsmasq:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/dnsmasq"
-  {{ service_name }}-pihole:
-    driver: local
-    driver_opts:
-      type: nfs
-      o: addr={{ volume_nfs_server }},{{ volume_nfs_options }}
-      device: ":{{ volume_nfs_path }}/pihole"
-{% endif %}
-
-{#
-  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 - 133
library/compose/pihole/services/pihole.yaml.j2

@@ -1,133 +0,0 @@
-{#
-  Pi-hole: Network-wide ad blocking and DNS privacy
-  Provides DNS, DHCP, and ad blocking services
-#}
-services:
-  {{ service_name }}:
-    image: docker.io/pihole/pihole:2025.11.0
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    {% if container_name %}
-    container_name: {{ container_name }}
-    {% endif %}
-    {% endif %}
-    {% if container_hostname %}
-    hostname: {{ container_hostname }}
-    {% endif %}
-    environment:
-      - TZ={{ container_timezone }}
-      - PIHOLE_UID={{ user_uid }}
-      - PIHOLE_GID={{ user_gid }}
-      {% if swarm_enabled %}
-      - WEBPASSWORD_FILE={{ service_name }}_webpassword
-      {% else %}
-      - FTLCONF_webserver_api_password=${WEBPASSWORD}
-      {% endif %}
-      {% if network_mode == 'bridge' %}
-      - FTLCONF_dns_listeningMode=all
-      {% 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
-      {% else %}
-      - "{{ ports_http }}:80/tcp"
-      - "{{ ports_https }}:443/tcp"
-      {% endif %}
-      {% endif %}
-      {% if swarm_enabled %}
-      - target: 53
-        published: {{ ports_dns }}
-        protocol: tcp
-        mode: host
-      - target: 53
-        published: {{ ports_dns }}
-        protocol: udp
-        mode: host
-      - target: 123
-        published: {{ ports_ntp }}
-        protocol: udp
-        mode: host
-      {% else %}
-      - "{{ ports_dns }}:53/tcp"
-      - "{{ ports_dns }}:53/udp"
-      - "{{ ports_ntp }}:123/udp"
-      {% endif %}
-    {% endif %}
-    volumes:
-      {% if volume_mode == 'mount' %}
-      - {{ volume_mount_path }}/dnsmasq:/etc/dnsmasq.d:rw
-      - {{ volume_mount_path }}/pihole:/etc/pihole:rw
-      {% else %}
-      - {{ service_name }}-dnsmasq:/etc/dnsmasq.d
-      - {{ service_name }}-pihole:/etc/pihole
-      {% endif %}
-    cap_add:
-      - NET_ADMIN
-      - SYS_TIME
-    {% if swarm_enabled %}
-    secrets:
-      - {{ service_name }}_webpassword
-    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 %}
-      {% 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 %}
-    {% endif %}

+ 15 - 30
library/compose/pihole/template.yaml

@@ -4,54 +4,40 @@ schema: "1.2"
 metadata:
   icon:
     provider: selfh
-    id: pihole
+    id: pi-hole
   name: Pihole
   description: |
     Network-wide advertisement and internet tracker blocking application that functions as a DNS blackhole.
     Provides DNS-level content filtering for all network devices, improving browsing performance, privacy, and security.
     Supports custom blocklists, whitelists, and seamless integration with existing network infrastructure.
-    ##  Swarm Deployment Warning
-    Pi-hole uses local storage and configuration files and does NOT support running multiple replicas.
+    ## Prerequisites
+    - :warning: Pi-hole uses local storage and configuration files and does NOT support running multiple replicas.
     This template enforces a single replica with node placement constraints to ensure stable DNS resolution.
     ## References
-    * **Project:** https://pi-hole.net/
-    * **Documentation:** https://docs.pi-hole.net/
-    * **GitHub:** https://github.com/pi-hole/pi-hole
+    - **Project:** https://pi-hole.net/
+    - **Documentation:** https://docs.pi-hole.net/
+    - **GitHub:** https://github.com/pi-hole/pi-hole
   version: 2025.11.0
   author: Christian Lempa
   date: '2025-11-05'
   tags:
     - traefik
     - swarm
-    - network_modes
-    - volume_modes
-  next_steps: |
-    ### 1. Deploy the Service
-    {% if swarm_enabled -%}
-    Deploy to Docker Swarm:
+    - network
+    - volume
+  next_steps: |-
+    Log in with your initial admin user:
     ```bash
-    docker stack deploy -c compose.yaml pihole
+    Username: admin
+    Password: {{ webpassword }}
     ```
-    {% else -%}
-    Start Pi-hole using Docker Compose:
-    ```bash
-    docker compose up -d
-    ```
-    {% endif -%}
-    ### 2. Access the Web Interface
-    {% if traefik_enabled -%}
-    * Navigate to: **https://{{ traefik_host }}/admin**
-    {% else -%}
-    * Navigate to: **http://localhost:{{ ports_http }}/admin**
-    {% endif -%}
-    * Login using the admin password (check `.env.secret` file).
 spec:
   general:
     vars:
       service_name:
-        default: "pihole"
+        default: pihole
       container_name:
-        default: "pihole"
+        default: pihole
   admin_settings:
     description: "Admin Pi-hole Settings"
     required: true
@@ -60,7 +46,6 @@ spec:
         description: "Web interface admin password"
         type: str
         sensitive: true
-        default: ""
         autogenerated: true
   ports:
     vars:
@@ -80,7 +65,7 @@ spec:
   traefik:
     vars:
       traefik_host:
-        default: "pihole.home.arpa"
+        default: pihole
   network:
     vars:
       network_mode:

+ 42 - 98
library/compose/portainer/compose.yaml.j2

@@ -3,72 +3,46 @@ services:
     image: docker.io/portainer/portainer-ce:2.35.0-alpine
     {% if not swarm_enabled %}
     restart: {{ restart_policy }}
-    container_name: {{ container_name }}
     {% endif %}
-    hostname: {{ container_hostname }}
     environment:
       - TZ={{ container_timezone }}
-    {% 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 not traefik_enabled and network_mode == 'bridge' %}
     ports:
-      {% if swarm_enabled %}
-      - target: 9000
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      - target: 9443
-        published: {{ ports_https }}
-        protocol: tcp
-        mode: host
-      - target: 8000
-        published: {{ ports_edge }}
-        protocol: tcp
-        mode: host
-      {% else %}
+    {% if not traefik_enabled %}
       - "{{ ports_http }}:9000"
       - "{{ ports_https }}:9443"
-      - "{{ ports_edge }}:8000"
-      {% endif %}
     {% endif %}
+      - "{{ ports_edge }}:8000"
     volumes:
       - /run/docker.sock:/var/run/docker.sock
-      - portainer-data:/data
+      {% if volume_mode == 'mount' %}
+      - {{ volume_mount_path }}/data:/data
+      {% else %}
+      - {{ service_name }}_data:/data
+      {% 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 }}
+      - 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={{ 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 }}
       {% endif %}
     {% endif %}
-    {% if swarm_enabled or resources_enabled %}
+    {% if swarm_enabled %}
     deploy:
-      {% if swarm_enabled %}
-      mode: {{ swarm_placement_mode }}
-      {% if swarm_placement_mode == 'replicated' %}
-      replicas: {{ swarm_replicas }}
-      {% endif %}
+      mode: replicated
+      replicas: 1
       {% if swarm_placement_host %}
       placement:
         constraints:
@@ -76,70 +50,40 @@ services:
       {% 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 %}
+      {% 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={{ traefik_entrypoint }}
-        {% if authentik_enabled %}
-        - traefik.http.routers.{{ service_name }}-http.middlewares={{ authentik_traefik_middleware }}
-        {% endif %}
+        - 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={{ 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 %}
+        - 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 %}
     {% endif %}
 
+{% if volume_mode == 'local' %}
+volumes:
+  {{ service_name }}_data:
+    driver: local
+{% elif volume_mode == 'nfs' %}
 volumes:
-  portainer-data:
+  {{ service_name }}_data:
     driver: local
+    driver_opts:
+      type: nfs
+      o: addr={{ volume_nfs_server }},nfsvers=4,{{ 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 %}

+ 0 - 124
library/compose/portainer/compose.yaml.j2.final

@@ -1,124 +0,0 @@
-services:
-  {{ service_name }}:
-    image: docker.io/portainer/portainer-ce:2.35.0-alpine
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
-    environment:
-      - TZ={{ container_timezone }}
-    {#
-      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 not traefik_enabled and network_mode == 'bridge' %}
-    ports:
-      {% if swarm_enabled %}
-      - target: 9000
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      - target: 9443
-        published: {{ ports_https }}
-        protocol: tcp
-        mode: host
-      - target: 8000
-        published: {{ ports_edge }}
-        protocol: tcp
-        mode: host
-      {% else %}
-      - "{{ ports_http }}:9000"
-      - "{{ ports_https }}:9443"
-      - "{{ ports_edge }}:8000"
-      {% endif %}
-    {% endif %}
-    volumes:
-      - /run/docker.sock:/var/run/docker.sock
-      - portainer-data:/data
-    {% if 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 %}
-    {% 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 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 %}
-
-volumes:
-  portainer-data:
-    driver: local
-
-    {% endif %}
-  {% if traefik_enabled %}
-  {{ traefik_network }}:
-    external: true
-  {% endif %}
-{% endif %}

+ 0 - 118
library/compose/portainer/compose.yaml.j2.portfix

@@ -1,118 +0,0 @@
-services:
-  {{ service_name }}:
-    image: docker.io/portainer/portainer-ce:2.35.0-alpine
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
-    environment:
-      - TZ={{ container_timezone }}
-    {#
-      When traefik is enabled, add traefik network for reverse proxy access
-    #}
-    {% if traefik_enabled %}
-    networks:
-      {{ traefik_network }}:
-    {% endif %}
-    {% endif %}
-    {% if not traefik_enabled and network_mode == 'bridge' %}
-    ports:
-      {% if swarm_enabled %}
-      - target: 9000
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      - target: 9443
-        published: {{ ports_https }}
-        protocol: tcp
-        mode: host
-      - target: 8000
-        published: {{ ports_edge }}
-        protocol: tcp
-        mode: host
-      {% else %}
-      - "{{ ports_http }}:9000"
-      - "{{ ports_https }}:9443"
-      - "{{ ports_edge }}:8000"
-      {% endif %}
-    {% endif %}
-    volumes:
-      - /run/docker.sock:/var/run/docker.sock
-      - portainer-data:/data
-    {% if 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 %}
-    {% 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 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 %}
-
-volumes:
-  portainer-data:
-    driver: local
-
-    {% endif %}
-  {% if traefik_enabled %}
-  {{ traefik_network }}:
-    external: true
-  {% endif %}
-{% endif %}

+ 11 - 17
library/compose/portainer/template.yaml

@@ -1,49 +1,43 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   icon:
     provider: selfh
     id: portainer
   name: Portainer
-  description: >
+  description: |-
     Portainer is a powerful and user-friendly management tool for Docker and Kubernetes environments.
     It provides a simple web-based interface to manage containers, images, networks, and volumes,
     making it easier to deploy and monitor applications.
-
-
-    Project: https://www.portainer.io/
-
-    Documentation: https://docs.portainer.io/
-
-    GitHub: https://github.com/portainer/portainer
+    ## References
+    - **Project:** https://www.portainer.io/
+    - **Documentation:** https://docs.portainer.io/
+    - **GitHub:** https://github.com/portainer/portainer
   version: 2.35.0-alpine
   author: Christian Lempa
   date: '2025-10-31'
   tags:
     - traefik
+    - swarm
+    - volumes
+schema: 1.2
 spec:
   general:
     vars:
       service_name:
-        default: "portainer"
-      container_name:
-        default: "portainer"
+        default: portainer
   ports:
     vars:
       ports_http:
-        description: "Host port for HTTP (9000)"
-        type: int
         default: 9000
       ports_https:
-        description: "Host port for HTTPS (9443)"
-        type: int
         default: 9443
       ports_edge:
         description: "Host port for Edge agent (8000)"
         type: int
         default: 8000
+        required: true
   traefik:
     vars:
       traefik_host:
-        default: portainer.home.arpa
+        default: portainer

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

@@ -4,7 +4,7 @@ schema: "1.2"
 metadata:
   icon:
     provider: selfh
-    id: postgres
+    id: postgresql
   name: PostgreSQL
   description: >
     PostgreSQL is a powerful, open source object-relational database system with over 30 years of active development
@@ -21,7 +21,7 @@ metadata:
   date: '2025-09-28'
   tags:
     - swarm
-    - volume_mode
+  draft: true
 spec:
   general:
     vars:

+ 2 - 23
library/compose/renovate/template.yaml

@@ -4,7 +4,7 @@ schema: "1.2"
 metadata:
   icon:
     provider: selfh
-    id: renovate
+    id: mend-renovate
   name: Renovate
   description: |-
     **Renovate** is an automated dependency update tool that creates pull requests for newer versions of dependencies.
@@ -28,28 +28,7 @@ metadata:
   tags:
     - traefik
     - swarm
-  next_steps: |
-    ## Post-Installation Steps
-    {% if swarm_enabled -%}
-    1. **Deploy to Docker Swarm**:
-       ```bash
-       docker stack deploy -c compose.yaml {{ service_name }}
-       ```
-       Secrets are automatically created from the generated `.env.secret.*` files.
-    2. **Access the Web Interface**:
-       - {%- if traefik_enabled %}Visit: https://{{ traefik_host }}
-       {%- else %}Visit: http://<swarm-node-ip>:{{ ports_http }}{% endif %}
-    3. **Configure webhooks** in your Git platform to trigger Renovate on repository events
-    {% else -%}
-    1. **Deploy with Docker Compose**:
-       ```bash
-       docker compose up -d
-       ```
-    2. **Access the Web Interface**:
-       - {%- if traefik_enabled %}Visit: https://{{ traefik_host }}
-       {%- else %}Visit: http://localhost:{{ ports_http }}{% endif %}
-    3. **Configure webhooks** in your Git platform to trigger Renovate on repository events
-    {% endif -%}
+  draft: true
 spec:
   general:
     vars:

+ 2 - 15
library/compose/semaphoreui/.env.j2

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

+ 48 - 147
library/compose/semaphoreui/compose.yaml.j2

@@ -1,204 +1,105 @@
 services:
   {{ service_name }}:
     image: docker.io/semaphoreui/semaphore:v2.16.43
-    {% if not swarm_enabled %}
     restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
-    user: "{{ user_uid }}:{{ user_gid }}"
-    environment:
-      - TZ={{ container_timezone }}
-      {% if database_type == 'mysql' %}
-      - SEMAPHORE_DB_DIALECT=mysql
-      {% elif database_type == 'postgres' %}
-      - SEMAPHORE_DB_DIALECT=postgres
-      {% endif %}
+    environment:  
+      - SEMAPHORE_DB_DIALECT={{ database_type }}
       {% if database_external %}
       - SEMAPHORE_DB_HOST={{ database_host }}
       {% else %}
-      - SEMAPHORE_DB_HOST={{ service_name }}-{{ database_type }}
+      - SEMAPHORE_DB_HOST={{ service_name }}_db
       {% endif %}
-      - SEMAPHORE_DB_PORT={% if database_type == 'postgres' %}5432{% else %}3306{% endif %}
       - SEMAPHORE_DB={{ database_name }}
       - SEMAPHORE_DB_USER={{ database_user }}
       - SEMAPHORE_DB_PASS=${DATABASE_PASSWORD}
-      - SEMAPHORE_ADMIN={{ semaphore_admin_name }}
-      - SEMAPHORE_ADMIN_NAME={{ semaphore_admin_name }}
-      - SEMAPHORE_ADMIN_EMAIL={{ semaphore_admin_email }}
+      - SEMAPHORE_ADMIN={{ admin_user }}
+      - SEMAPHORE_ADMIN_NAME={{ admin_name }}
+      - SEMAPHORE_ADMIN_EMAIL={{ admin_email }}
       - SEMAPHORE_ADMIN_PASSWORD=${SEMAPHORE_ADMIN_PASSWORD}
-      - SEMAPHORE_PLAYBOOK_PATH={{ semaphore_playbook_path }}
+      - SEMAPHORE_PLAYBOOK_PATH=/tmp/semaphore/
       - SEMAPHORE_ACCESS_KEY_ENCRYPTION=${SEMAPHORE_ACCESS_KEY_ENCRYPTION}
       - ANSIBLE_HOST_KEY_CHECKING={{ ansible_host_key_checking }}
-      {% if email_enabled %}
-      - SEMAPHORE_EMAIL_SENDER={{ email_from }}
-      - SEMAPHORE_EMAIL_HOST={{ email_host }}
-      - SEMAPHORE_EMAIL_PORT={{ email_port }}
-      - SEMAPHORE_EMAIL_USERNAME={{ email_username }}
-      - SEMAPHORE_EMAIL_PASSWORD=${EMAIL_PASSWORD}
-      - SEMAPHORE_EMAIL_SECURE={{ email_use_tls }}
-      {% endif %}
+    {% if not database_external or traefik_enabled %}
+    networks:
+    {% if not database_external %}
+      - {{ service_name }}_backend
+    {% endif %}
+    {% if traefik_enabled %}
+      - {{ traefik_network }}
+    {% endif %}
     {% endif %}
     {% if not traefik_enabled %}
     ports:
-      {% if swarm_enabled %}
-      - target: 3000
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      {% else %}
       - "{{ ports_http }}:3000"
-      {% endif %}
     {% endif %}
-    volumes:
-      - ./inventory:/inventory:ro
-      - ./authorized-keys:/authorized-keys:ro
-      - ./config:/etc/semaphore:rw
-    {% 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:
-      {% if database_type == 'mysql' %}
-      - {{ service_name }}-mysql
-      {% elif database_type == 'postgres' %}
-      - {{ service_name }}-postgres
-      {% 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=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 %}
+      - {{ service_name }}_db
     {% endif %}
 
-  {% if not database_external %}
-  {% if database_type == 'mysql' %}
-  {{ service_name }}-mysql:
-    image: docker.io/library/mysql:8.4
-    {% if not swarm_enabled %}
+  {% if not database_external and database_type == "postgres" %}
+  {{ service_name }}_db:
+    image: docker.io/library/postgres:17.6
     restart: {{ restart_policy }}
-    container_name: {{ service_name }}-mysql
-    {% endif %}
     environment:
-      {% if database_type == 'mysql' %}
-      - MYSQL_RANDOM_ROOT_PASSWORD=yes
-      - MYSQL_DATABASE={{ database_name }}
-      - MYSQL_USER={{ database_user }}
-      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
-      - MYSQL_CHARSET=utf8mb4
-      - MYSQL_COLLATION=utf8mb4_unicode_ci
-      {% elif database_type == 'postgres' %}
-      - POSTGRES_DB={{ database_name }}
       - POSTGRES_USER={{ database_user }}
       - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
-      - POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C
-      {% endif %}
+      - POSTGRES_DB={{ database_name }}
+    networks:
+      - {{ service_name }}_backend
     healthcheck:
-      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "{{ database_user }}", "-p{{ database_password }}"]
+      test: ["CMD-SHELL", "pg_isready -U {{ database_user }}"]
       start_period: 30s
       interval: 10s
       timeout: 10s
       retries: 5
-    {% endif %}
     volumes:
-      - database_data:/var/lib/mysql
-  {% elif database_type == 'postgres' %}
-  {{ service_name }}-postgres:
-    image: docker.io/library/postgres:17.6
-    {% if not swarm_enabled %}
+      - {{ service_name }}_db:/var/lib/postgresql/data
+  {% elif not database_external and database_type == "mysql" %}
+  {{ service_name }}_db:
+    image: docker.io/library/mysql:8.1
     restart: {{ restart_policy }}
-    container_name: {{ service_name }}-postgres
-    {% endif %}
     environment:
-      {% if database_type == 'mysql' %}
-      - MYSQL_RANDOM_ROOT_PASSWORD=yes
-      - MYSQL_DATABASE={{ database_name }}
       - MYSQL_USER={{ database_user }}
       - MYSQL_PASSWORD=${DATABASE_PASSWORD}
-      - MYSQL_CHARSET=utf8mb4
-      - MYSQL_COLLATION=utf8mb4_unicode_ci
-      {% elif database_type == 'postgres' %}
-      - POSTGRES_DB={{ database_name }}
-      - POSTGRES_USER={{ database_user }}
-      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
-      - POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C
-      {% endif %}
-    healthcheck:
-      test: ["CMD-SHELL", "pg_isready -U {{ database_user }}"]
-      start_period: 30s
-      interval: 10s
-      timeout: 10s
-      retries: 5
-    {% endif %}
+      - MYSQL_DATABASE={{ database_name }}
+      - MYSQL_ROOT_PASSWORD=${DATABASE_PASSWORD}
+    networks:
+      - {{ service_name }}_backend
     volumes:
-      - database_data:/var/lib/postgresql/data
-  {% endif %}
+      - {{ service_name }}_db:/var/lib/mysql
   {% endif %}
 
-    {% endif %}
+{% if not database_external %}
+volumes:
+  {{ service_name }}_db:
+    driver: local
+{% endif %}
+
+{% 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 %}
-
-volumes:
-  {% if not database_external %}
-  database_data:
-    driver: local
-  {% endif %}

+ 0 - 264
library/compose/semaphoreui/compose.yaml.j2.bak3

@@ -1,264 +0,0 @@
-services:
-  {{ service_name }}:
-    image: docker.io/semaphoreui/semaphore:v2.16.43
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
-    user: "{{ user_uid }}:{{ user_gid }}"
-    environment:
-      - TZ={{ container_timezone }}
-      {% if database_type == 'mysql' %}
-      - SEMAPHORE_DB_DIALECT=mysql
-      {% elif database_type == 'postgres' %}
-      - SEMAPHORE_DB_DIALECT=postgres
-      {% endif %}
-      {% if database_external %}
-      - SEMAPHORE_DB_HOST={{ database_host }}
-      {% else %}
-      - SEMAPHORE_DB_HOST={{ service_name }}-{{ database_type }}
-      {% endif %}
-      - SEMAPHORE_DB_PORT={% if database_type == 'postgres' %}5432{% else %}3306{% endif %}
-      - SEMAPHORE_DB={{ database_name }}
-      - SEMAPHORE_DB_USER={{ database_user }}
-      - SEMAPHORE_DB_PASS=${DATABASE_PASSWORD}
-      - SEMAPHORE_ADMIN={{ semaphore_admin_name }}
-      - SEMAPHORE_ADMIN_NAME={{ semaphore_admin_name }}
-      - SEMAPHORE_ADMIN_EMAIL={{ semaphore_admin_email }}
-      - SEMAPHORE_ADMIN_PASSWORD=${SEMAPHORE_ADMIN_PASSWORD}
-      - SEMAPHORE_PLAYBOOK_PATH={{ semaphore_playbook_path }}
-      - SEMAPHORE_ACCESS_KEY_ENCRYPTION=${SEMAPHORE_ACCESS_KEY_ENCRYPTION}
-      - ANSIBLE_HOST_KEY_CHECKING={{ ansible_host_key_checking }}
-      {% if email_enabled %}
-      - SEMAPHORE_EMAIL_SENDER={{ email_from }}
-      - SEMAPHORE_EMAIL_HOST={{ email_host }}
-      - SEMAPHORE_EMAIL_PORT={{ email_port }}
-      - SEMAPHORE_EMAIL_USERNAME={{ email_username }}
-      - SEMAPHORE_EMAIL_PASSWORD=${EMAIL_PASSWORD}
-      - SEMAPHORE_EMAIL_SECURE={{ email_use_tls }}
-      {% endif %}
-    {% if network_mode == 'host' %}
-    network_mode: host
-    {% else %}
-    networks:
-      {% if traefik_enabled %}
-      {{ traefik_network }}:
-      {% endif %}
-      {% if network_mode == 'macvlan' %}
-      {{ network_name }}:
-        ipv4_address: {{ network_macvlan_ipv4_address }}
-      {% elif network_mode == 'bridge' %}
-      {{ network_name }}:
-      {% endif %}
-    {% endif %}
-    {% if not traefik_enabled and network_mode == 'bridge' %}
-    ports:
-      {% if swarm_enabled %}
-      - target: 3000
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      {% else %}
-      - "{{ ports_http }}:3000"
-      {% endif %}
-    {% endif %}
-    volumes:
-      - ./inventory:/inventory:ro
-      - ./authorized-keys:/authorized-keys:ro
-      - ./config:/etc/semaphore:rw
-    {% if traefik_enabled and not swarm_enabled %}
-    labels:
-      - traefik.enable=true
-      - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}-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 %}
-    {% endif %}
-    depends_on:
-      {% if database_type == 'mysql' %}
-      - {{ service_name }}-mysql
-      {% elif database_type == 'postgres' %}
-      - {{ service_name }}-postgres
-      {% 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=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 %}
-
-  {% if not database_external %}
-  {% if database_type == 'mysql' %}
-  {{ service_name }}-mysql:
-    image: docker.io/library/mysql:8.4
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ service_name }}-mysql
-    {% endif %}
-    environment:
-      {% if database_type == 'mysql' %}
-      - MYSQL_RANDOM_ROOT_PASSWORD=yes
-      - MYSQL_DATABASE={{ database_name }}
-      - MYSQL_USER={{ database_user }}
-      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
-      - MYSQL_CHARSET=utf8mb4
-      - MYSQL_COLLATION=utf8mb4_unicode_ci
-      {% elif database_type == 'postgres' %}
-      - POSTGRES_DB={{ database_name }}
-      - POSTGRES_USER={{ database_user }}
-      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
-      - POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C
-      {% endif %}
-    healthcheck:
-      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "{{ database_user }}", "-p{{ database_password }}"]
-      start_period: 30s
-      interval: 10s
-      timeout: 10s
-      retries: 5
-    {% if network_mode == 'host' %}
-    network_mode: host
-    {% else %}
-    networks:
-      {% if traefik_enabled %}
-      {{ traefik_network }}:
-      {% endif %}
-      {% if network_mode == 'macvlan' %}
-      {{ network_name }}:
-        ipv4_address: {{ network_macvlan_ipv4_address }}
-      {% elif network_mode == 'bridge' %}
-      {{ network_name }}:
-      {% endif %}
-    {% endif %}
-    volumes:
-      - database_data:/var/lib/mysql
-  {% elif database_type == 'postgres' %}
-  {{ service_name }}-postgres:
-    image: docker.io/library/postgres:17.6
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ service_name }}-postgres
-    {% endif %}
-    environment:
-      {% if database_type == 'mysql' %}
-      - MYSQL_RANDOM_ROOT_PASSWORD=yes
-      - MYSQL_DATABASE={{ database_name }}
-      - MYSQL_USER={{ database_user }}
-      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
-      - MYSQL_CHARSET=utf8mb4
-      - MYSQL_COLLATION=utf8mb4_unicode_ci
-      {% elif database_type == 'postgres' %}
-      - POSTGRES_DB={{ database_name }}
-      - POSTGRES_USER={{ database_user }}
-      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
-      - POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C
-      {% endif %}
-    healthcheck:
-      test: ["CMD-SHELL", "pg_isready -U {{ database_user }}"]
-      start_period: 30s
-      interval: 10s
-      timeout: 10s
-      retries: 5
-    {% if network_mode == 'host' %}
-    network_mode: host
-    {% else %}
-    networks:
-      {% if traefik_enabled %}
-      {{ traefik_network }}:
-      {% endif %}
-      {% if network_mode == 'macvlan' %}
-      {{ network_name }}:
-        ipv4_address: {{ network_macvlan_ipv4_address }}
-      {% elif network_mode == 'bridge' %}
-      {{ network_name }}:
-      {% endif %}
-    {% endif %}
-    volumes:
-      - database_data:/var/lib/postgresql/data
-  {% 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 %}
-  {% if traefik_enabled %}
-  {{ traefik_network }}:
-    external: true
-  {% endif %}
-{% endif %}
-
-volumes:
-  {% if not database_external %}
-  database_data:
-    driver: local
-  {% endif %}

+ 0 - 222
library/compose/semaphoreui/compose.yaml.j2.final

@@ -1,222 +0,0 @@
-services:
-  {{ service_name }}:
-    image: docker.io/semaphoreui/semaphore:v2.16.43
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
-    user: "{{ user_uid }}:{{ user_gid }}"
-    environment:
-      - TZ={{ container_timezone }}
-      {% if database_type == 'mysql' %}
-      - SEMAPHORE_DB_DIALECT=mysql
-      {% elif database_type == 'postgres' %}
-      - SEMAPHORE_DB_DIALECT=postgres
-      {% endif %}
-      {% if database_external %}
-      - SEMAPHORE_DB_HOST={{ database_host }}
-      {% else %}
-      - SEMAPHORE_DB_HOST={{ service_name }}-{{ database_type }}
-      {% endif %}
-      - SEMAPHORE_DB_PORT={% if database_type == 'postgres' %}5432{% else %}3306{% endif %}
-      - SEMAPHORE_DB={{ database_name }}
-      - SEMAPHORE_DB_USER={{ database_user }}
-      - SEMAPHORE_DB_PASS=${DATABASE_PASSWORD}
-      - SEMAPHORE_ADMIN={{ semaphore_admin_name }}
-      - SEMAPHORE_ADMIN_NAME={{ semaphore_admin_name }}
-      - SEMAPHORE_ADMIN_EMAIL={{ semaphore_admin_email }}
-      - SEMAPHORE_ADMIN_PASSWORD=${SEMAPHORE_ADMIN_PASSWORD}
-      - SEMAPHORE_PLAYBOOK_PATH={{ semaphore_playbook_path }}
-      - SEMAPHORE_ACCESS_KEY_ENCRYPTION=${SEMAPHORE_ACCESS_KEY_ENCRYPTION}
-      - ANSIBLE_HOST_KEY_CHECKING={{ ansible_host_key_checking }}
-      {% if email_enabled %}
-      - SEMAPHORE_EMAIL_SENDER={{ email_from }}
-      - SEMAPHORE_EMAIL_HOST={{ email_host }}
-      - SEMAPHORE_EMAIL_PORT={{ email_port }}
-      - SEMAPHORE_EMAIL_USERNAME={{ email_username }}
-      - SEMAPHORE_EMAIL_PASSWORD=${EMAIL_PASSWORD}
-      - SEMAPHORE_EMAIL_SECURE={{ email_use_tls }}
-      {% endif %}
-      {% if network_mode == 'macvlan' %}
-      {{ network_name }}:
-        ipv4_address: {{ network_macvlan_ipv4_address }}
-      {% elif network_mode == 'bridge' %}
-      {{ network_name }}:
-      {% endif %}
-    {% endif %}
-    {% if not traefik_enabled and network_mode == 'bridge' %}
-    ports:
-      {% if swarm_enabled %}
-      - target: 3000
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      {% else %}
-      - "{{ ports_http }}:3000"
-      {% endif %}
-    {% endif %}
-    volumes:
-      - ./inventory:/inventory:ro
-      - ./authorized-keys:/authorized-keys:ro
-      - ./config:/etc/semaphore:rw
-    {% if traefik_enabled and not swarm_enabled %}
-    labels:
-      - traefik.enable=true
-      - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}-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 %}
-    {% endif %}
-    depends_on:
-      {% if database_type == 'mysql' %}
-      - {{ service_name }}-mysql
-      {% elif database_type == 'postgres' %}
-      - {{ service_name }}-postgres
-      {% 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=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 %}
-
-  {% if not database_external %}
-  {% if database_type == 'mysql' %}
-  {{ service_name }}-mysql:
-    image: docker.io/library/mysql:8.4
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ service_name }}-mysql
-    {% endif %}
-    environment:
-      {% if database_type == 'mysql' %}
-      - MYSQL_RANDOM_ROOT_PASSWORD=yes
-      - MYSQL_DATABASE={{ database_name }}
-      - MYSQL_USER={{ database_user }}
-      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
-      - MYSQL_CHARSET=utf8mb4
-      - MYSQL_COLLATION=utf8mb4_unicode_ci
-      {% elif database_type == 'postgres' %}
-      - POSTGRES_DB={{ database_name }}
-      - POSTGRES_USER={{ database_user }}
-      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
-      - POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C
-      {% endif %}
-    healthcheck:
-      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "{{ database_user }}", "-p{{ database_password }}"]
-      start_period: 30s
-      interval: 10s
-      timeout: 10s
-      retries: 5
-      {% if network_mode == 'macvlan' %}
-      {{ network_name }}:
-        ipv4_address: {{ network_macvlan_ipv4_address }}
-      {% elif network_mode == 'bridge' %}
-      {{ network_name }}:
-      {% endif %}
-    {% endif %}
-    volumes:
-      - database_data:/var/lib/mysql
-  {% elif database_type == 'postgres' %}
-  {{ service_name }}-postgres:
-    image: docker.io/library/postgres:17.6
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ service_name }}-postgres
-    {% endif %}
-    environment:
-      {% if database_type == 'mysql' %}
-      - MYSQL_RANDOM_ROOT_PASSWORD=yes
-      - MYSQL_DATABASE={{ database_name }}
-      - MYSQL_USER={{ database_user }}
-      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
-      - MYSQL_CHARSET=utf8mb4
-      - MYSQL_COLLATION=utf8mb4_unicode_ci
-      {% elif database_type == 'postgres' %}
-      - POSTGRES_DB={{ database_name }}
-      - POSTGRES_USER={{ database_user }}
-      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
-      - POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C
-      {% endif %}
-    healthcheck:
-      test: ["CMD-SHELL", "pg_isready -U {{ database_user }}"]
-      start_period: 30s
-      interval: 10s
-      timeout: 10s
-      retries: 5
-      {% if network_mode == 'macvlan' %}
-      {{ network_name }}:
-        ipv4_address: {{ network_macvlan_ipv4_address }}
-      {% elif network_mode == 'bridge' %}
-      {{ network_name }}:
-      {% endif %}
-    {% endif %}
-    volumes:
-      - database_data:/var/lib/postgresql/data
-  {% endif %}
-  {% endif %}
-
-    {% endif %}
-  {% if traefik_enabled %}
-  {{ traefik_network }}:
-    external: true
-  {% endif %}
-{% endif %}
-
-volumes:
-  {% if not database_external %}
-  database_data:
-    driver: local
-  {% endif %}

+ 0 - 204
library/compose/semaphoreui/compose.yaml.j2.portfix

@@ -1,204 +0,0 @@
-services:
-  {{ service_name }}:
-    image: docker.io/semaphoreui/semaphore:v2.16.43
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ container_name }}
-    {% endif %}
-    hostname: {{ container_hostname }}
-    user: "{{ user_uid }}:{{ user_gid }}"
-    environment:
-      - TZ={{ container_timezone }}
-      {% if database_type == 'mysql' %}
-      - SEMAPHORE_DB_DIALECT=mysql
-      {% elif database_type == 'postgres' %}
-      - SEMAPHORE_DB_DIALECT=postgres
-      {% endif %}
-      {% if database_external %}
-      - SEMAPHORE_DB_HOST={{ database_host }}
-      {% else %}
-      - SEMAPHORE_DB_HOST={{ service_name }}-{{ database_type }}
-      {% endif %}
-      - SEMAPHORE_DB_PORT={% if database_type == 'postgres' %}5432{% else %}3306{% endif %}
-      - SEMAPHORE_DB={{ database_name }}
-      - SEMAPHORE_DB_USER={{ database_user }}
-      - SEMAPHORE_DB_PASS=${DATABASE_PASSWORD}
-      - SEMAPHORE_ADMIN={{ semaphore_admin_name }}
-      - SEMAPHORE_ADMIN_NAME={{ semaphore_admin_name }}
-      - SEMAPHORE_ADMIN_EMAIL={{ semaphore_admin_email }}
-      - SEMAPHORE_ADMIN_PASSWORD=${SEMAPHORE_ADMIN_PASSWORD}
-      - SEMAPHORE_PLAYBOOK_PATH={{ semaphore_playbook_path }}
-      - SEMAPHORE_ACCESS_KEY_ENCRYPTION=${SEMAPHORE_ACCESS_KEY_ENCRYPTION}
-      - ANSIBLE_HOST_KEY_CHECKING={{ ansible_host_key_checking }}
-      {% if email_enabled %}
-      - SEMAPHORE_EMAIL_SENDER={{ email_from }}
-      - SEMAPHORE_EMAIL_HOST={{ email_host }}
-      - SEMAPHORE_EMAIL_PORT={{ email_port }}
-      - SEMAPHORE_EMAIL_USERNAME={{ email_username }}
-      - SEMAPHORE_EMAIL_PASSWORD=${EMAIL_PASSWORD}
-      - SEMAPHORE_EMAIL_SECURE={{ email_use_tls }}
-      {% endif %}
-    {% endif %}
-    {% if not traefik_enabled and network_mode == 'bridge' %}
-    ports:
-      {% if swarm_enabled %}
-      - target: 3000
-        published: {{ ports_http }}
-        protocol: tcp
-        mode: host
-      {% else %}
-      - "{{ ports_http }}:3000"
-      {% endif %}
-    {% endif %}
-    volumes:
-      - ./inventory:/inventory:ro
-      - ./authorized-keys:/authorized-keys:ro
-      - ./config:/etc/semaphore:rw
-    {% if traefik_enabled and not swarm_enabled %}
-    labels:
-      - traefik.enable=true
-      - traefik.docker.network={{ traefik_network }}
-      - traefik.http.services.{{ service_name }}-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 %}
-    {% endif %}
-    depends_on:
-      {% if database_type == 'mysql' %}
-      - {{ service_name }}-mysql
-      {% elif database_type == 'postgres' %}
-      - {{ service_name }}-postgres
-      {% 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=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 %}
-
-  {% if not database_external %}
-  {% if database_type == 'mysql' %}
-  {{ service_name }}-mysql:
-    image: docker.io/library/mysql:8.4
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ service_name }}-mysql
-    {% endif %}
-    environment:
-      {% if database_type == 'mysql' %}
-      - MYSQL_RANDOM_ROOT_PASSWORD=yes
-      - MYSQL_DATABASE={{ database_name }}
-      - MYSQL_USER={{ database_user }}
-      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
-      - MYSQL_CHARSET=utf8mb4
-      - MYSQL_COLLATION=utf8mb4_unicode_ci
-      {% elif database_type == 'postgres' %}
-      - POSTGRES_DB={{ database_name }}
-      - POSTGRES_USER={{ database_user }}
-      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
-      - POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C
-      {% endif %}
-    healthcheck:
-      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "{{ database_user }}", "-p{{ database_password }}"]
-      start_period: 30s
-      interval: 10s
-      timeout: 10s
-      retries: 5
-    {% endif %}
-    volumes:
-      - database_data:/var/lib/mysql
-  {% elif database_type == 'postgres' %}
-  {{ service_name }}-postgres:
-    image: docker.io/library/postgres:17.6
-    {% if not swarm_enabled %}
-    restart: {{ restart_policy }}
-    container_name: {{ service_name }}-postgres
-    {% endif %}
-    environment:
-      {% if database_type == 'mysql' %}
-      - MYSQL_RANDOM_ROOT_PASSWORD=yes
-      - MYSQL_DATABASE={{ database_name }}
-      - MYSQL_USER={{ database_user }}
-      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
-      - MYSQL_CHARSET=utf8mb4
-      - MYSQL_COLLATION=utf8mb4_unicode_ci
-      {% elif database_type == 'postgres' %}
-      - POSTGRES_DB={{ database_name }}
-      - POSTGRES_USER={{ database_user }}
-      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
-      - POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C
-      {% endif %}
-    healthcheck:
-      test: ["CMD-SHELL", "pg_isready -U {{ database_user }}"]
-      start_period: 30s
-      interval: 10s
-      timeout: 10s
-      retries: 5
-    {% endif %}
-    volumes:
-      - database_data:/var/lib/postgresql/data
-  {% endif %}
-  {% endif %}
-
-    {% endif %}
-  {% if traefik_enabled %}
-  {{ traefik_network }}:
-    external: true
-  {% endif %}
-{% endif %}
-
-volumes:
-  {% if not database_external %}
-  database_data:
-    driver: local
-  {% endif %}

+ 60 - 88
library/compose/semaphoreui/template.yaml

@@ -1,117 +1,89 @@
 ---
 kind: compose
-schema: "1.2"
 metadata:
   icon:
     provider: selfh
-    id: semaphoreui
+    id: semaphore-ui
   name: Semaphore UI
-  description: >
+  description: |-
     Modern UI for Ansible automation with task scheduling and web-based management.
     Semaphore provides a beautiful web interface to run Ansible playbooks, manage
     inventories, and schedule automated tasks. Perfect for teams who want a
     user-friendly way to execute and monitor Ansible automation.
-
-
-    Project: https://www.semaphoreui.com/
-
-    Documentation: https://docs.semaphoreui.com/
-
-    GitHub: https://github.com/semaphoreui/semaphore
+    ## Prerequisites
+    - :info: SemaphoreUI supports multiple database backends. You can choose between SQLite (default),
+    PostgreSQL, or MySQL. SQLite is suitable for small deployments, while PostgreSQL and MySQL
+    are recommended for larger installations.
+    ## References
+    - **Project:** https://www.semaphoreui.com/
+    - **Documentation:** https://docs.semaphoreui.com/
+    - **GitHub:** https://github.com/semaphoreui/semaphore
   version: v2.16.43
   author: Christian Lempa
   date: '2025-11-07'
   tags:
     - traefik
-  next_steps: |
-    1. Start Semaphore UI:
-       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. Login with your admin credentials:
-       - Username: {{ semaphore_admin_name }}
-       - Email: {{ semaphore_admin_email }}
-       - Password: {{ semaphore_admin_password }}
-
-    4. Getting started:
-       - Add SSH keys for accessing your managed hosts
-       - Create your first project and link it to a Git repository
-       - Add inventories (static or dynamic)
-       - Create task templates from your Ansible playbooks
-       - Schedule or run tasks on-demand
-
-    5. Security recommendations:
-       - Change the admin password after first login
-       - Enable two-factor authentication in user settings
-       - Review user permissions and create additional users
-       - Use SSH key authentication for Ansible connections
-
-    For more information, visit: https://docs.semaphoreui.com/
-
+    - database
+  next_steps: |-
+    Log in with your initial admin user:
+    ```bash
+    Username: {{ admin_user }}
+    Password: {{ admin_pass }}
+    ```
+schema: 1.2
 spec:
   general:
     vars:
       service_name:
-        default: "semaphore"
+        default: semaphoreui
       container_name:
-        default: "semaphore"
-  database:
-    required: true
-    vars:
-      database_type:
-        default: "mysql"
-      database_name:
-        default: "semaphore"
-      database_user:
-        default: "semaphore"
-  ports:
-    vars:
-      ports_http:
-        description: "Host port for web interface"
-        type: int
-        default: 3000
-  traefik:
-    vars:
-      traefik_host:
-        default: semaphoreui.home.arpa
-  semaphore:
-    description: "Configure Ansible Semaphore application settings"
-    required: true
-    vars:
-      semaphore_admin_name:
-        description: "Initial admin username"
-        type: str
-        default: "admin"
-      semaphore_admin_email:
-        description: "Admin email address"
+        default: semaphoreui
+      secret_key:
+        description: "Secret key for encrypting access keys"
         type: str
-        default: "admin@home.arpa"
-      semaphore_admin_password:
-        description: "Initial admin password"
-        extra: "Leave empty for auto-generated 20-character secure password"
-        type: str
-        default: ""
         sensitive: true
         autogenerated: true
-      semaphore_access_key_encryption:
-        description: "Encryption key for access keys storage"
-        extra: "Leave empty for auto-generated 32-character secure key"
+        required: true
+      admin_user:
+        description: "Administrator username"
+        type: str
+        required: true
+        default: admin
+      admin_name:
+        description: "Administrator full name"
+        type: str
+        required: true
+        default: Administrator
+      admin_email:
+        description: "Administrator email address"
+        type: str
+        required: true
+        default: admin@home.arpa
+      admin_pass:
+        description: "Administrator password"
         type: str
-        default: ""
         sensitive: true
         autogenerated: true
-      semaphore_playbook_path:
-        description: "Path for temporary playbook execution"
-        type: str
-        default: "/tmp/semaphore/"
+        required: true
       ansible_host_key_checking:
         description: "Enable Ansible SSH host key checking"
         type: bool
-        default: false
+  ports:
+    vars:
+      ports_http:
+        default: 3000
+  traefik:
+    vars:
+      traefik_host:
+        default: semaphoreui
+  database:
+    vars:
+      database_type:
+        options:
+          - postgres
+          - mysql
+        default: mysql
+      database_name:
+        default: semaphore
+      database_user:
+        default: semaphore

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

@@ -1,7 +1,7 @@
 ---
 services:
   {{ service_name }}:
-    image: docker.io/library/traefik:v3.5.4
+    image: docker.io/library/traefik:v3.6.4
     {% if not swarm_enabled %}
     {% if container_name %}
     container_name: {{ container_name }}

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

@@ -9,13 +9,12 @@ metadata:
     - **Project:** https://traefik.io/
     - **Documentation:** https://doc.traefik.io/traefik/
     - **GitHub:** https://github.com/traefik/traefik
-  version: v3.5.4
+  version: v3.6.4
   author: Christian Lempa
   date: "2025-11-05"
   tags:
-    - authentik
     - swarm
-    - volume_mode
+    - volume
   icon:
     provider: simpleicons
     id: traefikproxy

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

@@ -4,7 +4,7 @@ schema: "1.2"
 metadata:
   icon:
     provider: selfh
-    id: twingate-connector
+    id: twingate
   name: Twingate_Connector
   description: >
     The Twingate Connector is a lightweight software component that establishes secure connections between your private network and the
@@ -23,6 +23,7 @@ metadata:
   date: '2025-10-31'
   tags:
     - swarm
+  draft: true
 spec:
   general:
     vars:

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

@@ -4,7 +4,7 @@ schema: "1.2"
 metadata:
   icon:
     provider: selfh
-    id: uptimekuma
+    id: uptime-kuma
   name: Uptimekuma
   description: >
     Uptimekuma is a self-hosted monitoring tool that allows you to keep track of the uptime and performance of your websites and services.
@@ -20,6 +20,7 @@ metadata:
   author: Christian Lempa
   date: '2025-10-31'
   tags: []
+  draft: true
 spec:
   general:
     vars:

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

@@ -3,7 +3,7 @@ kind: compose
 metadata:
   icon:
     provider: selfh
-    id: whoami
+    id: traefik
   name: Whoami
   description: |
     A **simple web application** that displays information about the HTTP request it receives.

+ 2 - 2
library/helm/certmanager/template.yaml

@@ -16,8 +16,8 @@ metadata:
   date: "2025-01-11"
   tags: []
   icon:
-    provider: simpleicons
-    id: letsencrypt
+    provider: selfh
+    id: lets-encrypt
   draft: true
   next_steps: ""
 schema: "1.0"

+ 1 - 1
library/kubernetes/certmanager-certificate/template.yaml

@@ -4,7 +4,7 @@ schema: "1.0"
 metadata:
   icon:
     provider: selfh
-    id: certmanager-certificate
+    id: lets-encrypt
   name: Cert-Manager Certificate
   description: >
     Cert-manager Certificate resource for requesting TLS certificates from an Issuer or ClusterIssuer.

+ 1 - 1
library/kubernetes/certmanager-clusterissuer/template.yaml

@@ -4,7 +4,7 @@ schema: "1.0"
 metadata:
   icon:
     provider: selfh
-    id: certmanager-clusterissuer
+    id: lets-encrypt
   name: Cert-Manager ClusterIssuer (Cloudflare)
   description: >
     Cert-manager ClusterIssuer for automatic TLS certificate management with Let's Encrypt and Cloudflare DNS-01 challenge.

+ 1 - 1
library/kubernetes/certmanager-issuer/template.yaml

@@ -4,7 +4,7 @@ schema: "1.0"
 metadata:
   icon:
     provider: selfh
-    id: certmanager-issuer
+    id: lets-encrypt
   name: Cert-Manager Issuer (Cloudflare)
   description: >
     Cert-manager Issuer for automatic TLS certificate management with Let's Encrypt and Cloudflare DNS-01 challenge.

+ 1 - 1
library/kubernetes/traefik-ingressroute/template.yaml

@@ -4,7 +4,7 @@ schema: "1.0"
 metadata:
   icon:
     provider: selfh
-    id: traefik-ingressroute
+    id: traefik
   name: Traefik IngressRoute
   description: >
     Traefik IngressRoute CRD for HTTP/HTTPS routing with advanced features.

+ 1 - 1
library/kubernetes/traefik-ingressroutetcp/template.yaml

@@ -4,7 +4,7 @@ schema: "1.0"
 metadata:
   icon:
     provider: selfh
-    id: traefik-ingressroutetcp
+    id: traefik
   name: Traefik IngressRouteTCP
   description: >
     Traefik IngressRouteTCP CRD for TCP routing (non-HTTP protocols).

+ 2 - 2
library/kubernetes/traefik-middleware/template.yaml

@@ -16,8 +16,8 @@ metadata:
   date: "2025-01-11"
   tags: []
   icon:
-    provider: simpleicons
-    id: traefikproxy
+    provider: selfh
+    id: traefik
   draft: false
   next_steps: ""
 schema: "1.0"

+ 1 - 1
library/terraform/netbox-cluster-type/template.yaml

@@ -4,7 +4,7 @@ schema: "1.0"
 metadata:
   icon:
     provider: selfh
-    id: terraform
+    id: netbox
   name: NetBox Cluster Type
   description: >
     Create NetBox cluster type for categorizing virtualization clusters.

+ 1 - 1
library/terraform/netbox-cluster/template.yaml

@@ -4,7 +4,7 @@ schema: "1.0"
 metadata:
   icon:
     provider: selfh
-    id: terraform
+    id: netbox
   name: NetBox Cluster
   description: >
     Create NetBox virtualization cluster for organizing virtual machines.

+ 1 - 1
library/terraform/netbox-device-role/template.yaml

@@ -4,7 +4,7 @@ schema: "1.0"
 metadata:
   icon:
     provider: selfh
-    id: terraform
+    id: netbox
   name: NetBox Device Role
   description: >
     Create NetBox device role for categorizing physical devices.

+ 1 - 1
library/terraform/netbox-device-type/template.yaml

@@ -4,7 +4,7 @@ schema: "1.0"
 metadata:
   icon:
     provider: selfh
-    id: terraform
+    id: netbox
   name: NetBox Device Type
   description: >
     Create NetBox device type for hardware models.

+ 1 - 1
library/terraform/netbox-device/template.yaml

@@ -4,7 +4,7 @@ schema: "1.0"
 metadata:
   icon:
     provider: selfh
-    id: terraform
+    id: netbox
   name: NetBox Device
   description: >
     Register a physical device in NetBox with automatic role, type, and manufacturer creation.

+ 1 - 1
library/terraform/netbox-manufacturer/template.yaml

@@ -4,7 +4,7 @@ schema: "1.0"
 metadata:
   icon:
     provider: selfh
-    id: terraform
+    id: netbox
   name: NetBox Manufacturer
   description: >
     Create NetBox manufacturer for device hardware vendors.

+ 1 - 1
library/terraform/netbox-vm/template.yaml

@@ -4,7 +4,7 @@ schema: "1.0"
 metadata:
   icon:
     provider: selfh
-    id: terraform
+    id: netbox
   name: NetBox Virtual Machine
   description: >
     Register a virtual machine in NetBox with cluster and site association.