Compare commits

..
4 Commits
Author SHA256 Message Date
spacefreak bc8287a28c only look for version tags 2026-08-24 01:11:01 +02:00
spacefreak 04d351a859 add WSL section to README.md 2026-08-24 01:07:11 +02:00
spacefreak d5d4ea2430 change README.md 2026-08-24 00:53:49 +02:00
spacefreak e423b97c8a add README.md 2026-08-24 00:40:09 +02:00
2 changed files with 273 additions and 1 deletions
+272
View File
@@ -0,0 +1,272 @@
# pbc
A Bash wrapper around Proxmox's `proxmox-backup-client` for Proxmox Backup Server (PBS).
It keeps the repository, credentials and the list of paths to back up in a single config
file, injects the configured namespace (`--ns`) into every command that needs it, and lets
you pick a snapshot group, snapshot and archive from an interactive menu instead of typing
them out. Any command it does not implement itself is passed straight through to
`proxmox-backup-client`.
## Requirements
- A Debian-based host with `bash` and `curl`
- `jq` and `proxmox-backup-client` — both installed by `pbc install`
- A reachable PBS with a datastore and an API token
## Installation
```bash
curl -fsSL https://git.ccc-rheintal.ch/spacefreak/pbc/raw/branch/master/install.sh | sudo bash
```
This installs the newest tagged version to `/usr/local/bin/pbc`, installs the
dependencies, and puts an example config at `/etc/pbc/config.example`.
### Updating
Run the same command again to update `pbc` itself:
```bash
curl -fsSL https://git.ccc-rheintal.ch/spacefreak/pbc/raw/branch/master/install.sh | sudo bash
```
It fetches the newest tag and overwrites `/usr/local/bin/pbc`. Your `/etc/pbc/config` is
left untouched.
To update only the `proxmox-backup-client` binary:
```bash
sudo pbc install --update
```
This is skipped when the client is managed by the package manager — use `apt` in that case.
## Configuration
```bash
sudo cp /etc/pbc/config.example /etc/pbc/config
sudo chmod 640 /etc/pbc/config
sudo editor /etc/pbc/config
```
```bash
PBS_SERVER='pbs.domain.tld:8007'
PBS_USER='backup@pam:token-name'
PBS_PASSWORD='XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
PBS_DATASTORE='backup'
PBS_NAMESPACE='MyBackups'
BACKUP=('root.pxar:/' 'data.pxar:/mnt/data')
```
| Key | Description |
|---|---|
| `PBS_SERVER` | PBS host and port |
| `PBS_USER` | User and API token name, `user@realm:token-name` |
| `PBS_PASSWORD` | The API token secret |
| `PBS_DATASTORE` | Datastore to back up to |
| `PBS_NAMESPACE` | Namespace inside the datastore |
| `BACKUP` | Array of `archive-name.pxar:/path` entries to back up |
| `PBC` | Optional, path to `proxmox-backup-client` (default `/usr/local/bin/proxmox-backup-client`) |
`pbc` never creates the config for you — copy the example and edit it yourself. The file
has to be readable for the user that runs `pbc`, so if that is not root, hand
it over:
```bash
sudo chown youruser /etc/pbc/config
```
The config is sourced as Bash and contains your token secret. `pbc` refuses to run if the
file is writable by group or others, and warns if it is readable by others.
Check that it works:
```bash
pbc list
```
Use a different config with `-c`:
```bash
pbc -c ./myconfig list
```
## Usage
Run `pbc --help` for the synopsis and the list of options, or `pbc -H` to additionally
list every command of `proxmox-backup-client`. The commands `pbc` implements itself are
described below.
### backup
Back up everything listed in `BACKUP` and print the client output:
```bash
pbc backup
```
Extra arguments are forwarded to the client:
```bash
pbc backup --exclude '/mnt/data/cache'
```
### backup-cron
Same backup, but output is suppressed unless it fails — so cron only mails you on errors:
```cron
0 3 * * * /usr/local/bin/pbc backup-cron
```
### list
List backup groups with their latest snapshot and archives:
```bash
pbc list
```
### snapshot-list
List snapshots of a group with sizes and archives. Without an argument you get a menu of
the available groups:
```bash
pbc snapshot-list
pbc snapshot-list host/myhost
```
### mount
Mount a single archive of a snapshot on a local directory. Anything you leave out is
asked for interactively:
```bash
pbc mount
pbc mount host/myhost/2026-08-24T01:00:00Z root.pxar /mnt/restore
```
The target directory is not created for you — create it beforehand and make sure it is
writable for the user that runs `pbc`. Unmount when done:
```bash
umount /mnt/restore
```
### catalog-shell
Open an interactive shell to browse an archive and restore selected files — see below.
```bash
pbc catalog-shell
pbc catalog-shell host/myhost/2026-08-24T01:00:00Z root.pxar
```
### install
Install or update the dependencies. Requires root:
```bash
sudo pbc install
sudo pbc install --update
```
## Restoring files
### Selectively, with `catalog-shell`
```bash
pbc catalog-shell
```
Pick a snapshot and an archive from the menu, then browse the archive as if it were a
filesystem and mark what you want back:
```
pxar:/ > cd etc
pxar:/etc > ls
pxar:/etc > select hosts
pxar:/etc > find etc/nginx/** --select
pxar:/etc > list-selected
pxar:/etc > restore-selected /mnt/restore
pxar:/etc > exit
```
- `ls`, `cd`, `pwd`, `stat` — browse the archive
- `select <path>` — mark a single file, relative to the current directory
- `find <pattern> --select` — mark everything matching a glob
- `deselect <path>` / `clear-selected` — drop one entry or all of them
- `list-selected` — show what is currently marked
- `restore-selected <target>` — restore only the marked entries
- `restore <target> [pattern]` — restore the current directory and everything below it
- `help` — list all available shell commands
- `exit` — leave the shell
#### Selecting a directory and everything beneath it
`select` marks exactly the entry you give it and nothing else. Marking a directory
therefore restores an empty directory — its contents are *not* included. Use a glob with
`find --select` instead:
```
pxar:/ > find etc/nginx/** --select
pxar:/ > select etc/nginx
pxar:/ > restore-selected /mnt/restore
```
The `find` line picks up everything below `etc/nginx`; the `select` line adds the
directory itself, so its own permissions and ownership are restored as well.
Note that `find` patterns are always matched against paths relative to the archive root,
no matter which directory you are in, and that `find` scans the whole archive — expect it
to take a while on large backups.
The target path must not exist yet; the restore creates it. Its parent directory has to be
writable for the user that runs `pbc`.
### Everything, or with normal tools
To copy files out with `cp`, `rsync` or a file manager, mount the archive instead:
```bash
mkdir -p /mnt/restore # must exist and be writable for the user running pbc
pbc mount
cp -a /mnt/restore/etc/nginx /etc/nginx
umount /mnt/restore
```
## Running in WSL
`pbc` also works inside a Debian-based WSL instance, which makes it a way to back up
Windows directories: the Windows drives show up under `/mnt`, so they can be listed in
`BACKUP` like any other path.
```bash
BACKUP=('users.pxar:/mnt/c/Users' 'projects.pxar:/mnt/d/projects')
```
A few things to keep in mind:
- Access to files under `/mnt` is governed by Windows, not by the user you are inside the
WSL instance — `sudo` does not help there. To back up paths your Windows user cannot
read, start the WSL instance itself as administrator (run the terminal or `wsl.exe` via
*Run as administrator*), then run `pbc` in it.
- The owner and permission metadata stored in the archive is the one WSL synthesizes for
Windows files, not the original Windows ACLs.
- Cron is not running in a WSL instance by default, so `backup-cron` only fires if you
enable it — either by starting `cron` yourself, or by triggering `pbc backup-cron` from
the Windows Task Scheduler with `wsl.exe`:
```
wsl.exe -d Debian -u root /usr/local/bin/pbc backup-cron
```
For the reason above, such a task has to run with highest privileges to reach files that
are not accessible to your Windows user.
## License
GPLv3 — see [LICENSE](LICENSE).
+1 -1
View File
@@ -26,7 +26,7 @@ function download_files() {
done done
} }
GIT_TAG=$(curl -fsSL "$API_ENDPOINT/tags" | grep -o '{"name":"[^"]*"' | sed 's/^{"name":"//;s/"$//' | sort --version-sort | tail -n 1) GIT_TAG=$(curl -fsSL "$API_ENDPOINT/tags" | grep -o '{"name":"[^"]*"' | sed 's/^{"name":"//;s/"$//' | grep -E '^v[0-9]+' | sort --version-sort | tail -n 1)
if [ -n "$GIT_TAG" ]; then if [ -n "$GIT_TAG" ]; then
echo "Info: installing pbc version $GIT_TAG" echo "Info: installing pbc version $GIT_TAG"
else else