YAML mode

Manage Bahriya infrastructure declaratively with Reis YAML mode: file format, multi-document layout, create-or-update semantics, unknown fields, dry runs and exports.

Updated 8 Nov 202610 min read

Reis supports declarative infrastructure management through YAML files. Define your resources in YAML, commit them to version control, and apply them with reis apply -f for repeatable, auditable deployments.

This article is the orientation map — the file format, multi-document layout, and per-asset links. For the field-by-field reference of each asset type, jump straight to its dedicated page below.

Per-asset deployment guides

Each deployable asset has its own YAML walkthrough with the full set of fields that apply to it:

Projects, registries, and secrets are simpler and covered inline at the end of this article.

Prerequisites

Before applying resources, set an organisation context. Reis needs to know which organisation to apply resources to:

# Set your default organisation (interactive)
reis org:switch
 
# Or pass it explicitly on each apply
reis apply -f infrastructure.yml --org 550e8400-e29b-41d4-a716-446655440000

See Authentication for setting up credentials and your default organisation.

File format

Every YAML resource document carries a kind, an identity, and then the resource-specific fields. Fields are organised into named groups under top-level keys like workload, scaling, and networking:

kind: <resource-type>
project: <project-handle>    # required for project-scoped resources
handle: <resource-handle>
name: <display-name>
regions:
  - <region-id>
 
# resource-specific groups follow
workload:
  cpu: "500"

Supported kinds

KindPer-asset guide
container (type http)HTTP container
container (type worker)Worker
container (type cronjob)Cron job
memcachedMemcached
valkeyValkey
mysqlMySQL
tls_bundleTLS bundle
x509_certX.509 certificate
gpg_keypairGPG keypair
ssh_keypairSSH keypair
encryption_keyEncryption key
yaml_configYAML config
json_configJSON config
plain_configPlain config
env_fileEnv file
network_policyNetwork policy
pathruleHTTP container path rule
roleCustom role
billing_detailsBilling details
registryinline below
secretinline below
projectinline below

Multi-document files

Combine multiple resources in a single file using the YAML document separator ---:

kind: secret
handle: db-password
name: Database Password
value: s3cret-value-here
 
---
 
kind: registry
handle: ghcr
name: GitHub Registry
server: ghcr.io
username: myuser
password: ghp_xxxxxxxxxxxx
 
---
 
kind: container
project: my-project
handle: web-api
name: Web API
type: http
regions:
  - falkenstein-1
  - virginia-1
 
workload:
  image: ghcr.io/myorg/api:v1.0.0
  registry: ghcr
  cpu: "500"
  memory: "512"
  port: 8080
  healthcheck: /healthz
  env:
    NODE_ENV: production
  secrets:
    DB_PASSWORD: db-password
 
networking:
  external: true

Resources are applied in the order they appear. Define secrets and registries before the containers that reference them. Secrets and registries belong to the organisation rather than to a project: attach them to a project with reis project:attach before a container in that project uses them.

Container group structure

Every container — HTTP, worker, or cron job — uses the same group structure. Per-asset articles show which groups apply to which type.

GroupWhat it controls
(top level)name, handle, project, regions, type — identity and placement; init_jobs — init jobs that run before the main container starts
workloadImage and registry, CPU/memory, port, health check, env vars, secrets, ENTRYPOINT/CMD overrides
scalingManual replicas, autoscaling caps and targets
storageEphemeral storage size, persistent volumes
networkingHostnames, DNS, external networking
observabilityPrometheus scraping
securityBasic auth, rate limiting, IP allow/deny lists
proxy_cacheManaged proxy cache settings
scheduleCron expression, timezone, retry limits (cron jobs only)
vaultMounted TLS bundles, certificates, keypairs and encryption keys
configsEnv files and mounted YAML, JSON and plain-text configs

Every container document states its type: http, worker or cronjob. reis apply refuses a container document without one.

Reis 1.4.0 and 1.4.1 grouped init jobs under lifecycle (lifecycle.init_jobs). They are now top-level init_jobs, because Terraform reserves the name lifecycle. From Reis 1.6, lifecycle is an unknown field: move the list to init_jobs.

Project fields

kind: project
handle: my-project
name: My Project
regions:
  - falkenstein-1
  - virginia-1

A project is the namespace inside an organisation that owns containers, registries, secrets, vault items, configs, and Memcached instances. Members and roles are managed via the console or the API directly — they're not editable from YAML.

Attachments (which org-level registry, secret, TLS bundle, config, etc. are usable inside the project) are not managed from the project YAML. Use the dedicated project:attach, project:detach, and project:attachments commands instead — see Commands Reference: Projects.

Project quotas (read-only display)

When you reis export an existing project, you will also see a quotas: block showing the quotas applied to the project. These values are display-only: Reis ignores edits under quotas: when you re-apply the file, so an exported file applies without manual cleanup.

kind: project
handle: my-project
name: My Project
regions: [falkenstein-1, virginia-1]
quotas:                          # read-only — set by the platform
  request:
    cpu: 1000m
    memory: 1G
  limit:
    cpu: 2000m
    memory: 2G

Registry fields

kind: registry
handle: ghcr
name: GitHub Registry
server: ghcr.io
username: myuser
password: ghp_xxxxxxxxxxxx

Registries hold the credentials used to pull private images. Reference one from a container with workload.registry: <handle>.

Secret fields

kind: secret
handle: db-password
name: Database Password
value: s3cret-value-here

Secrets are encrypted at rest and injected into containers as environment variables — see the HTTP container guide for the workload.secrets mapping shape.

Create-or-update semantics

reis apply -f looks up each resource by its handle:

  • If the resource does not exist, it is created.
  • If the resource already exists, it is updated to match the YAML file. A field the file leaves out returns to its default, exactly as it would in Terraform. A field with no default, such as the image, keeps its current value.

That makes reis apply idempotent — running it multiple times with the same file produces the same result. Keep each file complete: state every field you rely on.

Applying a file that would clear or reset values

If an update would empty something that holds values today, such as environment variables, secrets, volumes, attached configs or an IP list, or would return a field you set back to its default because the file leaves it out, such as a port or an autoscaling setting, Reis lists those fields and asks before applying:

The file leaves out fields of container 'web-api' that hold values today. Applying it would change: workload.env, workload.port ('3000' → '8080')
Apply these changes? (yes/no) [no]:
  • In scripts and CI, pass --yes to confirm. Without it, a non-interactive run leaves the resource unchanged and exits with an error.
  • With --output json, the result for each resource lists the emptied fields as cleared and the fields returned to their default as reset, each with its current and default value.
  • To keep a value, state it in the file. A field the file states is never asked about, even when it is set to its default.

Projects, registries and other resources referenced by handle are resolved both when a resource is created and when it is updated.

Fields Reis does not know

A key that is not a field of the resource is usually a misspelling, and the field it was meant to set would quietly take its default. reis apply therefore refuses a document with an unknown field and names it:

`workload.bootstrap_tme` is not a field of container. Check the spelling: a misspelt field is ignored, and the field it meant takes its default. Fix or remove it, or pass --allow-unknown to ignore it.

Pass --allow-unknown to apply the document anyway: the unknown fields are ignored, with a warning for each. The keys under env and secrets are your own variable names, so any name is accepted there.

When a resource is not applied

Resources in a file are applied in order, and resources applied before a failure stay applied. When a resource fails, Reis says which resources were applied and which were not, and exits with an error. A failed lookup of an existing resource stops that resource rather than creating a second one.

Dry run

Check a YAML file without making any changes:

reis apply -f infrastructure.yml --dry-run

A dry run needs no login, organisation or network connection. It checks every document in the file, as a real apply does before it changes anything:

  • Errors, which a real apply refuses: unknown fields, a container document without type, and values that cannot be used.
  • Warnings, which a real apply reports and then applies: fields that a container type does not use or requires, and deprecated fields.
  • Notes, for information: fields that a new resource needs, such as workload.cpu and workload.memory for a container. A dry run cannot tell whether the resource already exists, and an update keeps the current values of fields the file leaves out.

The dry run exits with an error when it finds any error or warning, so it can gate a CI pipeline. Unknown fields ignored with --allow-unknown do not fail it. With --output json, it lists the findings for each document.

A dry run does not check what only the platform can: that referenced projects, registries and secrets exist, and that the resources fit your limits. Those are checked when you apply.

Exporting resources

Export an existing resource to YAML format, by its ID:

# Export to stdout
reis export container 0d9b3c2e-5f1a-4c8e-9a6b-2f7e1d4c8a10
 
# Export to file
reis export container 0d9b3c2e-5f1a-4c8e-9a6b-2f7e1d4c8a10 --file web-api.yml

Find a resource's ID with the matching list command, for example reis container:list. Reis can export projects, containers, Memcached, Valkey and MySQL instances, registries and secrets.

The exported YAML uses the same grouped format that reis apply expects, the same one the console's YAML view shows, so you can export, modify, and re-apply. Files exported by Reis versions before 1.4, which list every field under its API name at the top level, still apply.

Tips

  • Order matters in multi-document files. Define secrets and registries before the containers that reference them.
  • Use handles, not UUIDs. Handles are immutable in Bahriya and are the primary identifier in YAML files.
  • Environment variables can be specified as a YAML map (KEY: value). Reis converts them to the API format automatically.
  • Commit your YAML files to version control for an audit trail of infrastructure changes.
  • Use dry run to check files before applying them, especially in CI/CD pipelines.
  • Write numbers as numbers (port: 8080). Quoted numbers are accepted too, but exports write them unquoted.

YAML mode vs flag mode

You're doing…Reach for…
Managing a stack of related resources togetherYAML mode
Onboarding a new project under version controlYAML mode
Restoring a previously exported configYAML mode
A one-off deploy from your laptopflag mode
A scripted CI job that builds the same containerflag mode
Quick edits to a field or twoflag mode

Both modes produce identical results.