A companion CLI for Runtipi: multi-remote rclone backups with per-remote retention, restores, app/core updates, VPS security hardening, and Tailscale setup.
Project description
runtipi-companion
A single Python CLI that extends and replaces the shell scripts scattered
across Runtipi's docs (auto-backup, VPS security, server hardening) with one
tool: runtipi-companion.
Built on top of, not instead of, runtipi-cli -- every action that
Runtipi's own CLI already does well (start/stop, app update, appstore
update) is a thin wrapper around it. What this tool adds:
- Backups to multiple rclone remotes, each with its own retention. The official auto-backup-apps script only writes to local disk with one shared retention policy. This keeps that local behavior but adds any number of rclone remotes (B2, S3, Google Drive, SFTP, ...), each syncing independently with its own daily/weekly/monthly/yearly retention.
- Restore from local disk or any configured remote, symmetric with the backup format.
- Updates for individual apps, all apps (with an exclude list), app stores, and Runtipi core itself.
- A config wizard that runs on first start (or via
config wizard): interviews you in the terminal and writes a validated config file. - A setup wizard for a fresh box: clone/verify the Runtipi install,
locate
runtipi-cli(it isn't on$PATHby default), runprepare/start, create backup directories, sanity-check rclone remotes. - Security hardening that applies the checklists from VPS security and Server hardening: SSH key-only + no root login, UFW, fail2ban, and an optional tailscale-only lockdown (see below). Pick what to apply from an interactive menu, or target items individually with flags.
- Tailscale install +
up, matching the VPN-only access pattern the VPS security guide recommends (don't open 80/443 publicly).security harden --tailscale-securitygoes further, based on this UFW+tailscale writeup: it switches SSH totailscale sshand restricts UFW to allow inbound traffic only on thetailscale0interface, so the box is unreachable from the public internet entirely (only tailscale's own coordination port stays open). - One global YAML config for all of the above.
Safety model
Every command that changes system state (backups stop/start containers,
security hardening edits sshd_config/firewall/installs packages, restore
overwrites app data) defaults to --dry-run, which only prints what it
would do. Add --apply to actually execute. SSH hardening additionally
refuses to disable password authentication unless it finds an existing
authorized_keys for root/the current user/$SUDO_USER (override with
--force if you know what you're doing), and validates sshd_config with
sshd -t before restarting -- rolling back automatically if the config is
broken, so you can't lock yourself out of a remote box.
Install
python3 -m venv /opt/runtipi-companion-venv
/opt/runtipi-companion-venv/bin/pip install /path/to/runtipi-companion
sudo ln -s /opt/runtipi-companion-venv/bin/runtipi-companion /usr/local/bin/runtipi-companion
Or, for local development:
pip install -e ".[dev]"
pytest
The end-to-end suite (tests/e2e/e2e.sh) exercises real rclone/tailscale
binaries and applies changes for real, so run it in the provided Docker
image rather than on your own machine:
docker build -f tests/e2e/Dockerfile -t runtipi-companion-e2e .
docker run --rm runtipi-companion-e2e
Requires rclone on $PATH for remote backups, and tailscale's installer
handles its own binary. runtipi-cli does not need to be on $PATH --
runtipi-companion looks for it at <runtipi.path>/runtipi-cli automatically
(set runtipi.cli_path in the config if yours lives elsewhere).
Quickstart
On a first run (no config file anywhere), any command offers to launch the interactive config wizard, which interviews you and writes a validated config file. You can also start it explicitly:
runtipi-companion config wizard
Or go the manual route:
runtipi-companion config init --path ~/.config/runtipi-companion/config.yaml
$EDITOR ~/.config/runtipi-companion/config.yaml # set runtipi.path, add rclone remotes
Then bootstrap the system and take a first backup:
runtipi-companion setup wizard --apply
runtipi-companion backup run --type daily --apply
runtipi-companion backup list jellyfin
runtipi-companion security harden # interactive: pick what to harden
runtipi-companion security harden --all --apply # or apply everything at once
runtipi-companion security harden --tailscale-security --apply # VPN-only lockdown only
runtipi-companion tailscale install --apply
Configuration
See runtipi-companion.example.yaml for
every field with comments. Config is searched at /etc/runtipi-companion/config.yaml
then ~/.config/runtipi-companion/config.yaml, or pass --config /path.
The part that matters most for the "individual retention per remote" ask:
backup:
schedules: # local disk retention
daily: { retention: 7 }
weekly: { retention: 4 }
remotes:
- name: backblaze
rclone_remote: "b2-runtipi:my-bucket/runtipi-backups"
schedules: # THIS remote's own retention, independent of local
daily: { retention: 14 }
monthly: { retention: 12 }
- name: gdrive
rclone_remote: "gdrive:runtipi-backups"
schedules:
weekly: { retention: 4 } # only syncs weekly backups, keeps 4
A remote only receives backups for schedules it explicitly lists, and prunes itself independently of local disk and every other remote.
Commands
runtipi-companion config wizard|init|show|validate
runtipi-companion backup run|list|remotes
runtipi-companion restore run|list
runtipi-companion update apps|core|appstores
runtipi-companion setup wizard
runtipi-companion security harden|status
runtipi-companion tailscale install|status
Run runtipi-companion <group> <command> --help for full options.
Three commands have interactive TUI modes:
backup remotes— add/edit/remove/enable/disable rclone backup remotes in your config file from a menu, including per-schedule retention. Changes are validated before saving.restore runwith no arguments — pick the source (local disk or any configured remote), then the app, then the archive from a list of what actually exists, instead of typing filenames. Defaults to a dry-run preview; add--applyto restore for real.security hardenwith no flags (or--interactive/-i) — checklist menu to pick any combination of SSH / UFW / fail2ban / tailscale-only lockdown. Flags (--ssh,--ufw,--fail2ban,--tailscale-security,--all) still work for scripting and skip the menu.
Automating backups
Either cron (matches the original guide):
sudo crontab -e
0 2 * * 2-7 /usr/local/bin/runtipi-companion backup run --type daily --apply
0 2 * * 1 /usr/local/bin/runtipi-companion backup run --type weekly --apply
0 3 1 * * /usr/local/bin/runtipi-companion backup run --type monthly --apply
Or the systemd timers in systemd/, which add logging via
journalctl and Persistent=true (a backup missed because the box was off
runs as soon as it's back).
Restore
runtipi-companion restore list jellyfin --remote backblaze
runtipi-companion restore run jellyfin jellyfin-daily-2026-07-01.tar.gz --from-remote backblaze --apply
Omit --from-remote to restore from the local backup directory instead.
Limitations / things to know
- The backup format is this tool's own (tar.gz of
app/app-data/user-config, same layout as the original bash script), notruntipi-cli app backup's native format -- they aren't interchangeable.runtipi-cli app backupis still exposed via theRuntipiCLIwrapper for scripting if you want it. - Remote pruning parses
rclone lsf -Routput; very unusual remote path layouts (colons or the schedule name embedded oddly in a path segment) could confuse the app-name grouping. Test with--dry-runafter your first real sync to a new remote before trusting the retention prune. - Security hardening covers the concrete steps from Runtipi's own docs (SSH keys, UFW, fail2ban). It's not a substitute for reading those docs once yourself.
Ideas for later (not built yet)
Things worth considering if this becomes your daily driver:
- Backup encryption (age or gpg) before upload, since remotes are third-party cloud storage.
- Pre-update snapshot: auto-run a backup immediately before
update apps/update coreso every update is trivially reversible. doctorcommand: one-shot audit against the VPS-security checklist (report pass/fail instead of applying changes).- Backup integrity verification:
tar -t/ checksum each archive right after creation and fail loudly instead of discovering a corrupt backup at restore time. --jsonoutput on every command for monitoring integration.- Fleet mode: point one config at multiple Runtipi hosts (e.g. via SSH) for centralized backup/update status across boxes.
- Self-update:
runtipi-companion self-updateto pull new pip releases. - Richer notifications (ntfy priority levels, per-event webhook payloads) beyond the current single generic webhook.
Happy to build any of these next -- say the word.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file runtipi_companion-1.0.1.tar.gz.
File metadata
- Download URL: runtipi_companion-1.0.1.tar.gz
- Upload date:
- Size: 41.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5308b9ec118ffef5822e99356a68a246bdaf198ac24bb9009f417a8472e0463a
|
|
| MD5 |
488b24f04107a2d3aaa725b5ca14b4d5
|
|
| BLAKE2b-256 |
e3669cfccb14a8227cfdfc6cad6da4ccc6095283d0e7380fb9b23094adeb81f4
|
Provenance
The following attestation bundles were made for runtipi_companion-1.0.1.tar.gz:
Publisher:
release.yml on EckPhi/runtipi-companion
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
runtipi_companion-1.0.1.tar.gz -
Subject digest:
5308b9ec118ffef5822e99356a68a246bdaf198ac24bb9009f417a8472e0463a - Sigstore transparency entry: 2173176752
- Sigstore integration time:
-
Permalink:
EckPhi/runtipi-companion@8d0976006f226791b2210872b68dba6473db8d67 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/EckPhi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8d0976006f226791b2210872b68dba6473db8d67 -
Trigger Event:
push
-
Statement type:
File details
Details for the file runtipi_companion-1.0.1-py3-none-any.whl.
File metadata
- Download URL: runtipi_companion-1.0.1-py3-none-any.whl
- Upload date:
- Size: 38.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d6d6dc9c59864333dc53da84c47434208f645cec9b13977c9630b3ba62f9ac62
|
|
| MD5 |
99d6dfb85f299e4c5d82a819ea93748e
|
|
| BLAKE2b-256 |
c6d9c0e2f1446482a63e9166291e2deb4e483126e26568b1257c9d34c44d135c
|
Provenance
The following attestation bundles were made for runtipi_companion-1.0.1-py3-none-any.whl:
Publisher:
release.yml on EckPhi/runtipi-companion
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
runtipi_companion-1.0.1-py3-none-any.whl -
Subject digest:
d6d6dc9c59864333dc53da84c47434208f645cec9b13977c9630b3ba62f9ac62 - Sigstore transparency entry: 2173176764
- Sigstore integration time:
-
Permalink:
EckPhi/runtipi-companion@8d0976006f226791b2210872b68dba6473db8d67 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/EckPhi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8d0976006f226791b2210872b68dba6473db8d67 -
Trigger Event:
push
-
Statement type: