Skip to content

Introduction ​

Backup for Kubernetes clusters with BorgBackup, packaged as one container image and one Helm chart. k8s-borg runs in production on Borg 1.

It backs up the nodes, the cluster's shared data, S3 buckets and databases, each into its own Borg repository. A repository server for Borg 1 and Borg 2 can run in the same release and keep an external copy of its repositories. Optionally, the chart deploys a Borg UI server and runs the backups as its managed agents.

Why k8s-borg ​

  • One release backs up the whole cluster: the nodes' file systems, the cluster datastore (etcd or k3s), dumps of MariaDB, MySQL, PostgreSQL, MongoDB, Redis and Valkey, S3 buckets and shared file systems.
  • Borg, in Kubernetes: deduplication across runs (database dumps stay uncompressed so that it works), encryption on the client, and repositories on any SSH host with Borg, a Hetzner Storage Box included.
  • A repository server in the same release, optional: borg serve over SSH, unprivileged, every client key bound to its own directory with permissions the server enforces, and a copy of all repositories on S3 or SFTP for when its storage fails. The chart's own backup pods need full access, since they prune and compact, and the copy is a mirror, so it protects against lost storage, not against deletions; see External copy.
  • Optional Borg UI integration: the chart deploys the Borg UI server and enrolls the backup pods as its managed agents, with their repositories, check schedules and backup plans; the UI itself is Borg UI.
  • Restores that are tested: every database engine's dump and restore runs against real servers in CI, on every push.

Features ​

  • Node backups: a DaemonSet backs up every node's local file systems, one repository per node, staying on the node's own file systems: mounts below a root (kubelet and CSI volumes, NFS, other partitions) are skipped and named in the pod's log. Before the file backup, optionally:

    • a snapshot of the cluster datastore on control-plane nodes — the etcd of k3s or kubeadm, or k3s's SQLite — with the k3s server token or kubeadm's PKI, held in memory only and removed after the run;
    • consistent copies of SQLite databases on application volumes, made with SQLite's online backup while the application keeps writing.
  • Cluster data backups: a CronJob (or a Borg UI backup plan) backs up shared file systems and user homes, S3 buckets, and dumps of databases.

    • S3 buckets are mounted fresh before each run, and their content is checked against the bucket listing. A bucket whose mount misses objects is mounted again; if objects are still missing, a CronJob run fails (the rest is archived anyway), and a Borg UI backup plan ends with a warning.
    • Database dumps of MariaDB, MySQL and Percona Server 8.4, PostgreSQL (the whole instance, roles included), MongoDB, and Redis and Valkey (RDB snapshots, standalone and cluster). Credentials come from a static config, an existing Secret, or the Secret a database operator created in the database's namespace. The dumps are not compressed, so Borg deduplicates them. restore-* scripts restore a whole instance or a single database; Redis and Valkey have a documented restore procedure.
  • Console pod: a StatefulSet with the Borg tools, the database dumps and the backup sources at hand, for inspection and restores with kubectl exec.

  • Repository server for Borg 1 and Borg 2 (Borg 2 for testing only): sshd and borg serve from the same image, unprivileged, with no capabilities and a read-only root file system.

    • Every client key is confined to its own directory.
    • Permissions per client, enforced by the server: all, no-delete, write-only or read-only (Borg 1: all or append-only).
    • Adding a client is adding a line to authorized_keys.
    • An external copy of the repositories, for the case that the server's storage fails: a CronJob mirrors them to S3, an SFTP target such as a Hetzner Storage Box, or any other rclone remote — read-only, without a lock and without a passphrase.
  • Both Borg majors in one image: borg (1.x) for production, and borg2 (2.x beta, with borgstore) for testing only (see below), chosen per repository. Borg 2 also reaches plain SFTP targets with no Borg on the server.

  • Borg UI integration (optional): the chart deploys the Borg UI server and reconciles its configuration:

    • SQLite or an external PostgreSQL, and Redis (in-cluster or external);
    • OIDC login;
    • notification channels;
    • the license key or an offline entitlement;
    • remote machines.

    Node and cluster pods enroll as managed agents and register their repositories, check schedules and backup plans. Pre/post-backup scripts are published to each agent as an allow-list, so the server can only pick a script by name.

  • Tested: shellcheck, bats unit tests for the wrappers and the chart, and end-to-end suites against the real thing:

    • the whole repository lifecycle, the repository server and its external copy (to S3 and SFTP), with Borg 1 and Borg 2;
    • every database dump and restore against real servers: PostgreSQL 13–18, MariaDB 10.11/11.4/11.8, MySQL and Percona Server 8.4, MongoDB 7.0/8.2, Redis 7/8 and Valkey 8/9, and SQLite under write load.

    All of it runs in CI on every push and pull request.

⚠️ Do not use Borg 2 for real backups. Borg 2 is a beta, and its repository format has changed more than once without a conversion path: a Borg 2 upgrade can leave a repository that the new Borg cannot open. k8s-borg ships Borg 2 so it can be tested; production backups run on Borg 1.

All components (the image, the repository server, and the Borg UI server and agents) follow Borg 2 development as closely as possible, so that Borg 2 can be used in full as soon as a production-ready release is out.

How it compares ​

k8s-borg backs up data (files, databases, the cluster datastore) into Borg repositories. It complements the tools that capture the Kubernetes objects themselves rather than replacing them, and can run next to them:

ToolEngineWhat it is for
Velerovolume snapshots, KopiaKubernetes objects and persistent volumes, restored into a cluster by namespace
K8upresticPersistent volume claims, and application data through pre-backup commands
VolSyncrsync, rclone, resticAsynchronous replication of persistent volumes, between clusters or to object storage
k8s-borgBorgBackupNode file systems, the cluster datastore, database dumps, S3 buckets and shared file systems, each in its own Borg repository; optionally a repository server and Borg UI

Cost ​

k8s-borg has no paid edition and no features held back. Borg UI, which the chart can deploy, has a free community plan and paid plans of its own, and some of its features need a paid plan; see Borg UI. The chart applies a Borg UI license key or offline entitlement if you have one (borgUI.licensing). Standalone backups need no Borg UI at all.

Distributed under the AGPL-3.0-only license.