Harthelm Docs
Guides

Add a database

Add a managed PostgreSQL to the landscape and wire its secrets into env vars.

Last updated 2026-10-19

This guide adds a PostgreSQL database to a landscape and hands its address to your app. Harthelm creates the database at the Sync, keeps its password in the workspace's vault and dumps it every night.

Add the managed service

Add a managed service next to your app, and give the app the database's address:

harthelm.yml
schema: harthelm/v1

services:
  db:
    type: managed
    provider: postgres
    config:
      postgresVersion: "18"
      database: app
      user: app
    secrets:
      password: ${{ secrets.DB_PASSWORD }}

  web:
    type: steps
    steps: [{run: npm start}]
    needs: [db]
    env:
      DATABASE_URL: ${{ services.db.dsn }}
    ports:
      - port: 3000
        routes: [{path: /}]
  • provider: postgres is the built-in PostgreSQL. The console's Managed services → Catalog shows what else your installation offers: MariaDB, Valkey, RabbitMQ and S3 object storage are built in (Managed services).
  • config sets the version ("18" or "17"; it cannot change later), the database and the user.
  • secrets.password names a vault key. The password itself never goes into the file.
  • ${{ services.db.dsn }} becomes postgresql://app:<password>@<host>:5432/app when web starts.

Sync and give it a password

Click Sync in the Run panel. The review lists db under "To add" and says that DB_PASSWORD has no value yet. Click Enter secrets, then Generate for a random 32-character password, or type one with 12 to 128 characters. Then click Sync.

The database is ready in about a minute; its row in the Run panel shows "Managed service: Synchronized". Run or restart web so it picks up DATABASE_URL.

Other ways to connect

TemplateValue
${{ services.db.host }}the host
${{ services.db.port }}the port, 5432
${{ services.db.dsn }}the full URL with user, password and database
${{ services.db.secret }}the password alone Since 1.3.0
${{ services.db.details.<field> }}another field the provider reports

For libraries that read the PG* variables:

harthelm.yml
    env:
      PGHOST: ${{ services.db.host }}
      PGPORT: ${{ services.db.port }}
      PGDATABASE: app
      PGUSER: app
      PGPASSWORD: ${{ services.db.secret }}

${{ services.db.secret }} follows a password rotation by itself; ${{ secrets.DB_PASSWORD }} keeps the value of your last Sync until you sync again.

Size and storage

harthelm.yml
  db:
    type: managed
    provider: postgres
    plan: small
    resources:
      storage: 2Gi

plan is tiny (the default: 0.25 vCPU, 256 MiB, 1 GiB) or small (0.5 vCPU, 512 MiB, 5 GiB). resources changes what the plan lets you change. Sizes are MiB as a number, or 2048Mi, 2Gi, 1.5Gi; decimal units like 2GB are refused. Storage can only grow, and only where your installation's storage class can grow volumes: otherwise the Sync refuses a plan that needs more storage with update-constraint.

Rotate the password

Open the database in Managed services and click Rotate under Secrets. Harthelm generates a new value, stores it in the vault, and the services that read the connection restart with it.

Pause, resume and delete

  • Teardown pauses the database; the data stays. The next Sync resumes it.
  • Removing or renaming the service in harthelm.yml deletes the database and its data at the next Sync; the review warns first.

Backups

The database is dumped every night (backups: true is the default). The details drawer in Managed services shows the last dump and has Dump now.

MariaDB instead

A MariaDB works the same way, with provider: mariadb Since 1.3.0:

harthelm.yml
  db:
    type: managed
    provider: mariadb
    config:
      mariadbVersion: "11.8"
      database: shop
      user: shop
    secrets:
      password: ${{ secrets.DB_PASSWORD }}

${{ services.db.dsn }} is then mysql://shop:<password>@<host>:3306/shop, and ${{ services.db.details.jdbcUrl }} the JDBC URL without credentials. mariadbVersion is "12.3", "11.8" (the default) or "11.4". You can move to a newer series later by changing it; the server is upgraded in place and cannot go back, so click Dump now first.

Next steps

On this page