dibbla.yaml schema
Every field of the deployment manifest in one place — services, jobs and support — with which fields vary per environment and which two parsers read the file.
A dibbla.yaml in your project root describes what to deploy: one or more
services, any scheduled jobs, and whether the app offers support tickets. It is
read by dibbla deploy, and you can see exactly what
it resolves to — before deploying anything — with dibbla preview.
It is not related to dibbla-task.yaml, which is a
setup pipeline that runs on your machine.
A single-container app deploys without a manifest at all. Write one when you have more than one service, want a cron job, need per-environment values, or want any of the fields below.
A complete file
version: 1 # required; 1 is the only accepted value
services: # required; at least one
web:
build: ./web # path, or a build object (see below)
port: 3000 # required when public: true
public: true # routes a public URL to this service
replicas: 2
cpu: 500m
memory: 512Mi
environment:
API_URL: http://api:8080
depends_on: [api]
auth:
require_login: true
access_policy: all_members
api:
build:
context: ./api
dockerfile: Dockerfile.prod
target: release
args:
VERSION: "1.4"
secrets:
- id: npm_token # BuildKit --mount=type=secret,id=npm_token
source: NPM_TOKEN # name in the dibbla secrets store
port: 8080
healthcheck:
readiness:
http_get:
path: /healthz
period_seconds: 10
db:
image: postgres:16
stateful: true # StatefulSet + headless Service
volumes:
- path: /var/lib/postgresql/data
size: 10Gi
routes:
- type: tcp
port: 5432
jobs: # optional; cron jobs
nightly-report:
schedule: "0 2 * * *"
build: ./jobs/report
command: ["node", "report.js"]
support: # optional; per-app end-user tickets
enabled: true
visibility: app
Top level
| Field | Type | Required | Notes |
|---|---|---|---|
version | int | yes | 1. Anything else is rejected as MANIFEST_UNSUPPORTED. |
services | map | yes | Service name → definition. Names become DNS labels inside the deployment. |
jobs | map | no | Job name → cron job. See Jobs. |
support | object | no | Per-app support tickets. See Support. |
Services
| Field | Type | Env-aware | Notes |
|---|---|---|---|
build | string | object | no | Path to a build context, or the object form shown above. One of build/image is required. The path is resolved inside the uploaded archive, which the platform has already filtered — a per-service context is carved out of what survived, not out of your working tree. A service whose Dockerfile copies a vendored or pre-built directory fails here for the same reason a single-service one does. |
image | string | yes | A pulled image; must include a tag. |
port | int | no | Container port. Required when public: true. |
public | bool | yes | Routes a public URL to this service. More than one public service is allowed — the lexically first keeps the bare app hostname, the others get {alias}-{service}. |
replicas | int | yes | Pod count, capped by your organisation’s quota. |
cpu | string | yes | Kubernetes CPU spec: 500m, 1, 2. |
memory | string | yes | Kubernetes memory spec: 256Mi, 1Gi. |
environment | map | yes | Plain environment variables — settings, not secrets; supports ${VAR} substitution. The file is in your source and its history, so a name or value that looks like a secret is a warning for a person and refused for an AI assistant’s deploy or a proposal it wrote, and a name that is one of the app’s secrets is refused. Secrets go in the secret store — see env vars and secrets. |
command | list | no | Overrides the image’s CMD. |
entrypoint | list | no | Overrides the image’s ENTRYPOINT. |
volumes | list | no | Per-service persistent volumes; see Volumes. |
stateful | bool | no | Renders a StatefulSet with a headless Service. Requires at least one volume. |
profiles | list | no | Only include this service when one of these profiles is active. |
depends_on | list | no | Ordering for builds and startup. |
expose_to | list | no | Restrict which other services may reach this one (NetworkPolicy). |
init | list | no | Init containers; image: only, no build:. |
healthcheck | object | no | liveness, readiness, startup; see Health checks. Container probes — not application checks, which live in dibbla-checks.yaml. |
domain | string | object | no | A custom domain for this service. See Custom domains. |
routes | list | no | Non-HTTP entry points; see Routes. |
auth | object | no | Per-service login gate; see Auth. |
mcp | string | no | Publishes the tool server this service runs as an MCP of its own, under this name. See MCP. |
Env-aware fields
A field marked env-aware takes either a single value or a per-environment map:
services:
web:
replicas:
prod: 3
dev: 1
public: true
The environment comes from --env at deploy time. An environment with no
entry falls back to the platform default, not to another environment’s value.
Auth
auth:
require_login: true # env-aware
access_policy: invite_only # env-aware: all_members | invite_only
google_scopes: [profile]
microsoft_scopes: []
Unset fields fall back to the deploy-level --require-login /
--access-policy flags. The email_domain policy is console-only — it needs a
domain list that neither the flag nor this block carries. See
SSO & access control.
MCP
services:
tools:
build: .
mcp: my-tools # https://mcp.<domain>/platform/servers/my-tools
auth:
require_login: true
access_policy: invite_only
environment:
SERVER_NAME: my-tools # the name the worker registers under
mcp: publishes the tool server a service runs — a Go worker built with the
Go SDK — as an MCP at /platform/servers/<name>. The
walkthrough is Publish an MCP; the rules are:
- The value is the tool server’s name, the one the worker registers under
(
SERVER_NAME, orWithServerNamein code). 1–64 characters of letters, digits,.,_and-; anything else is rejected asMANIFEST_INVALIDatservices.<name>.mcp. One service per name. - Whoever may deploy the app may publish. No organization admin is involved, and the worker needs no API token: a deployed service connects with the app’s workload identity.
- Who gets the tools follows the app’s access list —
auth.access_policyand the app’s Access tab in the console — never a per-function exposure. The address always requires a Dibbla sign-in, also for an app withrequire_login: false. See who gets in. - The whole server is published. Every function that can be a tool is one; agents and engine-internal functions are left out.
- The deploy never fails over this line and never stays silent about it. It reports the server as published, or says why it was not (a name another app owns, an invalid name, per-server addresses switched off on the installation, or a publish that should be retried). See Deploy and read the answer.
- Removing the line and deploying unpublishes. So does deleting the app. A service behind an inactive profile publishes nothing.
The field is not env-aware.
Volumes
volumes:
- path: /data
size: 10Gi
access: rwo # rwo (default) | rwx
Health checks
Each of liveness, readiness and startup takes one probe —
http_get (path, optional port), tcp_socket (port) or exec — plus
optional initial_delay_seconds, period_seconds, timeout_seconds and
failure_threshold.
These are the Kubernetes probes: they ask the container to prove its process
is up, and a failing probe restarts the pod. They are not application checks —
the scheduled, user-journey-shaped assertions a deployment ships in a separate
dibbla-checks.yaml, which run against the public route and
never restart anything. A pod can pass every probe and still serve error
pages; the two files answer different questions on purpose.
Routes
routes:
- type: tcp # tcp | https | http
port: 5432
tls: edge # edge | none (TCP always terminates at the proxy)
hostname: my-db # subdomain label; defaults to {alias}-{service}
Jobs
| Field | Type | Required | Notes |
|---|---|---|---|
schedule | string | yes | Standard cron expression, e.g. 0 2 * * *. |
build / image | string | object | one of | Same shapes as a service. |
command | list | no | Overrides the image’s CMD. |
environment | map | no | Environment variables for the job. |
cpu, memory | string | no | Resource requests. |
successful_jobs_history_limit | int | no | How many completed runs to keep. |
failed_jobs_history_limit | int | no | How many failed runs to keep. |
See Scheduled jobs for the walkthrough.
Support
support:
enabled: true
visibility: app # app (default) | own
Turns on support tickets for this app’s end users.
Support can also be switched on from the console without a manifest — an
explicit support: block here always wins while it is present; in its absence
the console setting governs and survives redeploys.
assignees and attachments are accepted and validated, and neither changes
what the app does. Assignment is done in the console.
attachments is deprecated: files on a
ticket are live, and they follow
support.enabled, so this key switches nothing on or off. It still parses —
a manifest that already carries it keeps deploying — and dibbla preview
reports it as having no effect. Remove it at your convenience.
Two parsers read this file
The CLI validates the shape it needs to build and upload; deploy-api
validates the whole schema and is the authority. A field the CLI passes
through can still be rejected on the server — and an older CLI ignores
keys it has never heard of rather than erroring, which is what lets a new
manifest field ship without breaking every installed binary.
The consequence worth knowing: dibbla preview asks the server, so it shows
you the real resolution. Trust it over what the local parser accepted.