harthelm.yml
Every key of harthelm.yml, the file that describes a landscape: types, required keys, defaults and allowed values.
schemas/harthelm.schema.json at v1.5.0A Harthelm landscape: the steps that prepare and test a workspace and the services that run its app (schema harthelm/v1). Profiles are complete files named harthelm.<profile>.yml. Keys starting with x- are ignored everywhere; any other unknown key is an error.
The schema's id is urn:harthelm:schema:harthelm-v1. Every installation serves it without credentials at GET /api/v1/schemas/harthelm.json, so editors can validate the file.
harthelm.yml lives in /data/app, at the root of the workspace's code. The console's editor checks it as you type;
hart validate checks it in a terminal or in CI. A file per profile,
harthelm.<profile>.yml, describes a variant of the same landscape.
A complete example
schema: harthelm/v1
prepare:
- name: Install dependencies
run: |
mise install node@22
npm ci
test:
- name: Unit tests
run: npm test
services:
web:
type: steps
steps:
- name: Serve
run: npm start --workspace web
env:
PORT: 3000
API_URL: https://${{ workspace.devDomain }}/api
ports:
- port: 3000
routes:
- path: /
needs: [api]
api:
type: steps
plan: small
replicas: 2
steps:
- name: Serve
run: npm start --workspace api
env:
PORT: 8080
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ services.db.dsn }}
ports:
- port: 8080
routes:
- path: /api
strip: true
health:
port: 8080
path: /health
db:
type: managed
provider: postgres
config: {postgresVersion: "18", database: app, user: app}
secrets:
password: ${{ secrets.DB_PASSWORD }}The dev URL https://<id>-3000.<workspace domain>/ reaches web; /api/health reaches api as /health, balanced
over two replicas; api connects to the PostgreSQL database db. The guides explain each part:
configure services, secrets,
add a database, path routing.
How to read this reference
Each table lists the keys of one object: its type, whether it is required, and what it does with its default and
allowed values. Keys starting with x- are ignored everywhere, so you can keep notes or YAML anchors under them; any
other unknown key is an error. Values of ${{ … }} templates are filled in at Sync.
A file that breaks a rule is refused with a code and the place of each problem: see harthelm.yml errors. The variables your services see without setting them are on Workspace environment.
Top level
| Key | Type | Required | Description |
|---|---|---|---|
schema | harthelm/v1 | yes | The schema version. Changes inside harthelm/v1 are additive. |
prepare | list of Step | Steps that set up the workspace (install dependencies, build). They run in sequence in the IDE pod in /data/app with the workspace env vars but no secrets; the first failing step fails the stage. | |
test | list of Step | Steps that test the workspace, run like prepare. | |
services | map of name to Service | The services of the landscape, by name. Sync creates, changes and removes them; Run starts their steps. |
Keys starting with x- are ignored.
Step
One shell step, run with bash -lc in /data/app.
| Key | Type | Required | Description |
|---|---|---|---|
name | string | A label for the Run panel and the logs. At least 1 character. At most 100 characters. | |
run | string | yes | The command; multi-line blocks are fine. At least 1 character. |
Keys starting with x- are ignored.
Service
The type key picks the kind of service; each kind has its own keys:
steps: steps servicecontainer: container servicemanaged: managed serviceforward: forward service
steps service
A service that runs steps in every replica: all but the last run once in sequence, the last one is the server and is restarted with backoff.
| Key | Type | Required | Description |
|---|---|---|---|
type | steps | yes | |
steps | list of Step | yes | At least 1 entry. |
image | string | A custom base image built FROM harthelm-base; the installation's service image when absent. At most 512 characters. Pattern ^[a-z0-9][A-Za-z0-9._:/@-]*[A-Za-z0-9]$. | |
nixWritable | boolean | Mount /nix read-write instead of read-only. Default false. | |
plan | string | A service plan id of the installation (GET /api/v1/plans, kind service); the installation's default service plan when absent. Pattern ^[a-z0-9][a-z0-9-]{0,62}$. | |
replicas | integer | How many pods run the service; at most maxReplicas. Default 1. 1 to 100. | |
maxReplicas | integer | The most replicas the service may be scaled to without a Sync; at most the plan's maximum, which is also the default. 1 to 100. | |
availability | always, on-demand | always keeps the service running; on-demand lets it sleep after idleTimeoutMinutes without requests and wakes it on the next one. The default comes from the plan. | |
idleTimeoutMinutes | integer | Minutes without requests before an on-demand service sleeps; the installation's default when absent. 1 to 1440. | |
env | map of name to string or number or boolean | Environment variables of the service. Values may use ${{ workspace.id }}, ${{ workspace.devDomain }}, ${{ team.id }}, ${{ env.K }}, ${{ secrets.K }} and, for a managed service of this file, ${{ services.<name>.host }}, .port, .dsn (with the password), .secret (the password alone) and .details.<field>; numbers and booleans become strings. Names match ^[A-Za-z_][A-Za-z0-9_]{0,127}$. Names must not match ^HART_. | |
ports | list of Port | The ports the service listens on. | |
health | Health check | How the agent checks from inside the pod that a replica is ready. | |
mounts | list of Mount | Files or directories of /data/app mounted at another path in the container. | |
needs | list of string | Services this one needs: waking it wakes them too, transitively and in parallel, and the router holds the request until all are ready; none of them sleeps while this one is awake; Run starts them first. Managed and forward services may be named (nothing to wake). Unknown names and cycles are errors. At most 64 entries. No duplicates. Pattern ^[a-z]([-a-z0-9]{0,30}[a-z0-9])?$. Not ide. |
Keys starting with x- are ignored.
container service
A service from your own OCI image; the agent runs its ENTRYPOINT and CMD under the workspace's random UID, group 0. Sync pins the image's digest; private images use the team's container registries.
| Key | Type | Required | Description |
|---|---|---|---|
type | container | yes | |
image | string | yes | An image reference; pin it by digest for reproducible Syncs. At most 512 characters. Pattern ^[a-z0-9][A-Za-z0-9._:/@-]*[A-Za-z0-9]$. |
command | list of string | Replaces the image's ENTRYPOINT. | |
args | list of string | Replaces the image's CMD. | |
runAsUser | integer | Only for teams a cluster admin allowed to run as root or a fixed user. 0 to 2147483647. | |
plan | string | A service plan id of the installation (GET /api/v1/plans, kind service); the installation's default service plan when absent. Pattern ^[a-z0-9][a-z0-9-]{0,62}$. | |
replicas | integer | How many pods run the service; at most maxReplicas. Default 1. 1 to 100. | |
maxReplicas | integer | The most replicas the service may be scaled to without a Sync; at most the plan's maximum, which is also the default. 1 to 100. | |
availability | always, on-demand | always keeps the service running; on-demand lets it sleep after idleTimeoutMinutes without requests and wakes it on the next one. The default comes from the plan. | |
idleTimeoutMinutes | integer | Minutes without requests before an on-demand service sleeps; the installation's default when absent. 1 to 1440. | |
env | map of name to string or number or boolean | Environment variables of the service. Values may use ${{ workspace.id }}, ${{ workspace.devDomain }}, ${{ team.id }}, ${{ env.K }}, ${{ secrets.K }} and, for a managed service of this file, ${{ services.<name>.host }}, .port, .dsn (with the password), .secret (the password alone) and .details.<field>; numbers and booleans become strings. Names match ^[A-Za-z_][A-Za-z0-9_]{0,127}$. Names must not match ^HART_. | |
ports | list of Port | The ports the service listens on. | |
health | Health check | How the agent checks from inside the pod that a replica is ready. | |
mounts | list of Mount | Files or directories of /data/app mounted at another path in the container. | |
needs | list of string | Services this one needs: waking it wakes them too, transitively and in parallel, and the router holds the request until all are ready; none of them sleeps while this one is awake; Run starts them first. Managed and forward services may be named (nothing to wake). Unknown names and cycles are errors. At most 64 entries. No duplicates. Pattern ^[a-z]([-a-z0-9]{0,30}[a-z0-9])?$. Not ide. |
Keys starting with x- are ignored.
managed service
A managed service bound to this landscape, e.g. a PostgreSQL database: Sync creates or changes it, Teardown pauses it, the next Sync resumes it, removing it from the file deletes it and its data. Other services reach it through ${{ services.<name>.host|port|dsn|details.<field> }} in their env.
| Key | Type | Required | Description |
|---|---|---|---|
type | managed | yes | |
provider | string | yes | Provider name from the installation's catalog (GET /api/v1/providers), e.g. postgres. Pattern ^[a-z][a-z0-9-]{0,62}$. |
providerVersion | string | The provider's version; the newest enabled one when absent. An instance keeps the version it was created with. At least 1 character. At most 64 characters. | |
plan | string | One of the provider's plans, e.g. tiny; the provider's default plan when absent. Pattern ^[a-z0-9][a-z0-9-]{0,62}$. | |
resources | map of name to integer or string | Overrides of the plan's resource values the provider lets you change, e.g. {storage: 2048} or {storage: 2Gi}; checked against the provider's bounds and update constraints. Sizes (the provider's memory and storage resources) are MiB as a number, or a size with a binary unit: 2048MiB, 2048Mi, 2Gi, 2GiB, 1.5Gi. Names match ^[A-Za-z_][A-Za-z0-9_]{0,63}$. | |
config | object | Provider settings, checked against the provider's schema incl. update constraints; strings may use templates except secrets and services. | |
secrets | map of name to string | Provider secrets, each exactly one ${{ secrets.K }} reference: the value lives in the workspace's vault, never in this file. Sync asks for missing ones. Names match ^[A-Za-z_][A-Za-z0-9_]{0,127}$. | |
backups | boolean | Dump it nightly (providers that can); default true. |
Keys starting with x- are ignored.
forward service
Routes paths of the dev domain to a URL: a Service of your team's namespace or an address the installation allows (the router's SSRF guard). No pods run.
| Key | Type | Required | Description |
|---|---|---|---|
type | forward | yes | |
target | string | yes | An http(s) URL; may use templates. Pattern ^(https?://|\$\{\{). |
routes | list of Route | yes | A path prefix on the dev domain; the longest matching prefix wins, on segment boundaries. At least 1 entry. |
Keys starting with x- are ignored.
Port
| Key | Type | Required | Description |
|---|---|---|---|
port | integer | yes | 1 to 65535. |
public | boolean | Also serve this port on its own host <workspace>-<port>.<workspace domain>; public ports are unique in a landscape. Default false. | |
routes | list of Route | Paths of the dev domain that go to this port. |
Keys starting with x- are ignored.
Route
A path prefix on the dev domain; the longest matching prefix wins, on segment boundaries.
| Key | Type | Required | Description |
|---|---|---|---|
path | string | yes | At most 256 characters. Pattern ^/[A-Za-z0-9._~!$&'()*+,;=:@%/-]*$. |
strip | boolean | Remove the prefix before the request reaches the service (/api/items arrives as /items). Default false. |
Keys starting with x- are ignored.
Health check
How the agent checks from inside the pod that a replica is ready.
| Key | Type | Required | Description |
|---|---|---|---|
port | integer | yes | One of the service's ports. 1 to 65535. |
path | string | Default "/". At most 256 characters. Pattern ^/. | |
mode | any-http, http-2xx | any-http: any HTTP answer counts as healthy; http-2xx: only 2xx does. Default "any-http". |
Keys starting with x- are ignored.
Mount
| Key | Type | Required | Description |
|---|---|---|---|
from | string | yes | Path relative to /data/app. At least 1 character. At most 1024 characters. Pattern ^[^/]. |
to | string | yes | Absolute path in the container. At most 1024 characters. Pattern ^/.. |
Keys starting with x- are ignored.