templates.md 4.6 KB

Template Authoring Notes

Templates are directory-based. Each template directory contains template.json plus a required files/ directory with renderable content.

Template loading and rendering flow

  1. LibraryManager finds template directories containing template.json.
  2. Template loads and validates manifest metadata.
  3. Manifest variables are normalized into the runtime variable collection shape.
  4. Defaults are merged from template, user config, var-files, and CLI overrides.
  5. Sections are sorted and dependency constraints are validated.
  6. Variables are resolved dynamically according to needs constraints.
  7. Files under files/ render through the custom-delimiter Jinja environment.
  8. Rendered output is sanitized.
  9. Optional semantic or kind-specific validation runs.

Template structure

Minimal template.json shape:

{
  "slug": "my-template",
  "kind": "compose",
  "metadata": {
    "name": "My Template",
    "description": "A template description.",
    "version": {
      "name": "v1.0.0"
    }
  },
  "variables": [
    {
      "name": "general",
      "title": "General",
      "items": [
        {
          "name": "service_name",
          "type": "str",
          "title": "Service name",
          "default": "demo"
        }
      ]
    }
  ]
}

Template delimiters

For 0.2.0+, templates use custom Jinja-like delimiters:

  • Variables: << variable_name >>
  • Blocks: <% if condition %> ... <% endif %>
  • Comments: <# comment #>

Default Jinja delimiters such as {{ variable }} are intentionally left untouched so downstream tools like Ansible can use them literally.

Legacy .j2 behavior is no longer supported for new template.json templates. Renderable content lives under files/, and output paths match the relative paths under files/.

Metadata versioning

metadata.version is a structured object and may be partial. Supported fields include:

  • name
  • source_dep_name
  • source_dep_version
  • source_dep_digest
  • upstream_ref
  • notes

This separates the user-facing template version label from tracked upstream dependency metadata.

Application versions

Application/image versions should generally be hardcoded in template files, for example:

image: nginx:1.25.3

Do not create variables such as nginx_version unless there is a strong technical reason, such as multi-component version pinning. Hardcoding tested versions avoids mismatches between variables and validated template output.

Variables

Variable precedence, lowest to highest:

  1. Template defaults from template.json
  2. User config defaults
  3. Var-file values
  4. CLI --var overrides

Variable types:

  • str
  • int
  • float
  • bool
  • email
  • url
  • hostname
  • enum
  • secret

Common variable properties:

  • default - default value
  • title - prompt/display title
  • description - description/help text
  • required - require non-empty value
  • needs - conditional visibility constraint
  • config.options - enum options
  • config.placeholder - input placeholder
  • config.autogenerated - secret autogeneration settings

Sections and dependencies

Variable groups become sections. Sections can define:

  • toggle - auto-created boolean variable that enables/disables the section
  • needs - section dependency by section key

Variable-level needs constraints use this format:

  • var_name=value
  • var_name!=value
  • var1=value1;var2=value2
  • network_mode=bridge,macvlan where comma means OR

Dependency validation detects missing dependencies, circular dependencies, and self-dependencies.

Validation

Template validation includes:

  • manifest shape validation
  • custom-delimiter Jinja syntax validation
  • undeclared variable detection
  • rendering validation

Generic semantic validation is provided by cli/core/validators.py, including YAML and Docker Compose-oriented checks where applicable.

Kind-specific validation lives under cli/core/validation/ and can be enabled with module validation options such as --kind or matrix validation where supported.

Docker Compose notes

When using Traefik with Docker Compose and multiple networks, include the Traefik network label so Traefik routes through the intended network:

traefik.docker.network: << traefik_network >>

For Swarm/Compose-specific label placement, follow existing template patterns before introducing new ones.

Shortcodes

Template descriptions may use emoji-style shortcodes such as :warning:, :info:, or :docker:. Display code maps these to Nerd Font icons. Add new shortcodes in the display/icon layer, not directly in template rendering code.