Writing templates

The version 2 blueprint format, with metadata, inputs, generated secrets and volumes, and how to check a template before you use it.

Updated 4 November 2026

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

dekka.yaml
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_URL is a web service's public address with https://, which suits settings such as a webhook URL.
  • A connection to PostgreSQL also gives the service PGHOST, PGPORT, PGDATABASE, PGUSER and PGPASSWORD, for software that wants the parts of DATABASE_URL separately.
  • A connection to Redis also gives REDIS_HOST, REDIS_PORT and REDIS_PASSWORD next to REDIS_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)"