# Verified compose for Vaultwarden backup and restore, tested 2026-10-22 by the SelfHostBench lab (3 runs; the figures are the median). # Lab host: AMD EPYC 9V74 80-Core Processor, 2 vCPU, 7.8 GiB RAM, x86_64, Docker 28.0.4. # Floating image tags below were replaced by the version resolved at test time; # the registry digest is in the comment on each image line. # Measurements: vaultwarden-backup.json (schema 3). # Lab difference: Adds a `toolbox` container (docker:28.5.2-cli, the Docker socket, the live data volume and a backups volume) that runs the backup commands, stops and starts the live container and creates the separate restore projects. A backup host would run these commands from cron; the socket is there only so the lab can drive docker. # Companion env file: vaultwarden-backup.env (save it as .env next to this file). # Secrets are not published: the lab's values are public. In vaultwarden-backup.env: # VAULTWARDEN_ADMIN_TOKEN is commented out. Uncomment it and set your own value (the comment above it explains how). # Vaultwarden (unofficial Bitwarden-compatible server, written in Rust), pinned, with the data folder in one named volume. # This is the stack the backup test backs up. It has no reverse proxy: a backup copies the data folder, and the data folder is the same # with or without Caddy in front. (The web vault needs HTTPS in a browser; see the Vaultwarden Docker Compose guide for the Caddy version.) name: vaultwarden-backup services: vaultwarden: image: vaultwarden/server:1.37.4 # sha256:efb3cde962015fcc036b2ea625242248611943b27212ebf4392841fb30fad055 (version 1.37.4, from label:org.opencontainers.image.version) restart: unless-stopped environment: # The public URL of the vault. DOMAIN: "https://${DOMAIN}" # false after the first account exists. New users can then only join by admin invite. SIGNUPS_ALLOWED: "false" # Argon2 PHC hash of the admin password, generated with `vaultwarden hash`. It lives in the .env file, # because Compose would otherwise read the $ signs in the hash as variables. ADMIN_TOKEN: "${VAULTWARDEN_ADMIN_TOKEN}" volumes: # Everything lives here: db.sqlite3 (+ -wal/-shm while running), attachments/, sends/, rsa_key.pem, config.json. This is what you back up. - vw-data:/data/ ports: # Bound to localhost only: let the reverse proxy on the host reach it. - "127.0.0.1:8080:80" # The image already defines a check (/healthcheck.sh, every 60 s). This keeps that script and tightens the timing. healthcheck: test: ["CMD", "/healthcheck.sh"] interval: 30s timeout: 10s retries: 3 start_period: 30s start_interval: 1s volumes: vw-data: