Skip to content
NebulaCtrldocs

Template file

Every field of a template YAML file, its expressions, limits and the problems nebula template validate reports.

A template is one YAML document with schemaVersion: 1. It names a set of services (images, public Git repositories or database engines), their processes, volumes and variables, and the inputs a person answers at install time. Unknown fields are rejected at every level. To install or share one, follow Install and share templates.

Example

The example installs n8n with its own Postgres database, an install-time hostname and exposure, and a generated encryption key.

template.yaml
schemaVersion: 1
name: n8n
description: Workflow automation with a visual editor and 400+ integrations.
category: automation
tags: [workflows]
links:
  website: https://n8n.io
  docs: https://docs.n8n.io
  source: https://github.com/n8n-io/n8n
inputs:
  - key: HOSTNAME
    label: Hostname
    description: Where n8n is served, for example n8n.example.com.
    required: true
  - key: EXPOSURE
    label: Exposure
    default: public
    options: [public, tailnet]
services:
  - name: n8n
    image: docker.n8n.io/n8nio/n8n:1.80.0
    processes:
      - name: web
        kind: http
        port: 5678
        replicas: 1
        memory: {request: 256Mi, limit: 1Gi}
        volumes:
          - name: data
            mountPath: /home/node/.n8n
            size: 1Gi
        domain:
          hostname: ${{ inputs.HOSTNAME }}
          exposure: ${{ inputs.EXPOSURE }}
    variables:
      N8N_ENCRYPTION_KEY: ${{ secret(32) }}
      DB_POSTGRESDB_HOST: ${{ host(db) }}
      DB_POSTGRESDB_PASSWORD: ${{ db.POSTGRES_PASSWORD }}
      WEBHOOK_URL: https://${{ inputs.HOSTNAME }}/
  - name: db
    database: {engine: postgres}

The database's password never appears in the file. ${{ db.POSTGRES_PASSWORD }} stays a reference and resolves from the database's generated password when a release is frozen, and ${{ host(db) }} becomes svc-db.

Validate a file

nebula template validate checks one or more files without a database or a running control plane. It reads the files only.

nebula template validate templates/n8n.yaml

A problem prints to standard output as <file>:<line>: <path>: <message>. The <line>: part is omitted when the problem is not tied to a line. A file that has no problems prints a summary:

templates/n8n.yaml: ok (n8n, 2 services)

The command exits with status 1 when any file has a problem. It also requires the file name, without .yaml or .yml, to equal the template's slug, and reports a slug problem when it does not. That check runs only on a file that otherwise parses. A file in the community repository must pass it.

The community repository runs the same check with the published image:

docker run --rm -v "$PWD:/w:ro" ghcr.io/nebulactrl/nebula:latest template validate /w/templates/*.yaml

Sections

SectionDescribes
Template fieldsName, category, links and the top-level lists
InputsQuestions asked at install time
ServicesThe source of each service and its variables
ProcessesWhat each service runs
VolumesPersistent storage of a process
DomainThe hostname of an http process
Expressions${{ ... }} in variables and domains

Template fields

schemaVersion

Type integer · Required · Constraints must be 1

The only schema version.

name

Type string · Required · Constraints 1 to 60 bytes after trimming

The display name.

slug

Type string · Default the name in lowercase, with each run of characters other than a-z and 0-9 replaced by one -, trimmed, cut to 40 characters · Constraints ^[a-z][a-z0-9-]{0,39}$

The template's identifier. A name that starts with a digit or has no ASCII letters or digits derives an invalid slug, so set slug for it.

description

Type string · Required · Constraints 1 to 280 bytes

category

Type string · Required · Constraints one of analytics, automation, cms, communication, database, development, media, monitoring, productivity, security, storage, other

tags

Type array of strings · Default none · Constraints at most 8; each matches ^[a-z0-9][a-z0-9-]{0,23}$

Type table of website, docs and source · Default none · Constraints each an absolute https:// URL

readme

Type string of Markdown · Default none · Constraints at most 16 KiB

Shown on the template's page in the console. The console renders headings, paragraphs, ordered and unordered lists, inline code, bold text and links that start with https://. Nothing else becomes markup.

inputs

Type array · Default none · Constraints at most 20 entries

See Inputs.

services

Type array · Required · Constraints 1 to 20 entries

See Services.

A template cannot set file mounts, a release command, a deploy-after list, a storage class or process metrics. Exporting a project leaves them out, and warns about file mounts, a release command and a deploy-after list.

Inputs

Each entry of inputs is one question. The install form shows them in order.

key

Type string · Required · Constraints ^[A-Z][A-Z0-9_]{0,63}$; unique among inputs

The name a variable or domain uses to read the answer: ${{ inputs.KEY }}.

label

Type string · Required · Constraints 1 to 60 bytes after trimming

description

Type string · Default none · Constraints at most 280 bytes

default

Type string · Default "" · Constraints one of options when options is set

The answer used when the person gives none.

required

Type boolean · Default false

A required input whose answer is empty is a problem at install time. An empty answer to a required input is a problem even when the input has a default, because an explicit answer replaces the default.

secret

Type boolean · Default false

The install form masks the field, and every variable built from the input is stored as a secret.

options

Type array of strings · Default none

The answer must be one of these. The form shows a segmented control for up to four options and a select for more.

Services

Each entry of services names its source with exactly one of image, git or database.

name

Type string · Required · Constraints ^[a-z][a-z0-9]*(-[a-z0-9]+)*$, at most 30 characters; unique in the template; inputs is reserved

The service's name in the template. Its slug in the project is derived from it, and a person can rename it at install time.

image

Type string · Constraints an explicit registry host, a repository and a tag or digest

An image reference such as docker.io/library/nginx:1.27. nginx:1.27 is refused because it has no registry host, and latest is never implied. An explicit :latest is accepted.

git

Type table of url, dockerfile and context

A service built from a public repository. The first deploy builds the environment's tracked branch, and the template cannot name a branch.

  • git.url: string, required, https:// only, with no embedded user name or password.
  • git.dockerfile: string, default Dockerfile.
  • git.context: string, default ..

database

Type table of engine and version

A database service.

  • database.engine: one of postgres, valkey, mysql, mongodb.
  • database.version: a major version the control plane runs, defaulting to the newest. The accepted majors are Postgres 18 and 16, Valkey 9 and 7, MySQL 8, and MongoDB 7.

processes

Type array · Default one http process named web on port 8080 with 1 replica, 100m to 500m CPU and 128Mi to 512Mi memory · Constraints at most 10; image and git services only

See Processes. A database service declares no processes.

variables

Type table of string to string · Default none · Constraints keys match ^[A-Z_][A-Z0-9_]*$; at most 100; image and git services only

The service's variables. Values may contain expressions. A scalar such as 8080 is read as the text "8080".

Processes

Each entry of a service's processes is one process.

name

Type string · Required · Constraints ^[a-z][a-z0-9-]{0,29}$; unique within the service

kind

Type string · Required · Constraints http, worker, cron or job

A proxy process cannot be templated.

port

Type integer · Constraints 1 to 65535; required for http, optional for worker, omitted for cron and job

command

Type array of strings · Default the image's ENTRYPOINT and CMD

Replaces both the image's ENTRYPOINT and its CMD.

args

Type array of strings · Default the image's CMD

Replaces only the image's CMD.

replicas

Type integer · Default 1 for an http or worker process that sets none of cpu.request, cpu.limit and memory.request, otherwise 0 · Constraints a whole number that is not negative

Set replicas on every process that sets a resource.

schedule

Type string · Constraints required for cron; forbidden for every other kind

A five-field cron expression, evaluated in UTC. Descriptors such as @daily and TZ= prefixes are refused.

runAsUser

Type integer · Default a non-root user is enforced · Constraints at least 0

0 runs the process as root.

cpu and memory

Type table of request and limit · Default the control plane's defaults · Constraints Kubernetes quantities; either key may be left out

Quantities use Kubernetes notation, such as 100m or 256Mi. The validator does not compare request with limit. An install is refused when a request exceeds its limit.

startupPath, readinessPath and livenessPath

Type string · Default none · Constraints an absolute HTTP path; http only

volumes

Type array · Default none · Constraints at most 5 per process

See Volumes.

domain

Type table · Default none · Constraints http only

See Domain.

Volumes

Each entry of a process's volumes is one persistent volume.

name

Type string · Required · Constraints ^[a-z][a-z0-9-]{0,29}$; unique within the process

mountPath

Type string · Required · Constraints an absolute path

size

Type Kubernetes quantity · Required · Constraints greater than zero, at most 1Ti

Domain

A domain is rendered at install time, so both fields may hold ${{ inputs.KEY }}. No other expression is allowed in them.

hostname

Type string · Default empty · Constraints after rendering, empty or a bare hostname

An empty hostname creates no domain. A hostname has no scheme, port or path.

exposure

Type string · Default public · Constraints after rendering, public or tailnet

An empty rendered value means public.

Expressions

An expression is written ${{ EXPR }}, and spaces around EXPR are optional. Text may surround any number of expressions, as in https://${{ inputs.HOSTNAME }}/. Expressions are allowed in variable values and in domain.hostname and domain.exposure. An unterminated ${{, an empty expression, a ${{ inside another and an expression the parser does not know are problems.

inputs.KEY

Resolved at install time · Constraints KEY names a declared input

The install answer, else the input's default, else an empty string.

secret() and secret(N)

Resolved at install time · Constraints N is 16 to 128; the default is 32

N random characters from A-Za-z0-9. Each occurrence draws its own value, and each environment draws its own.

host(NAME)

Resolved at install time · Constraints NAME names a service of the template

The in-cluster host name of that service, svc-<slug>, where <slug> is the service's final slug. A service may use host() of itself.

NAME.KEY

Resolved when a release is frozen · Constraints NAME names a service of the template other than the service itself; KEY matches ^[A-Z_][A-Z0-9_]*$

A reference that stays ${{ <slug>.KEY }} and resolves when a release is frozen. For a database service, KEY is one of the variables its engine generates. For an image or git service, KEY is one of that service's own variables.

EngineKeys
postgresDATABASE_URL, DATABASE_READ_URL, POSTGRES_DB, POSTGRES_PASSWORD, POSTGRES_USER
valkeyREDIS_URL, REDIS_PASSWORD
mysqlMYSQL_URL, MYSQL_DATABASE, MYSQL_ROOT_PASSWORD
mongodbMONGODB_URL, MONGO_INITDB_ROOT_USERNAME, MONGO_INITDB_ROOT_PASSWORD

Rules

  • Renaming. Renaming a service at install time changes its final slug. Every host(NAME) and NAME.KEY that names it follows the rename.
  • Empty inputs. A variable that uses an inputs.KEY whose answer is empty is not set at all, even when it also holds other text, so the application keeps its own default. With an optional HOSTNAME, BASE_URL: https://${{ inputs.HOSTNAME }}/ is set when a hostname is given and left out when it is not.
  • Secrets. A variable is stored as a secret when its value contains secret(, uses an input with secret: true, or uses a NAME.KEY that names a database service. Every other variable is stored in plain text.

A variable that a person types into a service understands secret(N), host(slug) and slug.KEY, but not inputs.KEY. See Variable expressions.

Limits

LimitValue
File size256 KiB
name, label60 bytes
description280 bytes
slug40 characters
tags8, each at most 24 characters
readme16 KiB
inputs20
Input key64 characters
services1 to 20
Service name30 characters
processes per service10
volumes per process5
variables per service100
Volume sizeat most 1Ti
secret(N)16 to 128, default 32

Problems

The parser collects every problem in one pass. A problem is {path, line, message}. path locates the field, such as services[1].processes[0].port. line is the 1-based source line, and 0 when the problem is not tied to one, as for an install answer. message says what is wrong and what is allowed.

A file that is not valid YAML, has a second document or is not a mapping at the top level stops at one problem with no path. The API reports problems as a 422 whose detail lists path (line N): message. The preview endpoint answers 200 with a problems list instead.

On this page