Harthelm Docs
Concepts

Services

The four service types steps, container, managed and forward, and when to pick which.

Last updated 2026-10-19

services in harthelm.yml maps a name to a service. Every service has a type, and the type decides what runs.

typeWhat runsPick it when
stepsYour shell steps, in Harthelm's base image, with /data/app mountedYou run code from the workspace: a web server, an API, a worker
containerYour OCI image's entrypoint and commandYou have an image already, or need a runtime the base image lacks
managedA database or another service from the team's catalogYou need PostgreSQL or another managed service
forwardNothing: the router forwards paths of the dev URL to another addressYou want a path of your app to reach another service of the team or an allowed address
harthelm.yml
services:
  web:
    type: steps
    steps:
      - run: npm start
    ports:
      - port: 3000
        routes: [{path: /}]
  search:
    type: container
    image: registry.example.com/team/search@sha256:4f1c…
    ports:
      - port: 8080
  db:
    type: managed
    provider: postgres
    secrets:
      password: ${{ secrets.DB_PASSWORD }}
  docs:
    type: forward
    target: https://docs.example.org
    routes: [{path: /docs, strip: true}]

Names

Lower-case letters, digits and dashes, starting with a letter, at most 32 characters. ide is reserved. Renaming a service deletes it and creates a new one at the next Sync; for a managed service that deletes its data.

steps services

Harthelm runs your steps in every replica. All steps but the last run once; the last one is the server and is restarted when it exits. The service sees all of /data/app, so a change to your code needs only a restart, not a Sync. Tools you installed with mise in prepare are there too.

A steps service may name its own image, built FROM harthelm-base.

container services

A container service runs your image's ENTRYPOINT and CMD, or the command and args you set. It runs under the workspace's user, not the image's, and does not take part in the run stage: it starts at Sync and keeps running.

Sync pins the image's digest, so the service runs the same image until the next Sync. Pin it yourself with @sha256:… for reproducible Syncs; the Sync review warns about images by tag. Private images need the team's container registries. See Run a container image.

managed services

A managed service is created and changed by Sync, paused by Teardown, and deleted, with its data, when you remove it from the file and Sync. Other services read its address through templates such as ${{ services.db.dsn }}. See Managed services.

forward services

A forward service runs no pods. Its routes send paths of the dev URL to target: a Service of your team, or an address the installation allows (public addresses by default). A refused target blocks the Sync.

What every service with pods has

steps and container services share these keys; the Configure services guide shows them in use:

  • ports and their routes: what the dev URL sends to the service.
  • health: when a replica counts as ready.
  • plan, replicas and maxReplicas: the size of each replica and how many run.
  • availability and idleTimeoutMinutes: whether the service sleeps when unused.
  • env, mounts and needs: its variables, extra file paths and the services it calls.

A service with neither routes nor a public port is internal: the other services reach it as ws-<id>-<name>:<port>, where <id> is the workspace's id.

Next steps

On this page