Skip to main content
The top-level volumes: block declares persistent volumes your services can mount, Compose-style. A volume survives redeploys, and slipway seeds every new preview from the volume’s latest snapshot — so reviewers get realistic, isolated data instead of an empty database.
volumes:
  pgdata: {}            # a named volume — capacity is set by your plan

services:
  db:
    image: postgres:16
    env:
      POSTGRES_PASSWORD: ${secret.PG_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
  api:
    build: { context: ./api }
    env:
      DATABASE_URL: postgres://postgres:${secret.PG_PASSWORD}@db:5432/postgres
Bring any image — Postgres, MySQL, a search index, an uploads folder. Slipway manages the volume and its snapshots, not your credentials.

Declaring volumes

WhereSyntax
Top levelvolumes:pgdata: — each key declares a named volume. Capacity is set by your plan, not here.
In a servicevolumes:- pgdata:/path — references a declared volume; the path must be absolute.
A volume mounted by two or more services is shared between them (this needs a cluster with shared-storage support).

Sizing and the storage budget

Volume capacity is set by your plan, not in slipway.yaml — every volume is provisioned at your plan’s default size. A size: key in the spec is ignored (the deploy logs a warning if it finds one); this keeps a single repo from reserving a huge slice of shared storage. Your plan includes a total storage budget across every named volume, and slipway bills on the storage you actually use — the bytes written, not the volume’s reserved size — so a half-empty volume only counts for what’s in it. Watch usage in the sidebar’s Usage panel and on Settings → Billing.
The budget only counts volumes on slipway’s own cluster. Volumes on a cluster you bring yourself live on your storage and don’t count.

How seeding works

There’s no separate “base” database to manage — the snapshot history is the durable lineage:
  1. First deploy — the volume comes up empty.
  2. Load data — run your app and add data, or load a dump.
  3. Take a snapshot — from the deployment page or sw snapshot.
  4. Every preview after that — gets its own isolated copy, pre-seeded from the latest snapshot. A destructive migration in one PR never touches another.
Snapshots are manual — slipway never auto-snapshots, because the first deploy’s volume is empty. Take one when there’s data worth keeping.

Snapshots

Each volume keeps a numbered history, newest first. Manage them from a deployment page (Snapshot now + the history table) or the CLI:
sw snapshot   <deployment-id> --volume pgdata --note "seed data"   # capture the live volume
sw snapshots  <repo-id>       --volume pgdata                       # list history
sw restore    <deployment-id> --volume pgdata --snapshot 3          # revert in place
sw restore reverts a deployment’s live volume to an earlier snapshot. It takes a safety snapshot of the current state first, so the revert is itself undoable. Retention follows your plan (default 10 per volume) — the oldest are pruned past the cap, and the latest is never pruned.
Slipway snapshots whatever is on the volume. If you load production data, it ends up in every preview seeded from that snapshot — sanitise on the way in. Slipway is a preview tool, not a backup tool.

Any engine works

Seeding is a raw block-level copy, so slipway doesn’t need to understand the engine — Postgres, MySQL, Mongo, SQLite-on-disk, a Lucene index, or a plain uploads folder all snapshot and restore identically. Mount the volume wherever the image writes.

Limitations

  • Snapshots need snapshot-capable storage. On a cluster without it (e.g. local k3d), volumes still work but Snapshot now degrades gracefully with a clear message.
  • One replica. A volume-mounted service runs at a single replica.
  • Per-repo lineage. Snapshot history belongs to a (repo, volume) pair; cross-repo snapshot sharing isn’t available.
  • No anonymisation tooling. Sanitise data on the way in, not after.