Templates are the core unit of Boilerplates. A supported template is a directory with a template.json manifest and a files/ directory containing every renderable output file.
my-template/
├── template.json
└── files/
├── compose.yaml
├── .env
└── config/
└── app.yaml
Rules:
template.json is the only supported manifest formatfiles/template.yaml, template.yml, and top-level .j2 layouts are incompatible with the current runtimeslugkindmetadatavariablesExample:
{
"slug": "my-template",
"kind": "compose",
"metadata": {
"name": "My Template",
"description": "Short human description",
"author": "Your Name",
"date": "2026-04-22",
"tags": ["infra", "dev"],
"icon": {
"provider": "mdi",
"id": "docker",
"color": "blue"
},
"draft": false,
"version": {
"name": "v1.1",
"source_dep_name": "ghcr.io/example/my-image",
"source_dep_version": "1.1.0",
"source_dep_digest": "sha256:abc123def456",
"upstream_ref": "release-2026-04-22",
"notes": "Tracks the tested upstream dependency snapshot"
}
},
"variables": [
{
"name": "general",
"title": "General",
"items": [
{
"name": "service_name",
"type": "str",
"title": "Service name",
"default": "my-service"
}
]
}
]
}
slug and Template IDsslug is the canonical template ID exposed by the CLI.
Behavior:
slug is present, it wins over the directory nameslug ends with -<kind>, that suffix is normalized away for CLI useslug is missing, the directory name is usedExample:
portainer/kind: composeslug: portainer-composeportainerCommon metadata fields:
namedescriptionauthordatetagsicondraftversionmetadata.version is optional, but when present it must be an object.
Supported fields:
namesource_dep_namesource_dep_versionsource_dep_digestupstream_refnotesImportant behavior:
metadata.version.name is the user-facing version label shown in list/show outputAny variable used in files under files/ must be declared in template.json.
If a file references an undeclared variable, the template fails validation and load/render operations surface a template error.
Use the Variables page for the manifest structure.
Everything under files/ is part of the output tree.
Rendering behavior:
files/files/Templates use custom delimiters rather than default Jinja syntax:
<< value >><% if condition %><# comment #>Example:
services:
<< service_name >>:
image: nginx:1.27.0
<% if ports_enabled %>
ports:
- "<< http_port >>:80"
<% endif %>
Legacy {{ }}, {% %}, and {# #} delimiters are rejected.
Includes and imports resolve relative to the template's files/ directory.
<% include 'partials/header.yaml' %>
Templates are discovered from configured libraries.
A directory is considered a valid template only when:
template.json existsfiles/ existsIn practice, Boilerplates discovers templates by module directory path such as compose/<template>/ or terraform/<template>/.
Useful commands:
boilerplates compose list
boilerplates compose search nginx
boilerplates compose show nginx
Draft templates:
metadata.draft to true to hide the template from normal discoveryTypical workflow:
boilerplates compose show nginx
boilerplates compose generate nginx --output ./my-nginx
Useful flags:
--output for local output--remote and --remote-path for SSH upload targets--var-file for YAML overrides--var for direct CLI overrides--no-interactive for non-interactive generation--dry-run to preview generation without writing files--show-files to print rendered files during dry-runValidate one template:
boilerplates compose validate nginx
Validate all templates in a module:
boilerplates compose validate
Validation covers:
template.jsonfiles/metadata.version.name for the user-facing labelmetadata.version fields for upstream snapshot context when useful