Skip to content

deadnews/volkeep

Repository files navigation

volkeep

Label-driven Docker volume backup daemon, powered by restic

GitHub: Release Docker: ghcr CI: Main CI: Coverage

Service labelsDaemon configurationMulti-hostManual triggerDatabasesDeployRestore

  • Containers opt in via labels. At the scheduled time the daemon backs up their named volumes, optionally stopping the container for the duration or running a pre-backup command inside it.
  • Backups land in a restic repository: a local Docker volume, S3, or an rclone remote.

Service labels

Label Default Purpose
volkeep.enable required true to opt this container in
volkeep.stop false Stop the container during backup
volkeep.exec-pre Pre-backup command run in the container
volkeep.volumes all named mounts Comma-separated whitelist
volkeep.retention-days daemon default Daily snapshots to keep
name: app

services:
  app:
    image: app
    volumes:
      - data:/data:rw
    labels:
      volkeep.enable: true
      volkeep.stop: true

volumes:
  data:
  • Bind mounts and anonymous volumes are skipped.
  • Snapshots are tagged with the full volume name.

Daemon configuration

The daemon runs restic in a short-lived worker container named volkeep-worker.

Env Default Description
VOLKEEP_SCHEDULE required Daily fire time HH:MM (daemon TZ)
VOLKEEP_HOST required Identifier for restic snapshots --host
RESTIC_REPOSITORY required Restic URI, or volume:<name> (local)
RESTIC_PASSWORD required Restic repo password
AWS_* Forwarded to workers (S3 backends)
RCLONE_* Forwarded to workers (rclone backends)
VOLKEEP_RETENTION_DAYS 5 Daily snapshots to keep
VOLKEEP_MAX_AGE_DAYS 0 Remove snapshots older than this
VOLKEEP_CHECK true Verify repo integrity after each pass
VOLKEEP_JITTER 0 Random pre-fire delay (e.g. 30m)
VOLKEEP_RESTIC_IMAGE restic/restic Worker image
DOCKER_HOST local socket Override to reach a proxied daemon

RESTIC_REPOSITORY selects the repository:

  • Localvolume:<name> uses a Docker named volume as the repo, backed by a bind mount or any driver via driver_opts.

  • Remote — an S3 or rclone backend URI.
    For rclone remotes, point VOLKEEP_RESTIC_IMAGE at an image bundling the rclone binary (e.g. tofran/restic-rclone) and configure it with RCLONE_CONFIG_*.

VOLKEEP_RESTIC_IMAGE is pulled once at daemon start.

RESTIC_PASSWORD is fixed at repo init. Rotating it later locks you out of existing snapshots. Use restic key add instead.

VOLKEEP_MAX_AGE_DAYS ages out snapshots of volumes that are no longer backed up, whether the service is gone or its volume set changed; 0 keeps them forever. The cutoff must exceed retention window.

Multi-host

By design, each host runs its own daemon and repository. To share a single S3 bucket:

  • Give each host a distinct prefix: s3:s3.host.com/bucket/<host>.
  • Set VOLKEEP_JITTER to spread concurrent fires.

Manual trigger

Run a backup pass on demand:

docker kill -s SIGUSR1 volkeep

Databases

A live database can be dumped instead of stopped:

  • volkeep.exec-pre runs a command inside the container before its volumes are backed up. A non-zero exit skips the backup.
  • volkeep.volumes must whitelist the volume receiving the dump.
  • Wrap the command in /bin/sh -c '...' for redirection or variable expansion.

Deploy

volkeep needs access to the Docker API. compose.dev.yml wires it through a socket-proxy and shows the full stack. The snippets below cover only volkeep's own config.

Local:

name: volkeep

services:
  volkeep:
    image: ghcr.io/deadnews/volkeep
    container_name: volkeep
    environment:
      VOLKEEP_SCHEDULE: 03:00
      VOLKEEP_HOST: ${HOSTNAME:-web-1}
      RESTIC_REPOSITORY: volume:volkeep_backup
      RESTIC_PASSWORD: ${RESTIC_PASSWORD}

volumes:
  backup:

Remote:

name: volkeep

services:
  volkeep:
    image: ghcr.io/deadnews/volkeep
    container_name: volkeep
    environment:
      VOLKEEP_SCHEDULE: 03:00
      VOLKEEP_JITTER: 30m
      VOLKEEP_HOST: ${HOSTNAME:-web-1}
      RESTIC_REPOSITORY: s3:s3.host.com/bucket/${HOSTNAME:-web-1}
      RESTIC_PASSWORD: ${RESTIC_PASSWORD}
      AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
      AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}

Restore

Backups are stored in an ordinary restic repository. Drive it with any restic command (restore, mount). See the restic docs.

Local:

alias RESTIC='docker run --rm \
  -e RESTIC_PASSWORD \
  -v volkeep_backup:/repo \
  restic/restic -r /repo'

RESTIC snapshots --tag app_data
RESTIC restore latest --tag app_data --target /tmp/out

Remote:

alias RESTIC='docker run --rm \
  -e RESTIC_PASSWORD \
  -e AWS_ACCESS_KEY_ID \
  -e AWS_SECRET_ACCESS_KEY \
  restic/restic -r s3:s3.host.com/bucket/web-1'

RESTIC snapshots --host web-1 --tag app_data
RESTIC restore latest --host web-1 --tag app_data --target /tmp/out

About

Label-driven Docker volume backup daemon, powered by restic

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors