Skip to content

Repository files navigation

Proxmox Backup Server for NixOS (experimental)

Build and push to Cachix Proxmox Backup Server running natively on NixOS

NixOS PR: NixOS/nixpkgs#543254

A native Nix package and NixOS module for Proxmox Backup Server (PBS). No Debian container, no dpkg runtime, just a regular Nix derivation and a systemd-based service.

The package builds PBS 4.2.4 from the upstream Proxmox git sources, reusing the same source set and Cargo lock basis as the nixpkgs proxmox-backup-client package. The web UI assets are pulled from the official Proxmox .deb archives and stitched together at build time.

Important

Feedback wanted. This port works for me, but it needs more eyes. I'm filing a tracking issue per untested feature. If you run PBS on NixOS with this, please try them out and report back (👍/👎, logs, edge cases) on the relevant issue, or open a new one. Real-world reports are the fastest way to move this from "experimental" to "trusted."

Warning

This is an experimental, unofficial port. It is not supported by Proxmox Server Solutions GmbH. Do not contact Proxmox support for issues with this packaging. The web UI carries a banner saying as much. See Status & limitations before relying on it for anything you care about.

Todo

  • NixOS VM based Tests
  • Real World Testing
  • Gather Feedback
  • Wait on The Proxmox Trademark Situation
  • Upstream to Nixpkgs

What you get

  • pkgs.proxmox-backup-server: the base build (binaries proxmox-backup-api, proxmox-backup-proxy, proxmox-backup-manager, proxmox-tape, pmt, pmtx, the debug/migration helpers, the bundled web UI, and shell completions). See How it works for why this isn't run directly.
  • pkgs.proxmox-backup-server-fhs: the runnable package, the base wrapped in an FHS environment. This is what the module and the CLIs use.
  • services.proxmox-backup-server: a NixOS module that creates the proxmox-backup-server user/group, the runtime/state/cache/log directory tree, PAM auth integration, and the proxmox-backup / proxmox-backup-proxy systemd units plus the daily-update timer.

Requirements

  • Nix with flakes enabled.
  • A x86_64-linux or aarch64-linux host. The bundled web UI .deb assets are amd64/all, and PBS itself is Linux-only.

Binary cache

Prebuilt packages are pushed to Cachix from GitHub Actions. To use the cache, add this to your Nix configuration:

{
  nix.settings = {
    substituters = [ "https://awildleon-nixos-pbs.cachix.org" ];
    trusted-public-keys = [
      "awildleon-nixos-pbs.cachix.org-1:4kEEBSONGJ0F7Ita/3ZRcTWaR6M7YHXhltJaoEYl3ew="
    ];
  };
}

Or for a single command:

nix build .#proxmox-backup-server-fhs \
  --extra-substituters https://awildleon-nixos-pbs.cachix.org \
  --extra-trusted-public-keys awildleon-nixos-pbs.cachix.org-1:4kEEBSONGJ0F7Ita/3ZRcTWaR6M7YHXhltJaoEYl3ew=

Build

nix build .#proxmox-backup-server-fhs   # runnable, FHS-wrapped
nix build .#proxmox-backup-server       # base build only

The -fhs result contains the FHS-wrapped CLI launchers under bin/ and the daemon launchers under libexec/proxmox-backup/. The base result holds the raw binaries and assets in a normal Nix layout (bin/, lib/<multiarch>/proxmox-backup/, share/).

Try it in a VM

The flake ships a throwaway pbs-test-vm NixOS configuration so you can poke at a running instance without touching your host.

Build and run it:

./scripts/run-vm.sh

This builds the VM and launches it with all runtime state pinned to ./.vm-state/ (gitignored), so the system disk and the secondary datastore disk (/dev/vdb, a blank 20 GiB image) persist across reboots. Delete .vm-state/ to start fresh.

Running the generated run-pbs-test-vm script directly also works, but it stores the secondary disk in a throwaway /tmp/nix-vm.* dir that is recreated blank on every boot — use scripts/run-vm.sh if you want the disk to stick around.

For an auto-rebuilding clean-slate dev loop, use ./scripts/dev-loop.sh.

Then access:

Service Address
PBS web / API https://localhost:8007/ (self-signed cert)
SSH ssh -p 2222 root@localhost

The VM's root password is nixos. Log in to the web UI with user root@pam and that same password.

Use it on a real host

With flakes

Add the flake as an input and import the module:

{
  inputs.pbs.url = "github:AWildLeon/nixos-pbs"; # or path:/... for local work

  outputs = { self, nixpkgs, pbs, ... }: {
    nixosConfigurations.host = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        pbs.nixosModules.proxmox-backup-server
        ({ pkgs, ... }: {
          nixpkgs.overlays = [ pbs.overlays.default ];

          services.proxmox-backup-server = {
            enable = true;
            openFirewall = true;
          };
        })
      ];
    };
  };
}

Without flakes

The repo ships a default.nix / shell.nix that re-export the same outputs via NixOS/flake-compat, so you don't need flakes enabled. Pin the repo with fetchTarball (or a channel / niv / a vendored checkout) and use the overlays.default and nixosModules.proxmox-backup-server attributes in your configuration.nix:

{ pkgs, ... }:
let
  pbs = import (fetchTarball {
    url = "https://github.com/AWildLeon/nixos-pbs/archive/main.tar.gz";
    # sha256 = lib.fakeSha256; # pin this for reproducibility
  });
in
{
  imports = [ pbs.nixosModules.proxmox-backup-server ];

  nixpkgs.overlays = [ pbs.overlays.default ];

  services.proxmox-backup-server = {
    enable = true;
    openFirewall = true;
  };
}

To just build the package without flakes: nix-build (produces ./result).

After a rebuild, set things up from the CLI (the web login uses PAM, so root@pam with the host's root password works too):

proxmox-backup-manager user list
# then create a datastore, e.g. (or do it via the UI)
proxmox-backup-manager datastore create main /var/lib/proxmox-backup/datastores/main

Module options

Option Type Default Description
services.proxmox-backup-server.enable bool false Enable the Proxmox Backup Server service.
services.proxmox-backup-server.package package pkgs.proxmox-backup-server-fhs The PBS package to use.
services.proxmox-backup-server.openFirewall bool false Open TCP port 8007 for the web/API proxy.
services.proxmox-backup-server.sslCertificate nullOr path null PEM cert to serve; null uses PBS's self-signed cert.
services.proxmox-backup-server.sslCertificateKey nullOr path null PEM private key matching sslCertificate.
services.proxmox-backup-server.ensureDatastores attrsOf (submodule) { } Datastores to create/update on activation.
services.proxmox-backup-server.ensurePruneJobs attrsOf (submodule) { } Prune jobs to create/update on activation.
services.proxmox-backup-server.ensureVerifyJobs attrsOf (submodule) { } Verification jobs to create/update on activation.
services.proxmox-backup-server.ensureSyncJobs attrsOf (submodule) { } Sync jobs to create/update on activation.

Custom TLS certificate

By default PBS serves a self-signed certificate it generates on first start. To use your own, point both options at PEM files:

services.proxmox-backup-server = {
  sslCertificate = "/run/secrets/pbs/cert.pem";    # cert (chain)
  sslCertificateKey = config.age.secrets.pbsKey.path; # matching private key
};

The files are copied (not symlinked) into /etc/proxmox-backup/proxy.{pem,key} before the daemons start, so PBS uses them instead of generating a self-signed cert; unset either option and it falls back to the self-signed one. Keep the key out of the world-readable Nix store — reference a path from your secrets tooling (agenix/sops-nix). When the source files change, the daemons restart and re-copy them (so a renewed cert is picked up on the next nixos-rebuild switch).

Declarative datastores and jobs

Datastores and prune/verify/sync jobs can be declared in Nix instead of clicking through the UI. On activation a proxmox-backup-setup oneshot service reconciles them through proxmox-backup-manager: each declared resource is created if missing and updated to match otherwise. Anything you create in the GUI but do not declare here is left untouched (no deletion).

services.proxmox-backup-server = {
  enable = true;
  openFirewall = true;

  ensureDatastores.main = {
    path = "/mnt/backup/main";   # parent must exist (e.g. a `fileSystems` mount)
    comment = "Primary store";
    gcSchedule = "daily";
  };

  ensurePruneJobs.main-prune = {
    datastore = "main";
    schedule = "daily";
    settings = { keep-daily = 7; keep-weekly = 4; };
  };

  ensureVerifyJobs.main-verify = {
    datastore = "main";
    schedule = "sun 02:00";
  };
};

Each submodule exposes a few first-class options (path/datastore, comment, gcSchedule, schedule, disable) plus a freeform settings attrset for any other PBS flag — keys use the kebab-case spelling PBS expects (e.g. keep-daily) and map to --<key> <value>. The datastore path is the host path; remember a datastore on the bare root / needs the /hostsys prefix (see Datastore paths). Remotes are not managed yet, so a remote-backed sync job needs its remote created in the GUI first.

How it works

PBS is built as a normal rustPlatform.buildRustPackage derivation from the upstream git repos (proxmox-backup, proxmox, proxmox-fuse, pxar, pathpatterns), with a small cargo patch to re-route dependencies that aren't on crates.io. The web UI is assembled in postPatch/postInstall: the official .deb assets are unpacked, the ExtJS bundle is concatenated from the upstream Makefile's file list, and UI features that don't apply to a NixOS host (xterm.js shell, APT updates/repositories, disk/ZFS management, network/time config, reboot/shutdown buttons) are trimmed out.

PBS hardcodes Debian-style FHS paths at runtime (/usr/share/javascript/proxmox-backup, /usr/lib/<multiarch>/proxmox-backup, /usr/bin/ip, ...), so packaging is split in two, the way nixpkgs handles FHS-assuming software (compare steam / steam-run):

  • proxmox-backup-server is the base build. It lays the binaries and web assets out in a normal Nix layout ($out/{bin,lib,share}); it is not run directly.
  • proxmox-backup-server-fhs wraps each binary in a buildFHSEnv. buildFHSEnv maps the base package's $out/{bin,lib,share} onto /usr, giving PBS the paths it expects, bind-mounts /run and /var from the host, and provides a private /etc that already links the host's passwd/group/shadow/pam.d/ssl (so PAM and TLS work); the writable config dir /etc/proxmox-backup is bound in on top.

The systemd units and the CLIs on $PATH all come from proxmox-backup-server-fhs, so the NixOS module carries no bind-mount plumbing of its own.

Datastore paths

The daemons run inside the FHS sandbox, but buildFHSEnv automatically bind-mounts every top-level host directory into the sandbox at the same path (recursively, so submounts like a ZFS pool or a dedicated disk come along). So datastores on the usual locations work out of the box at their normal host paths — no prefix or extra config needed:

  • /var/... (e.g. the default /var/lib/proxmox-backup/datastores/...)
  • /mnt/..., /media/..., /srv/..., /opt/...
  • /home/..., /root/..., /boot/..., /tmp/...
  • any custom top-level mount, e.g. a ZFS pool at /tank/... or a disk mounted at /data/...

The only top-level names that are not bridged are the ones the sandbox owns or ignores: /usr, /bin, /sbin, /lib, /lib32, /lib64, /libexec, /nix, /dev, /proc, and /etc — you would not site a datastore in any of those anyway.

The one path that does not work is the bare filesystem root /: the sandbox / is a throwaway namespace root, so a datastore created directly at / is written there and silently vanishes on the next request. For that case the wrapper also binds the real host root in at /hostsys, so you can address the top of the host filesystem explicitly:

proxmox-backup-manager datastore create root /hostsys/subdir

Note

Binds are set up when the daemons start, so a filesystem mounted after the PBS services are already running won't be visible until you restart them. Declarative fileSystems mounts (mounted at boot, before PBS starts) are fine.

Warning

/hostsys exposes the entire host filesystem (read-write) to the PBS daemons, and proxmox-backup-api runs as root.

Repository layout

flake.nix                              # packages, overlay, NixOS module, test VM
default.nix / shell.nix                # non-flake entry points (via flake-compat)
overlay.nix                            # nixpkgs overlay, shared by both entry points
modules/proxmox-backup-server.nix      # the services.proxmox-backup-server module
pkgs/proxmox-backup-server/
  package.nix                          # base build (orchestration only)
  sources.nix                          # pinned upstream git + .deb sources
  www/                                 # web UI replacement files + transform scripts
  Cargo.lock                           # pinned Rust dependency lock
  0001-cargo-re-route-...patch         # cargo source re-routing patch
pkgs/proxmox-backup-server-fhs/
  package.nix                          # buildFHSEnv wrapper around the base build

Status & limitations

The package builds and the core services run, but the following still need testing or are known to be incomplete. Each has a tracking issue. Please add your results there:

  • Runtime service behavior under real backup/restore/sync/GC workloads.
  • Generated docs and manpages.
  • PAM / authentication integration beyond basic root@pam login.
  • Tape backup is untested and likely does not work. The tape tooling (proxmox-tape, pmt, pmtx, sg-tape-cmd) is built and the UI is left in place, but it has not been exercised on this port and is not expected to work as-is. If you need tape, please open an issue so it can be looked at.
  • Assumptions that only hold on Debian-style systems.

Contributions and bug reports are welcome.

License

The packaging in this repository (the Nix expressions, module, and docs) is licensed under the MIT license. See LICENSE.

Note that the software it builds, Proxmox Backup Server, remains licensed under the AGPL-3.0-only by its authors; the MIT license here does not relicense it. Proxmox® is a registered trademark of Proxmox Server Solutions GmbH. This is an independent, unofficial packaging effort and is not affiliated with or endorsed by Proxmox.

Note

Phrasing borrowed from nixpkgs: the MIT license does not apply to the packages built by this repo, merely to the files in this repository (the Nix expressions, build scripts, NixOS module, etc.). It also might not apply to patches included here, which may be derivative works of the packages to which they apply. Those artifacts are covered by the licenses of the respective packages.

End Goal

The end goal of this project is to get a stable Proxmox Backup Server (with module) upstreamed into NixOS/nixpkgs.

Notes

Large parts of this were written with Claude Code. I've reviewed everything, corrected where needed, and understand every line of it. That said: there may be dragons.

About

Proxmox Backup Server on NixOS (Experimental)

Resources

Stars

7 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages