Skip to content

Caching

When template apply fetches a template from a GitLab source, deckctl extracts it to a local cache so the next apply doesn’t re-download the archive. Local sources never cache — there is nothing to fetch.

The cache is per-user, lives on the filesystem, and is safe to delete at any time.

Resolved in this order, first match wins:

  1. --cache-dir <path>
  2. DECKCTL_CACHE_DIR
  3. $XDG_CACHE_HOME/deckctl/
  4. ~/.cache/deckctl/
Terminal window
deckctl template apply my-template ./out --source work --cache-dir /tmp/deckctl-cache
DECKCTL_CACHE_DIR=/srv/shared-cache deckctl template list --source work

Entries are keyed by source name, template name and ref:

~/.cache/deckctl/
.staging/ # temporary staging for atomic writes
work/ # source name from config.yaml
go-service/ # template name (the GitLab project slug)
v1.2.3/ # ref — a tag or a branch name
main.go.tmpl
deckctl.yaml
meta.json # cache metadata, not part of the template
main/
...

Because the ref is part of the key, v1.2.3 and v1.3.0 of the same template coexist rather than evicting each other.

meta.json records when the entry was fetched, when it was last verified, the server’s ETag, and the source URL. deckctl manages it; don’t hand-edit it.

Each entry tracks two timestamps:

  • fetched_at — when bytes were last actually downloaded
  • last_checked — when the entry was last verified against the server (freshly downloaded, or confirmed unchanged by a 304)

On every apply:

  1. Is there an entry for this source/template/ref?
  2. Is last_checked within the TTL? (Default: 1 hour.)

Both yes → the cached files are used with no network call for the archive.

Otherwise deckctl sends a conditional request carrying the stored ETag (If-None-Match):

Server respondsResult
304 Not ModifiedCached bytes are still current; last_checked bumped to now
200 OKNew content; the entry is atomically replaced along with the new ETag

A full archive download only happens when the upstream template genuinely changed.

sources:
- name: work
type: gitlab
url: https://gitlab.com
group: platform/templates
cache_ttl: 24h # check upstream at most once a day

cache_ttl uses Go duration syntax: 30m, 2h, 1h30m, 90s. Omitted means 1h.

A negative cache_ttl is rejected at config load, and setting cache_ttl on a local source is rejected too.

--no-cache forces a fresh unconditional fetch for a single apply:

Terminal window
deckctl template apply my-template ./out --source work --no-cache

The result is still written to the cache, so the next apply is fast again. Use it when you suspect the cache is stale — for example after an upstream force-push that didn’t move the ETag.

Terminal window
deckctl cache list
┌────────┬────────────┬────────┬──────────┬───────┐
│ Source │ Template │ SHA │ Size │ Age │
├────────┼────────────┼────────┼──────────┼───────┤
│ work │ go-service │ v1.2.3 │ 2.1 MB │ 5m12s │
│ work │ helm-chart │ main │ 880.5 KB │ 1h32m │
└────────┴────────────┴────────┴──────────┴───────┘
  • SHA — the ref: a tag or branch name. (The column name is historical; it is not necessarily a commit SHA.)
  • Age — time since last_checked, i.e. how long since deckctl last verified this entry.

-o json emits the full metadata including etag, fetched_at, last_checked, source_url and the on-disk path.

Terminal window
# Everything, with a confirmation prompt
deckctl cache clear
# Narrow it down — filters compose with AND
deckctl cache clear --source work
deckctl cache clear --template go-service
deckctl cache clear --older-than 1w
# Combine, and skip the prompt
deckctl cache clear --source work --older-than 30d --auto-approve

--older-than accepts a single number plus one unit from s, m, h, d, w, and decimals are fine (0.5h). It is compared against last_checked.

Deleting the cache directory with rm -rf is equally safe — deckctl rebuilds what it needs on the next apply.

deckctl assumes a single-user CLI workflow. Two simultaneous applies against the same cache directory are not synchronised: the atomic-rename write prevents partial-write corruption, but one of the two may re-fetch instead of reusing the in-flight write. For normal use this is invisible.

SymptomFix
Template seems stuck on an old version--no-cache once, or deckctl cache clear --source X --template Y
Cache directory growingdeckctl cache list to see sizes; deckctl cache clear --older-than 30d to prune
Changed cache_ttl but still getting the old versionTTL is measured from last_checked, not fetched_at. Force with --no-cache
cache_ttl: 1d errors on loadUse 24h — Go duration syntax has no day unit