Most apps do not need a volume. Use /shared.db and /shared/ for state shared across instances and regions.

A persistent volume is a private filesystem for software that expects local disk, such as Postgres, Redis, or a development environment. Attaching one makes the service stable: its identity and disk remain together across deploys, restarts, and host moves.

attach a volume

Attach a volume to an automatic source build:

kedge up --volume /var/lib/myapp --volume-size 20GiB

For a prebuilt image:

kedge publish my-database --image postgres:18 \
  --volume /var/lib/postgresql/data \
  --volume-size 20GiB

size is a sparse logical limit. The volume is seeded from the image path on first boot.

Dockerfiles and images

A Dockerfile VOLUME creates a one-member app by default:

VOLUME ["/var/lib/postgresql/data"]

Use --members 0 when an image declares a volume the service does not need:

kedge publish myapp --image example/myapp:1.2 --members 0

Several volume paths for one member share its underlying store while remaining separate directories. Dockerfile and image volume declarations seed those paths unless volume.nocopy is set.

Compose

Use a named volume for a service in a Compose app:

services:
  db:
    image: postgres:18
    volumes: [data:/var/lib/postgresql/data]

volumes:
  data:
    x-kedge:
      shared: false
      size: 20GiB

shared: false gives each stable member its own persistent volume. A shared: true volume mounts the app's replicated /shared/ tree instead.

With a persistent volume, deploy.replicas sets the member count. Outside Compose, use --members:

kedge up --volume /data --members 3

stable members

Kedge calls each stable copy of a service a member. Members have ordinals starting at zero:

Each member has its own volume. Scaling down retains that volume; scaling up reattaches the same ordinal.

List members or move one to another region:

kedge members my-database
kedge members my-database migrate 0 nrt

Kedge handles ordinary restarts and host recovery automatically.

durability

Volume flushes use the host's local disk (NVMe on production hosts). fsync, ext4 journal commits, and database checkpoints do not wait for object storage.

Kedge publishes local writes to object storage in the background after 1s or 16 MiB. A controlled stop, member migration, or fork waits until every accepted write is published before it proceeds. If object storage is unavailable, that operation fails and leaves the member in place.

The local writeback limit is 4 GiB per volume by default. If publication cannot keep up and the limit fills, new writes fail instead of consuming unbounded host memory. Restarting the daemon on the same host replays the local journal. After complete host loss, recovery uses the last published remote state.

Check publication state:

kedge volumes status my-database-0

clean means every write accepted by the volume backend is present in the remote manifest. It does not include data still held in the guest filesystem's page cache. Other states report pending writes, object-store errors, lease fencing, or a full local journal.

retention and deletion

Member volumes are named <app>-<ordinal> and never disappear as a side effect. App deletion, service removal, or scale-down leaves them retained.

List and explicitly delete retained volumes:

kedge volumes
kedge volumes rm my-database-0

Attached volumes and ancestors still used by forks refuse deletion. The generated REST volume operations expose the same operations.