Each folder here is a settings folder: build one with settings-image build examples/<name>.
task test-mount pushes all of them, mounts them in a pod as image volumes, and checks every file.
A settings folder has two folders (format-v2.md):
resources/: Kubernetes resources. Read and checked: aSecretmust be SOPS-encrypted, and aConfigMap's keys also become plain files underconfigmaps/<name>/.raw/: anything else, byte for byte. Never read.
| Folder | Shows | In the image |
|---|---|---|
| configmap/ | three ConfigMaps in one file: one with binaryData, one annotated to stay a resource only |
resources/app.yaml, and each key as a plain file: configmaps/shop-settings/settings.yaml, configmaps/branding/logo.png (decoded). No configmaps/feature-flags/ |
| custom-resources/ | custom resources in YAML and JSON, a nested folder, a TOML file in raw/ |
everything byte for byte, at the same paths. No configmaps/: there is no ConfigMap |
| pictures/ | product pictures (PNG) and a user's avatar (SVG) in raw/, referred to by path from custom resources |
every file byte for byte: raw/products/vanilla.png |
| gitops-reverser/ | a folder gitops-reverser writes into: its .gittargetignore marker and a README.md beside resources/ |
resources/menu.yaml and configmaps/coffee-menu/menu.txt. The two top-level files are skipped and named in the output |
| sops-secret/ | a SOPS-encrypted Secret, next to a plain ConfigMap |
the Secret under resources/, still encrypted; the ConfigMap's keys under configmaps/database/ |
Anything in raw/ goes into the image as it is, so a picture needs nothing special: commit it, and
refer to it by its path in the image (raw/products/vanilla.png) from the settings. That beats
binaryData in a ConfigMap (as in configmap/) for anything larger than a logo: no
base64, and no 1 MiB object limit. The whole folder is limited to 10 MiB.
The same goes for an application's own settings file: raw/settings.yaml is never parsed, so it
may be a list, a scalar, or anything the application reads.
The build does not validate against a schema. A file under resources/ must parse as a stream of
YAML (or JSON) resources, each with apiVersion, kind and metadata.name, and no resource may
appear twice. Everything else is carried byte for byte, and the application decodes the kinds it
knows.
Every ConfigMap's keys become files by default. To ship one as a resource only:
metadata:
name: feature-flags
annotations:
settings-image.configbutler.ai/files: "false"A Secret must be encrypted with SOPS, and every value under data and
stringData must be ciphertext, whatever the SOPS rule says. Its apiVersion, kind and
metadata must stay readable, so encrypt the payload only, as .sops.yaml here does
with encrypted_regex: ^(data|stringData)$. The rules are in
format-v2.md §4.
cd examples
sops encrypt --in-place sops-secret/resources/database.secret.yaml # uses .sops.yaml here.sops.yaml sits beside the folders, not in one, so it is not packed. Its recipient is
testdata/sops/age-test-key.txt, a test key committed on purpose, so you can decrypt what a
pod mounted:
SOPS_AGE_KEY_FILE=testdata/sops/age-test-key.txt sops decrypt examples/sops-secret/resources/database.secret.yamlThe image never holds a key, and the build never decrypts anything. Decrypting is the reader's job, with its own key.