Writing templates
The version 2 blueprint format, with metadata, inputs, generated secrets and volumes, and how to check a template before you use it.
Templates are dekka.yaml blueprints with version: 2, which adds a few keys to the version 1 format: metadata for how the template is shown, inputs for values the person deploying provides, expressions that generate secrets, and a volume on a service. Everything from version 1, including resource specs, shared variables, references and connections, works the same way, and you can use version 2 in your own dekka.yaml to plan an environment that needs inputs or volumes.
A complete example
version: 2
name: Notes
description: A notes app with Postgres, an admin token and a disk for uploads.
metadata:
category: frameworks
summary: A notes app with its database and uploads.
tags: [notes, postgres]
maintainer: community
notes: Sign in with the admin token, which you can reveal from the service's variables.
inputs:
- key: SMTP_PASSWORD
label: SMTP password
description: For sending sign-in emails.
secret: true
required: true
- key: REGION_LABEL
label: Region label
default: accra
variables:
# Shared, so it's generated once and kept on every later plan.
SESSION_KEY: ${{secret(32)}}
resources:
- name: notes
kind: application
spec:
source: { provider: image, image: "ghcr.io/acme/notes:1.4" }
runtime: { port: 8080, health_check_path: /health, memory_mib: 512 }
volume: { mount_path: /data, size_gb: 1 }
variables:
SESSION_KEY: ${{shared.SESSION_KEY}}
ADMIN_TOKEN: adm_${{secret_hex(16)}}
SMTP_PASSWORD: ${{input.SMTP_PASSWORD}}
REGION_LABEL: ${{input.REGION_LABEL}}
PUBLIC_URL: ${{notes.DEKKA_PUBLIC_URL}}
DB_HOST: ${{notes.PGHOST}}
- name: notes-db
kind: postgres
spec: { version: "17", storage_mib: 512 }
connections:
- { from: notes, to: notes-db }Metadata
metadata decides how the template appears in the library, and plain blueprints leave it out.
| Key | What it holds |
|---|---|
category |
Required. One of ai, automation, analytics, monitoring, databases, frameworks, search, storage or cms. |
summary |
Required. One line for the template's card, up to 140 characters. |
logo |
An icon name in lowercase letters, numbers and hyphens, such as n8n. |
tags |
Up to 8 words that help search find it. |
website, source_url |
Links to the project's site and its source code. |
license |
The upstream licence, such as MIT or AGPL-3.0. |
maintainer |
dekka or community, which is dekka unless you set it. |
docs_slug |
A docs page about the template. |
notes |
What to do after deploying, such as creating the first account, up to 600 characters. |
featured |
true to list the template first. |
Inputs
Each entry in inputs is a value asked for when the template is planned, and a variable uses it as ${{input.KEY}}. A template can declare up to 20.
| Key | What it does |
|---|---|
key |
Required. The name used in ${{input.KEY}}, in letters, numbers and underscores. |
label |
Required. The field's label in the dashboard. |
description |
A line under the field explaining what the value is for. |
secret |
true to mask the field and store every variable that uses it as a secret. |
required |
true to refuse a plan without it. |
default |
The value used when none is given. |
placeholder |
Example text shown in the empty field. |
A required input left empty answers 422 missing_input, whose details.inputs lists each key. When an optional input is left empty and a variable's value is nothing but that input, the variable isn't set at all.
Generated secrets
${{secret(N)}} becomes N random bytes written as URL-safe base64, and ${{secret_hex(N)}} becomes N random bytes as hexadecimal, so secret_hex(16) gives 32 characters. N is between 8 and 128. Expressions can sit inside a longer value, as in sk-${{secret(24)}}, and each one gets its own value.
Dekka replaces expressions when the blueprint is planned, and any variable that used a secret expression or a secret input is stored as a secret and never shown in the plan. A shared variable that already exists keeps its value on every later plan, so put a generated key that protects stored data, such as an encryption key, in variables at the top level and reference it with ${{shared.KEY}}. A service variable with a secret expression gets a new value each time you plan the blueprint again.
Volumes
volume on a service creates a disk with it, mounted at mount_path, with size_gb between 1 and 1024 that defaults to 1. A service has at most one volume and runs a single replica, and databases manage their own storage, so they can't have one. The disk is named after the service, such as notes-data, and works like any other volume.
Variables a template can reference
References such as ${{api.DEKKA_PRIVATE_DOMAIN}} and ${{shared.KEY}} stay in the stored value and resolve on every deploy, as described in Variables. Templates often use these as well.
DEKKA_PUBLIC_URLis a web service's public address withhttps://, which suits settings such as a webhook URL.- A connection to PostgreSQL also gives the service
PGHOST,PGPORT,PGDATABASE,PGUSERandPGPASSWORD, for software that wants the parts ofDATABASE_URLseparately. - A connection to Redis also gives
REDIS_HOST,REDIS_PORTandREDIS_PASSWORDnext toREDIS_URL.
A service can reference its own connection variables, as ${{notes.PGHOST}} does above, and the passwords stay secret.
Check a template
POST /v1/blueprints/validate checks a blueprint without an environment and lists every problem with its path.
curl https://api.dekka.cloud/v1/blueprints/validate \
-H "Authorization: Bearer $DEKKA_API_KEY" \
--json "$(jq -Rs '{ content: . }' dekka.yaml)"These are the errors specific to version 2.
| Path | Message |
|---|---|
inputs |
each input needs its own key |
resources.0.variables.KEY |
${{input.NAME}} names an input the template doesn't declare |
resources.0.variables.KEY |
${{secret(4)}} must generate between 8 and 128 bytes |
resources.1.volume |
only services have volumes; databases manage their own storage |
resources.0.volume |
a service with a volume runs one replica |
To try a template in an environment, plan it with its inputs and read the changes before deploying. Nothing is created until you deploy the changeset.
curl https://api.dekka.cloud/v1/environments/$ENVIRONMENT_ID/blueprint/plan \
-H "Authorization: Bearer $DEKKA_API_KEY" \
--json "$(jq -Rs '{ content: ., inputs: { SMTP_PASSWORD: "…" } }' dekka.yaml)"