Skip to main content

BtrUp: Btrfs + Borg backup script

⚠️ Warning ⚠️ You use BtrUp at your own risk. You are the only person responsible for any damage caused by the use of BtrUp. ⚠️

BtrUp is distributed under the terms of the GPLv3 license. See the LICENSE file for more details.

See CHANGELOG.md for the version history.

Installation

pip install btrup

You must also have a Btrfs filesystem and Borg Backup installed on your computer.

Usage

BtrUp is executed on the command line with a TOML config file as mandatory argument.

btrup CONFIG [-n] [-s]

The option -n will result in a dry run and can be used to test the configuration.

The option -s skips the creation of a new snapshot, which can be useful after a successful snapshot followed by a failed or interrupted Borg backup.

The config file has the following format:

# Datetime format used for snapshot and backup suffixes.
datetime_format = '%Y_%m_%d__%H_%M_%S'
# Time origin to discretize time into bins.
time_origin = '2024_01_01__03_55_00'

# To determine which snapshots and backups to keep,
# time is divided into bins of equal width
# Each [[keeps]] section specifies the bin width as "interval".
# The first bin starts at time_origin in every [[keeps]] section.
# Within one bin, only the oldest snapshot (and backup) is retained.
# When there are multiple [[keeps]] sections,
# a spanshot is kept if it matches the criteria of
# at least one of the [[keeps]] sections.

# With the `time_origin` in this example,
# the daily backup corresponds to the snapshot made at 4am

# The 12 most recent 10-minutely snapshots are retained, not backed up.
[[keeps]]
interval = "10 minutes"
amount = 12

# The 28 most recent hourly snapshot are retained, not backed up.
[[keeps]]
interval = "hour"
amount = 48

# The 14 most recent daily snapshot are retained and backed up.
[[keeps]]
backup = true
interval = "day"
amount = 14

# The 52 most recent weekly snapshot are retained and backed up.
[[keeps]]
backup = true
interval = "7 days"
amount = 52

[btrfs]
# Mount point of the Btrfs volume to take snapshot from, must be mounted.
source_path = "/home"
# Prefix for btrfs snapshot volumes, timestamp is appended.
prefix = "snapshots/home."
# Path where the snapshot volumes will be mounted.
snapshot_mnt = "/mnt"
# The snapshot path will be "{snapshot_mnt}/{prefix}{datetime}".
# The snapshots must be stored in the same Btrfs file system as the source.
# In this example, `/mn/snapshots` must be a Btrfs volume
# in the same filesystem as that of the `/home` volume,

# Commands to be executed before making a snapshot: cleanups, mysqldump, etc.
pre = []
# Commands to be executed after making a snapshot: remove dump etc.
post = []
# Note that each command is a string that is executed without a subshell.

[borg]
# Prefix used for Borg archives
prefix = "home."
# List of borg repositories
repositories = ["/mnt/bigdisk", "offsite:/mnt/storage"]
# The archive name is "{repository}::{prefix}{datetime}" for each repository.

# Dictionary with environment variables set for each Borg command, e.g. BORG_CACHE_DIR
env = {}
# Extra arguments for borg create command, e.g. to define exclusions
extra = []
# Paths inside the subvolume to backup.
paths = ["alice", "bob"]

# If you prefer to disable the borg backup, delete the entire borg section
# or leave the list of repositories empty.

Ideally, backups are performed by a dedicated user account with access to user data and backups. Users, whose data is being backed up, should only have read access to the backups. This way, they (or any malware running in their account) cannot damage the backed up data.

Periodic backups

To run BtrUp on a regular basis, you can write a shell script that calls BtrUp with the desired arguments, and then add this shell script to the crontab of the backup account.

A more modern solution is to create a systemd timer and service. The instructions below should be executed as root.

Start by creating a backup user that will run the backup process:

useradd backup -s /sbin/nologin

Install btrup systemwide, as some Linux distributions (using SELinux) will not allow services to execute programs located in home directories.

pip install btrup

Create two files in /etc/systemd/system/:

/etc/systemd/system/btrup.timer:

[Unit]
Description=Periodic backup with BtrUp

[Timer]
# Every hour. See man systemd.timer(5)
OnCalendar=hourly
Persistent=true
Unit=btrup.service

[Install]
WantedBy=default.target

/etc/systemd/system/btrup.service

[Unit]
Description=Periodic backup with BtrUp

[Service]
Type=oneshot
User=backup
ExecStart=/usr/local/bin/btrup /etc/btrup/config.toml
AmbientCapabilities=CAP_DAC_READ_SEARCH

You also need to create a configuration file /etc/btrup/config.toml. (This can be generalized towards multiple configs and a shell script calling btrup for each config.) For security reasons, it is recommended to use absolute paths for all executables, which may be different on your system.

The flag CAP_DAC_READ_SEARCH gives the backup service account global read-access. In addition, you need to configure sudoers to allow the backup user to manage Btrfs subvolumes. This can be accomplished with the following lines in your sudoers configuration:

backup ALL=(ALL) NOPASSWD: /usr/sbin/btrfs subvolume snapshot -r /home *
backup ALL=(ALL) NOPASSWD: /usr/sbin/btrfs subvolume list /home
backup ALL=(ALL) NOPASSWD: /usr/sbin/btrfs subvolume delete /mnt/snapshots/home.*

Make sure you make these commands match your config file and are specific as possible. By making them more specific, you will reduce the risk of accidental wrong subvolume operations. Also ensure that you use visudo to edit the sudoers file, to avoid that mistakes in your sudoers file will lock you out of the root account.

To enable the backup timer and service, run the following as root:

systemctl daemon-reload
systemctl enable --now btrup.timer

To check the state of the timer:

systemctl list-timers

To test the backup without waiting for the timer

systemctl start btrup.service

To check the backup logs:

journalctl -efu btrup.service

Credits

BtrUp is written by Toon Verstraelen. Most of the magic in BtrUp is provided by two great tools:

  • BtrUp assume your data resides in a Btrfs file system volume, which is used by BtrUp to create snapshots.
  • Backups of the snapshots are made with Borg.

Metadata

Release files for btrup 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for btrup 1.0.2
File Size Uploaded
btrup-1.0.2.tar.gz 28.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for btrup 1.0.2
File Interpreter ABI Platform
btrup-1.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 51.0 kB

Release files / btrup-1.0.2.tar.gz

Download URL btrup-1.0.2.tar.gz
Size 28.5 kB
Tags Source
SHA-256 checksum
How to use checksums
6e792a57a84eec9a10d161f8a0343b0e6fb63e2f1d2978d5ce04070262922b38
BLAKE2b-256 checksum
How to use checksums
ba81708cd390f2b4fffeb9c5e43ed6c557f3ae914a31ff84089ad458104acbd6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 30, 2025.

Transparency log

Release files / btrup-1.0.2-py3-none-any.whl

Download URL btrup-1.0.2-py3-none-any.whl
Size 22.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0d45bf73e697d40d394679114b2b5c4a181b429f4f857ed8675aeeb4585ad29d
BLAKE2b-256 checksum
How to use checksums
c22ac8f0f8d8d2882a90121f88bdbeddb657dcbdd549ce53c140152922d5bfc2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 30, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.2 This release

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page