Secrets
A credential — an API token, a registry password, a service-account key — reaches a
task or service through secrets. The build file only names where the value comes
from; the value itself is read when the task or service starts, never written to the
build file, the cache or hammerkit's logs.
tasks:
publish:
image: node:22-alpine
secrets:
- from: env:NPM_TOKEN # host environment variable
env: NPM_TOKEN # → environment variable in the container
- from: file:~/.config/gcloud/key.json
path: /run/secrets/gcloud.json # → read-only file in the container
cmds:
- npm publishEach secret has a source and exactly one target:
| Field | Description |
|---|---|
from | env:NAME — a variable of the environment hammerkit runs in, or one given with --set; file:path — a host file, relative to the build file or ~/…. |
env | Inject the value as this environment variable. |
path | Mount the value as a read-only file at this path in the container. |
cache | true makes the value part of the task's cache key (see below). Default false. |
Services take the same secrets. A service's init
is a task and declares the secrets it needs itself.
services:
postgres:
image: postgres:17-alpine
secrets:
- from: env:DB_PASSWORD
env: POSTGRES_PASSWORD
healthcheck:
cmd: pg_isready -U postgresWhen the value is read
Planning a run never reads a secret, so ls, validate and runs of other tasks work
without it. The value is read right before the container starts; when its source is
missing the task fails before anything runs, naming the secret:
secret NPM_TOKEN: environment variable NPM_TOKEN is not set.
Logs
Hammerkit masks every secret value it has read as *** in all it logs, including
the output it streams from tasks and services. Each line of a multi-line value (a key
file) is masked on its own. Values shorter than four characters are not masked —
masking them would mangle unrelated output. A secret given as an env target is
visible to anyone who can docker inspect the container, as with any environment
variable; a path target is not.
Caching
By default a secret does not affect the cache: rotating a token does not rebuild
everything that uses it. When a secret determines the output — a license key that
changes what gets built, say — set cache: true. The cache key then holds a salted
digest of the value, never the value itself, and a new value means a new key.
tasks:
build:
image: alpine:3.21
secrets:
- from: env:LICENSE_KEY
env: LICENSE_KEY
cache: true
cmds:
- ./build.shRuntimes
- Docker:
envtargets are set on the container. Afile:source is bind-mounted read-only. Anenv:source with apathtarget is written to an owner-only file in hammerkit's data directory, mounted read-only and removed when the container is. The file is readable by the host user only; an image whose process runs as another user (a database dropping to its own user, say) and needs to read it should get the secret as anenvtarget, or from afile:source whose permissions you set. - Kubernetes: the values go into a
Secretof the task or service, which the pod references (secretKeyRefforenvtargets, a read-onlysubPathmount forpathtargets). A task's Secret is deleted once it has run, a service's when it stops. - Local tasks take
envtargets only; apathtarget on a local task is an error.
A daemon service started with up keeps its secrets until down. A
changed secret declaration recreates it on the next up; a rotated value does not —
run hammerkit down first.
Container
A task can run inside a container. This improves cross-platform support for your build files and reduces the number of locally installed tools required to run your tasks.
Running on Kubernetes
Run your tasks and services on a Kubernetes cluster instead of the local docker daemon by selecting an environment.