kubrain.yaml
kubrain.yaml lives in your repository and travels with every release. It
describes what to build and how to run it; the cloud renders Kubernetes objects
from it with a deterministic engine, so kubrain deploy validate locally matches
what the cloud produces. It comes in two mutually-exclusive shapes.
Flat (single component)
build:
dockerfile: Dockerfile # path relative to the context root; OR
# buildpack: auto # no Dockerfile → Cloud Native Buildpacks / Nixpacks
port: 8080 # container port
command: ["./server"] # optional; overrides the image entrypoint
replicas: 3
resources:
ram: 512Mi # billed dimension
health:
path: /healthz # probe path
# port: 8080 # defaults to the top-level port
env: # each entry is ONE of four forms (see Secrets & env)
LOG_LEVEL: info # 1. literal value
DB_PASSWORD: {secret: db-app, key: password} # 2. from a Secret key
TUNING: {configMap: app-config, key: level} # 3. from a ConfigMap key
REDIS_URL: {addon: redis, key: url} # 4. from a built-in addon's Secret
envFrom: # import every key of a Secret/ConfigMap as env vars
- secret: db-app # (add `prefix: FOO_` to namespace the keys)
- addon: redis
service:
enabled: true # default true when `port` is set
type: ClusterIP # ClusterIP | LoadBalancer
ingress:
enabled: true # default true when any host is set
host: app.example.com # shorthand for a single host
# hosts: [app.example.com, www.app.example.com] # multiple hosts on one cert
tls: true # one SAN cert covering all hosts, via cert-manager
deployment:
strategy: RollingUpdate # RollingUpdate | RecreateMulti-component
A deployment can be several workloads that ship as one release and roll/rollback together:
components:
web:
build: {dockerfile: Dockerfile}
port: 8080
replicas: 3
ingress: {enabled: true, host: app.example.com, tls: true}
env: {LOG_LEVEL: info}
worker:
build: {dockerfile: Dockerfile.worker}
command: ["./worker"]
service: {enabled: false} # a background worker needs no Service
redis:
image: redis:7-alpine # prebuilt dependency — NOT built from source
port: 6379
service: {enabled: true} # reachable as <deployment>-redis:6379Rules that matter
- Flat and
components:are mutually exclusive. - A component has either
build:(built from source) orimage:(prebuilt) — never both. Prebuilt components skip the build. - Each built component’s image is named
<deployment>-<component>(e.g.myapp-web); a flat app builds one image named for the deployment. - Components find each other by the derived Service name
<deployment>-<component>on their port — inject those addresses viaenv.
Secrets & env
Each env: entry is exactly one of four forms (the validator rejects
combinations, e.g. a literal alongside a ref):
| Form | Syntax | Becomes |
|---|---|---|
| 1. Literal | NAME: value | value: — no lookup |
| 2. Secret key | NAME: {secret: s, key: k} | secretKeyRef |
| 3. ConfigMap key | NAME: {configMap: c, key: k} | configMapKeyRef |
| 4. Addon credential | NAME: {addon: redis, key: url} | a resolved secretKeyRef |
key: defaults to the env var’s own name when omitted. envFrom: mirrors forms
2–4 for whole-secret/ConfigMap import (each entry sets exactly one of secret:,
configMap:, or addon:, plus an optional prefix:). A namespace: on a
Secret ref makes Kubrain mirror that Secret into the app’s namespace at
rollout (re-synced each deploy, so rotation follows).
Values set via set_secret are referenced, never baked into
the image — so rollback and promotion keep working and pick up current values on
the next deploy.
Custom manifests
Kubrain renders from templates, resolved per kind:
- Built-in presets — Deployment (always), Service and Ingress (when enabled). Enough for most apps.
- Per-repo override — a file at
kubrain/k8s-templates/<kind>.yaml(deployment.yaml,service.yaml,ingress.yaml) replaces the built-in for that kind. Use for a StatefulSet, custom annotations, etc. - Extra resources — any other file in
kubrain/k8s-templates/(acronjob.yaml,hpa.yaml, a CRD) renders as an additional object applied with the release.
Any custom workload template must reference {{ .Image }} (the built digest
for the current component). Iterate with kubrain deploy render --dir <dir> to
see the exact objects before shipping.
Storage
Every cluster ships a default StorageClass kubrain. Declare a standard PVC
(accessModes: ["ReadWriteOnce"], storageClassName: kubrain); it binds when a
pod first uses it, and data survives restarts and reschedules. Design around the
RWO access mode — single-instance stateful workloads use replicas: 1 + one PVC
deployment.strategy: Recreate; multi-replica stateful workloads use a StatefulSet override withvolumeClaimTemplates. Volumes bill at €0.05/GiB-month (first 20 GiB free).
See also
- Quickstart — where
kubrain.yamlfits in the ship flow. - MCP tools —
deploy,set_secret,validate.