Every feature, what it's for, and how it looks in kustron-env.yaml. Scroll to the bottom for a full worked example that uses them all at once.
Deploy from local source (uses your Dockerfile, or Railpack if there isn't one), pull any container image, or install a Helm chart. Mix all three in one environment.
apps:
- name: api
source: ./services/api # Dockerfile, or railpack fallback
port: 3000
- name: postgres
image: postgres:15
port: 5432
healthcheck: tcp
- name: prometheus
helm:
chart: kube-prometheus-stack
repo: https://prometheus-community.github.io/helm-charts
version: "45.0.0"
Images are tagged with a content hash of the source, not a timestamp. Run env up twice with no changes and the second run is a fast no-op: same tag, build skipped, nothing redeployed.
$ kustron env up
[api] source hash 9f3ab2c41e07
[api] image 9f3ab2c41e07 already deployed — skipping build
[api] Rollout complete
Done.
Point an HTTP probe at a TCP-only service like Redis or DynamoDB and rollout stalls forever. So healthchecks are opt-in: tcp for socket probes, a path for HTTP, or nothing for no probes at all.
apps:
- name: my-api
image: ghcr.io/me/api:1.0
port: 80
healthcheck: /health # HTTP GET
- name: kivo # redis-compatible cache
image: ghcr.io/itsmunim/kivo:v1.5.0
port: 6379
healthcheck: tcp # TCP socket probe
Build an image from source, then deploy it anywhere: the built-in template, a raw Kustomize/manifests directory, or a Helm chart of your own via helm.imageValues.
apps:
- name: backend
source: ./
helm:
chart: ./charts/backend # your own chart
imageValues: # inject built image
repository: image.repository
tag: image.tag
Declare what must be ready before an app deploys, waiting on rollout, a CRD established, or a custom command. Push runs before deploy, and post hooks after — great for seeding a DB or applying CRDs.
apps:
- name: api
image: ghcr.io/me/api:1.0
port: 80
dependsOn: [postgres]
wait:
type: rollout
- name: postgres
image: postgres:15
port: 5432
healthcheck: tcp
hooks:
post:
- "kubectl exec deploy/postgres -- psql -c 'CREATE TABLE IF NOT EXISTS items(id text);'"
First-class overrides for the container command, args, and resource limits. When you need more, patch deep-merges arbitrary keys onto the generated Deployment — no more "one missing field means leaving Kustron".
apps:
- name: worker
image: alpine:3
port: 80
command: ["sleep"]
args: ["1000"]
resources:
requests:
cpu: 250m
memory: 256Mi
patch:
spec:
template:
metadata:
annotations:
team: platform
Nested values pass through a real --values file (not flattened --set strings). Add extra local values files, or point at a local chart directory or an OCI registry chart.
apps:
- name: grafana
helm:
chart: ./charts/grafana # or oci://ghcr.io/org/chart
values:
grafana:
enabled: true # nested YAML works
alertmanager:
enabled: false
valuesFiles:
- ./env-overrides.yaml
Login to any container registry, have Kustron create an imagePullSecret per app and attach it to the Deployment, or import images directly into the cluster when push isn't an option.
apps:
- name: private
image: ghcr.io/me/private:1.0
port: 80
registry:
server: ghcr.io
username: itsmunim
password: ${GH_TOKEN}
Deploy the same app twice with different parameters using instance, or isolate apps into their own namespaces. Great for testing multi-tenant setups locally.
apps:
- name: kafka
instance: primary # deploys as kafka-primary
namespace: region-a
image: bitnami/kafka:3
port: 9092
- name: kafka
instance: secondary # deploys as kafka-secondary
namespace: region-b
image: bitnami/kafka:3
port: 9092
Bundle apps into a component with a Kustronfile — inputs, outputs, and app entries. Reference it from any environment and wire its outputs into your apps with ${component.output}.
components:
- name: hub-cache
source: ./components/cache # or a git URL
inputs:
replicas: 3
apps:
- name: api
source: ./
port: 3000
env:
CACHE_ENDPOINT: ${hub-cache.endpoint}
One environment that uses all of it: a source app with env interpolation, a welded dependsOn chain, a TCP-healthchecked cache, a component, an escape-hatched job, and a helm app.
config:
namespace: dev
components:
- name: hub-cache # reusable redis-compatible cache
source: ./components/cache
inputs:
replicas: 3
apps:
# API: builds from source, waits for cache + postgres, interpolates endpoints
- name: api
source: ./services/api
port: 3000
exposed: true
dependsOn: [hub-cache-cache, postgres]
env:
CACHE_ENDPOINT: ${hub-cache.endpoint}
DB_ENDPOINT: ${postgres.endpoint}
DB_PASSWORD: ${POSTGRES_PASSWORD:-secret}
# Cache from the component, healthchecked over TCP
# (apps under `components` are prefixed: hub-cache-cache)
# Postgres: TCP healthcheck + post-hook to seed a table
- name: postgres
image: postgres:15
port: 5432
healthcheck: tcp
env:
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
hooks:
post:
- "kubectl exec deploy/postgres -- psql -U postgres -d app -c 'CREATE TABLE IF NOT EXISTS items(id text);'"
# Escape-hatched worker: no Dockerfile image, args overridden, patched
- name: worker
image: alpine:3
port: 80
command: ["sleep"]
args: ["9999"]
patch:
spec:
template:
metadata:
labels:
role: worker
# Helm app with nested values + a local chart
- name: nginx-ingress
helm:
chart: ./charts/ingress
values:
controller:
replicaCount: 2
metrics:
enabled: true
# One command brings it all up:
# kustron env up
#
# One command tears it all down:
# kustron env down