Skip to content

Backup Jobs

A backup job defines what to back up, where to put it, when to run, and how many copies to keep. This guide explains every option in the job wizard.

Creating a Job

Go to Jobs → Create Job. The wizard has four steps.

Create backup job wizard


Step 1 — What

Pick which items to include in this job. Vault discovers items automatically from your server:

Category What's included
Containers Docker containers (image, XML template, and all mapped appdata volumes); per-container path excludes
Virtual Machines libvirt VMs (live snapshot or cold backup depending on running state); NVRAM preserved
ZFS datasets Native zfs send/receive with snapshot management
Folders Any arbitrary path on your server (e.g. /mnt/user/appdata, /boot/config)
Plugins Installed Unraid plugins

Notes on containers

  • Vault backs up the container image, its XML template, and all host paths mapped into the container.
  • Special files (Unix sockets, device nodes, named pipes) are skipped automatically — they cannot be archived and their presence does not fail the backup.
  • Tailscale-enabled containers are fully supported.
  • Bind-mounts that point at the host root (/ → /rootfs, used by Glances, Telegraf, Netdata, cAdvisor, node-exporter) are detected and skipped without walking the host filesystem.

Mount point detection and auto-skip overrides

When configuring container backups, Vault lists each container's bind mounts and named volumes:

  • Appdata paths (e.g. /mnt/cache/appdata/..., /mnt/user/appdata/...) and named volumes are included by default.
  • Shared data paths (such as /mnt/user/media, /mnt/user/downloads, /mnt/user/isos, /mnt/user/domains, /mnt/user/backups, /mnt/remotes) are auto-skipped by default to prevent inadvertently creating massive backups.
  • Overriding shared data auto-skips: If an application (such as Gotify or a custom service) stores application state or an SQLite database inside a shared path, you can check that mount in the job editor. Overridden mounts display an included (override) badge and are backed up.
  • System and hardware paths (/dev, /proc, /sys, /run, root /mnt, and direct /mnt/diskN paths) are always skipped and cannot be overridden for safety.
  • Database dumps vs file-based databases: Logical SQL dumps (database.sql) are available for server databases (PostgreSQL, MySQL, MariaDB). For file-based databases like SQLite, back up their directory mount directly; no separate dump option is needed.

Container database dumps

For containers Vault recognises as a database (PostgreSQL, MySQL, MariaDB), a job can capture a logical SQL dump alongside the volume archive. The dump is a sidecar of the backup (database.sql, optionally .gz/.zst when the job compresses) — it is not stored inside any restored volume.

On restore Vault decides whether to replay it automatically:

  • Reloaded automatically when the backup recorded that the dump is the authoritative copy (the volumes were captured while the server was running, so the dump is the consistent snapshot). Vault waits for the database to become ready and replays the dump into the restored container.
  • Left for you, not reloaded when the restored volume is the newer, consistent copy (the volumes were captured with the server stopped), when the container was not running, or when the database never became ready. Replaying in these cases would roll the database back to where the dump began, so Vault preserves the dump instead of applying it.

When the dump is not reloaded, Vault copies it out of its temporary staging directory to a durable, discoverable location and reports the exact path in the restore progress and logs (database dump saved to …):

  • Custom destination — written to <destination>/<container>-database.sql (with the job's .gz/.zst suffix when compressed).
  • In place (no custom destination) — written beside the container's first restored volume, for example /mnt/user/appdata/<container>-database.sql.

The file name is prefixed with the container name so several containers restored under a shared parent (such as /mnt/user/appdata) do not collide. You can replay a preserved dump manually with your database's normal import tool (for example mysql < database.sql or psql -f database.sql). This behaviour is the same for classic and deduplicated backups.

Excluding container sub-paths

You can list paths to exclude from a container backup (e.g. /config/Library/Application Support/Plex Media Server/Cache or /config/Sonarr/MediaCover). The job wizard exposes a free-text list per container, and Vault ships a GET /api/v1/presets/exclusions catalogue of common rules for popular containers (Plex, Sonarr, Radarr, etc.) the UI offers as starting points. For some apps the response also carries advisory notes/warnings (e.g. the Immich database caveat below), which the wizard shows inline.

Container appdata path and volume exclusions

Docker container backups focus on configuration and application state rather than large media libraries:

  • Configured appdata path: Vault derives container config mounts from the global appdata_path setting (default /mnt/user/appdata, configurable in Settings → General). Mounts located under this path (or /mnt/cache/appdata) and Unraid /boot configurations are included in container backups by default.
  • Non-appdata bind mounts excluded by default: Bind mounts outside the appdata path (e.g. /mnt/user/media, /mnt/user/downloads, or custom array paths) are marked as non-appdata and auto-excluded by default to prevent accidental multi-terabyte library backups in a container job. Docker named volumes (e.g. Compose volumes under /var/lib/docker/volumes) are recognized as application data and remain included by default.
  • Per-mount override: In the job wizard under Container Mounts & Exclusions, you can expand any container and check any overridable non-appdata or shared data mount to include it in the backup.
  • System paths: Device and virtual filesystem mounts (e.g. /dev, /proc, /sys, direct array disks /mnt/disk*, or root /mnt) are safety-critical and cannot be included.

Auto-include new containers

Container backup jobs can optionally enable Auto-Include New Containers:

  • Automatic discovery: Any newly created Docker container discovered during a scheduled or manual run is automatically enrolled in the job.
  • Safe defaults: Auto-added containers default to backing up appdata mounts and Docker named volumes; non-appdata bind mounts (e.g. multi-terabyte media shares) are excluded by default.
  • Exclusion label: Containers carrying the Docker label vault.exclude are skipped from auto-inclusion.
  • Automatic pruning: When a container is permanently removed from Docker, it is automatically pruned from the job after remaining absent for at least 24 hours, preventing stale item alerts while protecting against transient downtime.

Backing up Immich

Immich detection works for both the official ghcr.io/immich-app/immich-server image (media root mounted at /data) and the imagegenius fork ghcr.io/imagegenius/immich (/photos). The recommended exclusions cover both layouts:

Always backed up (critical) Excluded (regeneratable)
Official (/data) upload, library, profile, backups thumbs, encoded-video
imagegenius (/photos) upload, library, profile, backups thumbs, encoded-video

Thumbnails and re-encoded video are re-created by Immich on demand after a restore, so excluding them saves space without data loss. The upload/library/profile folders hold your original photos and videos and are never excluded.

Immich's database is the catch: Immich stores every asset's metadata in a PostgreSQL database that runs in a separate container, so a filesystem backup of the Immich container does not capture it — and a raw file copy of the Postgres data directory is not crash-consistent. Choose one of:

  1. Recommended — Immich's built-in database backup. In Immich, go to Administration → Settings → Backup Settings and enable the database dump. Immich writes timestamped .sql.gz dumps into the backups/ folder inside your media root (/data/backups or /photos/backups), which Vault keeps. Backing up the media root then captures both your photos and a restorable database dump in one job.
  2. Back up the Postgres container as its own Vault job. Add the Immich Postgres container to a separate job; Vault stops it before archiving, giving a consistent copy of its data directory.
  3. Dump via a pre-backup script. Vault runs a job's Pre-backup script before stopping any containers, with VAULT_JOB_NAME, VAULT_STATUS, VAULT_JOB_ID, and VAULT_RUN_ID exported into its environment. A script can docker exec a pg_dump into the media root before the backup runs.

Notes on ZFS datasets

  • Vault uses zfs send (full) and zfs send -i (incremental) under the hood and manages the snapshot lifecycle itself.
  • Restoration goes through zfs receive, preserving properties and child datasets.
  • The host zfs binary must be on PATH — true on any Unraid system with a ZFS pool.

Notes on folders

  • You can back up /mnt/user/appdata to get all container appdata in one shot without selecting individual containers.
  • Folder backups only wake the destination disk when writing — the source array is read sequentially.

Step 2 — Where & when

Select which storage destination this job will write to. If you have not added one yet, save the wizard and go to Storage first — see Storage Destinations. This step also sets the schedule.

Schedule

Option Description
Hourly Runs at a fixed minute past every hour
Daily Runs at a specific time each day
Weekly Runs on selected days of the week at a specific time
Monthly Runs on a specific day of the month (or First/Last day) at a specific time
Yearly Runs on a specific month and day at a specific time
Custom (cron) Any valid cron expression for advanced schedules

"First day" and "Last day" of month: Instead of a fixed day number, you can choose First day of month or Last day of month. Last-day jobs fire correctly on months of any length (28, 29, 30, or 31 days).

Custom (cron): Choose Custom to type a cron expression directly, for schedules the presets cannot express — "every three hours on every second day" is 0 */3 */2 * *. The five fields are minute hour day-of-month month day-of-week, and the usual *, */step, from-to and a,b,c syntax all work. Vault also accepts L in the day-of-month field, meaning the last day of the month, and the @daily/@hourly-style descriptors.

The form checks the expression as you type, and the server validates it again on save and rejects anything it cannot schedule — a job is never stored with a schedule that would silently never fire. A custom expression is available everywhere a schedule is: the job schedule, the separate verify schedule, and replication sync.

Time format: The schedule UI uses your Unraid time format setting (12-hour or 24-hour) automatically.

Manual-only jobs: Leave the schedule empty to create a job that never runs on its own — you run it on demand with the Run Now button.


Step 3 — How

Choose the backup strategy and, under Advanced options, tune compression, retention, verification, and scripts.

Backup Types

Type What it backs up Restore speed Storage use
Full Everything, every run Fastest — single archive Highest — full copy each time
Incremental Changes since the last backup of any type Slowest — may need to chain archives Lowest
Differential Changes since the last full backup Medium — needs full + one diff Medium

Scheduled full backups

An incremental or differential job can run its own periodic full backup, under Advanced options → Scheduled full backup. It takes a second schedule, independent of the job's: hourly incrementals with a full every Sunday at 04:00, for example.

This matters because every incremental or differential restore needs its chain intact back to the last full. A periodic full restarts the chain, which keeps restores fast and bounds how much a single damaged archive can cost you. Before this option the only way to get one was a duplicate job that differed solely in backup type.

The option is hidden for Full jobs — every run is already a full — and clearing it turns the extra cadence off.

First run of an Incremental or Differential job

On the very first run there is no parent backup to attach to, so an Incremental or Differential job automatically performs a Full backup and only later runs capture changes. Choosing Incremental or Differential from the start is the correct workflow — there is no need to run a manual Full first.

For most home server use cases, a weekly Full backup with daily Incremental runs gives a good balance between storage cost and restore speed.

Retention

Retention controls how many restore points are kept before the oldest is deleted. (A restore point is one completed backup you can restore from.) Vault supports two modes. If any Long-Term Retention value is set, LTR is used and the simple settings are ignored.

Mode 1 — Simple count

Field Description
Keep last N restore points Vault keeps this many restore points per job. When a new backup completes and the count exceeds the limit, the oldest is pruned — including its backup files from storage.

Good for "always keep my last 7 backups" type policies.

Mode 2 — Long-Term Retention (LTR)

Field Description
Keep latest Number of most-recent restore points kept unconditionally (independent of cadence)
Keep daily Number of distinct days to retain (one restore point per day)
Keep weekly Number of distinct weeks (one restore point per week)
Keep monthly Number of distinct months (one restore point per month)
Keep yearly Number of distinct years (one restore point per year)

Long-Term Retention (LTR) lets you keep, say, "last 7 days + last 4 weeks + last 6 months + last 3 years" without paying for hundreds of restore points. The Job UI shows a retention preview — exactly which restore points the policy would prune on the next run — before you save.

If you leave all LTR counters at 0, classic simple retention applies. If any LTR counter is greater than 0, Vault uses LTR for that job and ignores the simple settings (Keep last N and keep-for-N-days).

Set retention based on storage budget and how far back you want to be able to restore:

  • 7 daily backups → simple count = 7
  • A weekly full + 6 daily incrementals → simple count = 7, two jobs (one weekly full, one daily incremental)
  • Year of history without storage bloat → LTR with keep_daily=7, keep_weekly=4, keep_monthly=12, keep_yearly=3

Step 4 — Name & review

Field Description
Name A unique name for this job. Shown on the Dashboard, History, and Restore pages.

Check the plain-language summary of what the job will do, then click Save.


Running a Job Manually

To trigger an immediate backup without waiting for the schedule:

  1. Go to Jobs
  2. Click the Run Now button on the job card (play icon)

Progress streams in real time via WebSocket. The run appears in History when complete.


Monitoring Progress

While a job is running:

  • The Dashboard shows a live progress indicator
  • The History page lists the run with status "Running"
  • The Logs page shows per-item progress entries including container name, backup type, storage destination, and elapsed time

Cancelling a Running Job

To stop an in-progress backup:

  1. Go to Jobs (or the Dashboard's Backup in progress tile)
  2. Click the Cancel button on the running job

Vault signals cancellation through the entire pipeline — file I/O, directory traversal, and engine handlers all check for cancellation and stop gracefully. The job is marked as "cancelled" in History.

Cancel also works on a job that is queued behind another run (for example after clicking Run Now while a scheduled backup is active) — the queued run is removed before it starts. Deleting a job that is currently running cancels the run first.

Jobs also have an automatic safeguard:

  • 2-hour stall detection — a job with no progress for 2 hours is cancelled (with a warning at 30 minutes). There is no overall time limit: a backup that is still transferring data can run for as long as it needs.

Deleting a Job

To delete a job:

  1. Go to Jobs
  2. Click the ... menu → Delete
  3. Choose whether to Keep backup files or Delete backup files from storage

Deleting backup files removes the archives from the storage destination. Keeping them leaves the files in place — useful if you plan to import them into a new job later.


Deleting Individual Restore Points

To delete a specific backup without touching the job or other restore points:

  1. Go to Restore in the left sidebar
  2. Select the job
  3. Find the restore point and click the trash icon
  4. Confirm the deletion

This removes both the backup files from storage and the restore point record from the database.


Restoring Across Backup Jobs

The Restore wizard supports selecting items that belong to different backup jobs (for example, restoring containers from an appdata job together with a VM from a virtualisation job):

  1. Step 1 (Select Items): Select any combination of backed-up items across different jobs.
  2. Step 2 (Choose Versions): The timeline aggregates restore points from each backup job covering your selected items:
  3. Each restore point indicates its source job name and a coverage label (such as "Contains 2 of 3 selected items" or "Contains all 3 selected items").
  4. If an item is missing from a restore point, Vault explains why: whether the item is not part of that job or was not captured in that specific backup.
  5. For older legacy restore points created before per-item manifests were tracked, membership is marked as "Item list not recorded for this backup" and treated permissively for items in that job.
  6. You can choose one restore point per source job. A coverage summary shows which version is assigned to each selected item, highlighting any items not yet covered.
  7. If some items cannot be covered, recovery options allow you to remove uncovered items from the selection with a single click or return to the item selector.
  8. Step 3 (Restore Options & Pre-Flight):
  9. Each source job is reviewed as an independent restore unit with its own destination overrides, passphrase (if age encrypted), clean destination toggle, and per-item file selection disclosures.
  10. Pre-flight checks run per unit to ensure backups can be restored before starting.
  11. Restores are submitted and executed sequentially, streaming live progress and logs per job.

Note

This wizard flow restores regular backup items. To recover the Vault configuration, database, or plugin state after a system failure, see Disaster Recovery.


Managing Stale Items

If a container, VM, ZFS dataset, or folder is removed from Unraid after a job is created, Vault marks it as "Not found" in the job's item list. Click the remove button next to the flagged item to drop it from the job. The Folder picker performs an async os.Stat (via GET /api/v1/path-exists) for every custom folder on mount, so legitimately valid paths aren't falsely flagged.


Verification

Every backup item is hashed with SHA-256 during the upload, and Vault stores the digest alongside the restore point. Two verification layers exist on top of that:

  • Per-run verify — when the job's Verify backup setting is on (default), each item is read back from storage at the end of the run and re-hashed. Mismatches fail the run.
  • On-demand verify — from the Restore page you can click Verify on any individual restore point at any time. The result, including per-item byte counts and errors, is stored under verify_runs and listed in the restore point's verify history.

Both paths use the same code, so per-run and on-demand results are directly comparable.

Scheduled verification and backup contention

A job can also run verification on its own cron schedule (Verify schedule) in either quick mode (checks each item exists and is the right size) or deep mode (streams every item back and re-hashes it). Deep verification reads the entire backup, so it saturates the destination's disk and network.

To keep that from turning a running backup into an apparent freeze, Vault defers a scheduled deep verify while a backup is active on the same storage destination. The deferral is recorded in the activity log (Deep verify for "<job>" deferred: a backup is running on the same storage destination) and the deep verify simply runs at its next scheduled tick. Quick verification is cheap enough that it still runs alongside a backup, and on-demand verification you trigger yourself is never deferred.


Encryption

If you set a passphrase under Settings → Security → Encryption, all backup archives are encrypted with your backup password (age encryption — a modern, audited standard) before they leave the host. Without the passphrase, restoring is not possible. Encryption is transparent to storage destinations — it applies equally to local, SFTP, SMB, NFS, WebDAV, and S3.

The encryption status is shown on the Dashboard's 3-2-1 compliance widget, and on each restore point's chain-health badge (along with details on the referenced full backup for chained restores).


Deduplication

Deduplication stores each unique piece of data only once, so repeated data across runs and sources doesn't cost extra space — see How Backup Works for the concept. The rest of this section covers the operational details.

When you enable Dedup on a storage destination, Vault chunks every backup with Keyed-FastCDC (256 KiB / 1 MiB / 4 MiB min/avg/max) and stores only one copy of each chunk in a per-destination dedup repo at <dest>/_vault/packs/ and <dest>/_vault/index/. The repo's chunker is keyed off a 32-byte secret per destination, which closes the fingerprinting-attack class described in Truong et al. 2025 — observers without the secret can't recompute chunk boundaries from public corpora.

Operational notes:

  • Each backup writes a per-item manifest into the dedup repo. The manifest_id (or item_manifests map for multi-item jobs) is stored on the restore point so restores can resolve chunks.
  • The Storage card shows live dedup stats (chunks, packs, logical/physical bytes, dedup ratio).
  • vault dedup gc --dest <id> (or POST /api/v1/storage/{id}/gc) runs mark-and-sweep GC: walks every live restore point's manifest, marks reachable chunks, then deletes packs whose every chunk is unreachable. Mixed packs are left in place.
  • vault dedup repair --dest <id> rebuilds the SQLite chunk/pack index from the on-storage *.idx blobs — use when the local DB is lost or corrupted but the destination is intact.
  • Dedup-mode and non-dedup destinations can coexist on the same Vault install; the flag is per-destination and immutable after creation.

Imported backups (Storage → Scan + Import) carry per-item dedup manifest IDs in their manifest.json, so dedup restore points produced on one Vault instance can be re-discovered and restored on another. Run manifests are encrypted at rest (AES-256-GCM for deduplicated destinations, age encryption for age-encrypted jobs); scanning an age-encrypted destination prompts for the decryption passphrase to unlock and import the backup metadata.


Job Queue & Queue Management

Vault executes one mutating backup or restore operation at a time under its runner mutex. When multiple backup jobs or restores are triggered concurrently (either by schedule or manually via Run Now), subsequent operations enter the in-memory runner queue.

  • Queue Visibility: Active and pending operations are surfaced at the top of the History tab in the Active & Queued Operations card, showing the operation kind (Backup, Restore, Cleanup), status (Queued or Running), job name, and queued timestamp with live WebSocket updates.
  • Cancelling Queued Operations: Operators can cancel queued backup and restore operations directly from the History tab before execution begins by clicking Cancel and confirming the prompt.
  • Cleanup Operations: Background storage deletion and remote orphan cleanup operations (Cleanup) run to completion to prevent leaving orphaned remote files, and cannot be cancelled from the queue.

Next steps