Skip to Content
Addons and templates

Addons and templates

An addon is a backing service: PostgreSQL, Redis, Kafka, Elasticsearch, RabbitMQ. Anything your application depends on but that is not itself your application.

What separates an addon from a workload is what it produces. A workload serves users. An addon produces exports — connection strings, hostnames, credentials — that workloads consume. That is the whole reason the concept exists: without it, deploying a database is half the job and somebody still has to wire the consumer to it by hand.

In an app

The common case. A component with an addon: key is a backing service rather than a container, and it lives and dies with the app:

kubenest.yaml
components: api: image: myorg/my-api:v1.2.0 port: 8000 env: DATABASE_URL: ${postgres.dsn} postgres: addon: postgres@16 storage: 20Gi

kubenest deploy creates Postgres first because api reads a value from it, waits for the exports, then starts api with DATABASE_URL already resolved. Pausing the app does not pause the addon — databases keep running so data survives.

Standing on its own

When several apps share one database, or when its life should outlast any single app:

terminal
kubenest addon create shared-postgres --type postgres@16 --project acme-prod --storage 50Gi

Reference it from any app in the same project:

kubenest.yaml
env: DATABASE_URL: ${addon:shared-postgres.dsn}

The catalog

terminal
kubenest addon catalog
output
postgres 16, 15, 14 PostgreSQL redis 7, 6 Redis kafka 3 Apache Kafka

Each entry pins a chart, its default values, and the export keys it publishes. To run something the catalog does not carry, name a chart directly:

components: clickhouse: chart: oci://registry-1.docker.io/bitnamicharts/clickhouse version: 6.3.1 values: auth: username: analytics

Exports

What postgres publishes:

KeyExample
dsnpostgresql://myapp:…@shared-postgres.acme-prod.svc.cluster.local:5432/myapp?sslmode=require
hostshared-postgres.acme-prod.svc.cluster.local
port5432
user, databasemyapp

Conventional aliases come too, so a container expecting the standard PostgreSQL environment names needs no mapping: DATABASE_URL, PGHOST, PGPORT, PGUSER, PGDATABASE.

Each type has its own extractor and key set. Anything else falls back to service.url and service.port.

terminal
kubenest addon show shared-postgres
output
shared-postgres postgres@16 running 50Gi revision 3 exports dsn postgresql://myapp:••••@shared-postgres.acme-prod… host shared-postgres.acme-prod.svc.cluster.local port 5432 user myapp database myapp used by my-api/api DATABASE_URL my-api/worker DATABASE_URL reports/etl DATABASE_URL

Nothing ever prints a password. Secret-backed values are redacted in every response and every command. They are resolved on the cluster, at deploy time, into the consuming container — never through the control plane and never onto your terminal. kubenest addon password shared-postgres reads it from the cluster directly, when you genuinely need it.

Changing one

Every values change or version bump is a numbered revision, so there is an audit trail and a way back.

terminal
kubenest addon set shared-postgres memory=1Gi --note "production load" kubenest addon upgrade shared-postgres --to 17 kubenest addon revisions shared-postgres kubenest addon rollback shared-postgres --to 2

A rollback creates a new revision rather than deleting the ones in between.

Rolling an addon back to an older chart version is a Helm downgrade. Helm will do it, but many database charts run schema migrations on upgrade that do not reverse. Snapshot the data first — kubenest backup now — before rolling back anything stateful.

Deleting one

terminal
kubenest addon destroy shared-postgres

It refuses while an app still references it, naming what depends on it. The volume goes with it, so take a backup first if the data matters.

An addon that is a component of an app can be lifted out before you destroy the app, keeping the data:

terminal
kubenest addon detach my-api/postgres --as shared-postgres

Templates

A template is the shape of a working app with the varying parts pulled out: components, wiring and dependencies fixed, image tag and replica count and database name exposed as parameters.

Think of it the way you think of a Helm chart, one level up. A chart describes Kubernetes resources; a template describes a whole multi-component application.

Making one

From an app that already works — the natural way, because you have already debugged it:

terminal
kubenest template save my-api --as acme/api-service --version 1.0.0

Wiring and ordering come along. The CLI proposes parameters for the fields that usually vary and you edit the list.

From a Helm chart — wrap any public chart so it can be deployed with a few named inputs instead of a values file:

terminal
kubenest template from-chart oci://…/postgresql --version 13.4.4 --as acme/postgres

From a file — templates are portable, and this is what the registry ships:

terminal
kubenest template import ./api-service.yaml

Parameters

acme/api-service
parameters: image_tag: type: string required: true binds: components.api.image replicas: type: integer default: 2 binds: components.api.replicas db_password: type: string generate: password binds: components.postgres.values.auth.password

generate fills a parameter at deploy time when nobody supplies one — password, hex32, hex16, uuid. That is the pattern for secrets: nobody has to invent one, and nobody has to store one.

Deploying one

terminal
kubenest template deploy acme/api-service \ --project acme-staging \ --set image_tag=v1.2.0 \ --set replicas=3

The result is an ordinary app. Patch it, scale it, roll it back, save it as a new template.

The registry

Templates published by us and by other users:

terminal
kubenest template search postgres kubenest template deploy community/postgres-ha --project acme-prod

Deploying from the registry installs a local copy first, so a template you are running cannot change under you when its author publishes a new version.


Next: Day 2 · Architecture

Last updated on