The jekyo.yaml file

Volumes & state

Databases and stateful services keep their data across deploys, restarts, and even jekyo down. Backups run on a schedule and restore with one command.


Declaring volumes

app: shop
services:
  db:
    image: pgvector/pgvector:pg16
    env:
      POSTGRES_PASSWORD: ${PG_PASSWORD}
    port: 5432
    volumes:
      pgdata: /var/lib/postgresql/data   # name -> mount path
volumes:
  pgdata:
    size: 10Gi

Two parts: the service mounts a named volume, and the top-level volumes: block declares it. Declaring an unused volume, or mounting an undeclared one, is a validation error.

Volume options

KeyTypeDefaultDescription
sizestringrequiredStorage size, for example 10Gi.
classstringlocal-pathStorage class.
backupmapScheduled backups. See Backups.

Sharing a volume between services

Give the mount a subpath and several services can share one volume (and one size):

services:
  app:
    volumes:
      data:
        path: /var/lib/app
        subpath: app
  db:
    volumes:
      data:
        path: /var/lib/mysql
        subpath: mysql
volumes:
  data:
    size: 10Gi

Lifecycle

CommandEffect on data
jekyo upNever touches volumes. Redeploys reuse them.
jekyo down shopRemoves workloads. Volumes are kept.
jekyo down shop --volumesDeletes volumes too. Asks for confirmation.

This asymmetry is deliberate: destroying compute is routine, destroying data never is.

Backups

Backups are encrypted snapshots (restic) sent to any S3-compatible storage: AWS S3, Cloudflare R2, Backblaze B2, or a MinIO you host yourself.

1. Set the target, once per cluster

jekyo backup config \
  --endpoint https://s3.eu-central-1.amazonaws.com \
  --bucket my-backups \
  --access-key AKIA... \
  --secret-key ...

An encryption password is generated and stored in the cluster. Export a copy somewhere safe: without it, backups cannot be read.

2. Schedule it in jekyo.yaml

volumes:
  pgdata:
    size: 10Gi
    backup:
      schedule: "0 3 * * *"   # every night at 03:00
      keep: 7                 # snapshots to retain

jekyo init offers this automatically for every volume a template ships: answer 15m, hourly, daily, weekly, or a cron expression. Non-interactive: jekyo init postgres --defaults --backup daily.

Backup options

KeyTypeDefaultDescription
schedulestringrequiredCron expression for snapshot timing.
keepint7Snapshots retained; older ones are pruned.

3. Operate

jekyo backup now shop/pgdata          # snapshot immediately
jekyo backup ls shop/pgdata           # list snapshots
jekyo backup restore shop/pgdata      # restore the latest
jekyo backup restore shop/pgdata a1b2c3   # restore a specific snapshot

Restore is safe by construction: it asks for confirmation, stops the workloads using the volume, replaces the volume contents with the snapshot, and starts the workloads again.

Snapshots are per volume, per app

Each volume gets its own encrypted repository under bucket/<app>/<volume>. Deleting an app with --volumes does not delete its backups; they remain restorable after a redeploy.

Current constraints

  • Volumes are node-local (fast, simple). Off-server durability is exactly what backups are for.
  • replicas > 1 together with volumes is rejected. Per-replica volumes are on the roadmap.

Local backups

No S3 account? jekyo backup config --local keeps restic repositories on the server itself at /var/lib/jekyo/backups. Mount a dedicated disk at that path for real durability: a backup on the disk that dies with the data protects against mistakes, not hardware. Everything else (schedules, backup now, ls, restore) works identically.

Previous
Domains & TLS