Harthelm Docs
Guides

Configure services

Ports, routes, health checks, replicas and env vars per service.

Last updated 2026-10-19

This guide takes a steps service from the smallest working form to one with an API path, a health check, two replicas and its own variables. The same keys work on container services. Every key is in the harthelm.yml reference.

Start small

harthelm.yml
schema: harthelm/v1

services:
  web:
    type: steps
    steps:
      - run: npm start

This runs npm start in one replica. It has no port, so it is reachable from nothing yet.

Steps

A step is a run command and an optional name for the Run panel and the logs. All steps but the last run once per replica, in order; the last one is the server and is restarted when it exits:

harthelm.yml
services:
  web:
    type: steps
    steps:
      - name: Build
        run: npm run build
      - name: Serve
        run: npm start

Install dependencies in the prepare stage, not here: prepare runs once for the workspace, a service's steps run in every replica and on every start.

Ports and routes

A port with routes puts the service on the dev URL:

harthelm.yml
services:
  web:
    type: steps
    steps: [{run: npm start}]
    ports:
      - port: 3000
        routes:
          - path: /
  api:
    type: steps
    steps: [{run: npm run api}]
    ports:
      - port: 8080
        routes:
          - path: /api
            strip: true

https://<id>-3000.<workspace domain>/api/items reaches api as /items; every other path reaches web. The longest matching prefix wins, on whole path segments.

  • public: true on a port also gives it a host of its own, https://<id>-<port>.<workspace domain>.
  • Ports are 1 to 65535, unique per service; 7777 is reserved. Ports below 1024 work.
  • A service without routes or a public port is internal: other services reach it as ws-<id>-<name>:<port>.

Health checks

Without a health check, a replica counts as ready as soon as its last step runs. With one, Harthelm asks the path from inside the pod and sends traffic only to replicas that answer:

harthelm.yml
    health:
      port: 8080
      path: /health
      mode: http-2xx

mode: any-http (the default) takes any HTTP answer as healthy; http-2xx takes only 2xx. The port must be one of the service's ports.

Size and replicas

harthelm.yml
    plan: small
    replicas: 2
    maxReplicas: 3
  • plan sets CPU and memory per replica; without it, the installation's default service plan applies. See Plans and quotas.
  • replicas (1 to 100) runs that many pods; requests are balanced over the ready ones.
  • maxReplicas caps how far you can scale without a Sync, in the Run panel or with hart scale. It is at most the plan's maximum.

Environment variables

env adds variables to the service, on top of the workspace's:

harthelm.yml
    env:
      PORT: 8080
      NODE_ENV: ${{ env.NODE_ENV }}
      PUBLIC_URL: https://${{ workspace.devDomain }}
      API_KEY: ${{ secrets.API_KEY }}

Numbers and booleans become strings. The service's own value wins over a workspace variable of the same name. Templates are filled in at Sync; see Environment variables and secrets.

Sleep

harthelm.yml
    availability: on-demand
    idleTimeoutMinutes: 30

An on-demand service sleeps after the given minutes without requests and wakes on the next one. Without availability, the plan's applies.

Apply the change

Save the file, then Sync. The review shows each changed service with what changed and whether its pods restart.

Terminal
hart sync

The whole service

harthelm.yml
services:
  api:
    type: steps
    plan: small
    replicas: 2
    steps:
      - name: Serve
        run: npm run api
    env:
      PORT: 8080
      API_KEY: ${{ secrets.API_KEY }}
    ports:
      - port: 8080
        routes:
          - path: /api
            strip: true
    health:
      port: 8080
      path: /health
      mode: http-2xx
    availability: on-demand
    idleTimeoutMinutes: 30

Next steps

On this page