Skip to content

Core concepts

Five ideas cover everything deckctl does.

A source is a place templates live. There are two types:

TypeWhat it points atCached?
localA directory on your filesystemNo — read directly from disk
gitlabA group on gitlab.com or a self-hosted instanceYes

Sources are named, and every template command takes --source <name>. They are stored in ~/.config/deckctl/config.yaml and managed with deckctl source add/list/get/remove. Secrets are never written to that file — GitLab tokens are referenced by environment variable name via token_env.

See Configuring sources.

A template is a directory of files.

  • On a local source, every immediate subdirectory containing a deckctl.yaml is a template. Directories without one are ignored.
  • On a GitLab source, every non-archived project in the configured group is a template, and its files sit at the project root.

The sidecar is the file that turns a directory into a template. It declares the variables the template expects, the Jinja2 delimiters to use, and which filename suffixes mean “render me”.

deckctl reads two sidecar formats:

FileFormat
deckctl.yamlNative — see the sidecar reference
copier.ymlCopier — translated on the fly

Under the default --sidecar-format auto, a deckctl.yaml wins if both are present. See Copier compatibility.

Whether a file is rendered through Jinja2 or copied verbatim is decided by its filename suffix, never by sniffing its content:

Source fileResultOutput file
main.go.tplRendered, suffix strippedmain.go
values.yaml.j2Rendered, suffix strippedvalues.yaml
LICENSECopied byte-for-byteLICENSE
media/logo.pngCopied byte-for-bytemedia/logo.png

The default suffix list is .tpl, .tmpl, .jinja, .j2, overridable per template with template_suffixes.

This is deliberate: it is deterministic, it is binary-safe, and it means a Helm chart’s literal {{ .Values.x }} survives untouched as long as you don’t add a suffix to the file. When you do need to render a file that already contains {{ }}, change the delimiters instead — see Custom delimiters.

Variables are declared in the sidecar with an optional type, default, description and (for choice) a list of allowed values. At apply time each one resolves in this order:

  1. --var key=value on the command line — always wins
  2. The default from the sidecar
  3. An interactive prompt

deckctl template variables <name> prints the whole declared surface without applying anything. See Variables and prompting.

Templates fetched from GitLab are extracted into ~/.cache/deckctl/, keyed by <source>/<template>/<ref>. On the next apply deckctl reuses the cached copy if its last freshness check is within the TTL (1 hour by default); otherwise it sends a conditional request with the stored ETag, and only downloads bytes if the server says something actually changed.

Local sources never cache — there is nothing to fetch.

See Caching.

Every successful template apply writes a record — timestamp, template, source, output path and the resolved variables — into a bbolt database at ~/.config/deckctl/history.db. Read it back with deckctl history list, wipe it with deckctl history clear.

Dry runs are not recorded.

deckctl template apply go-service ./out --source work --tag v1.2.3
├─ resolve source "work" from config.yaml
├─ GitLab source → list tags, pick v1.2.3
├─ cache hit for work/go-service/v1.2.3 within TTL? → reuse
│ └─ else conditional GET (ETag) → 304 reuse, or 200 re-extract
├─ detect sidecar format (deckctl.yaml → native, copier.yml → copier)
├─ resolve variables: --var > default > prompt
├─ walk the tree: suffix → render through gonja, no suffix → copy
└─ write history record