Install Guide
A step-by-step walkthrough for standing up a BackupBloc server and attaching Linux or macOS clients to it. The entire flow is driven by two scripts — install-server.sh and install-agent.sh (or install-agent-mac.sh) — and takes roughly ten minutes end-to-end.
rocket_launchClient quick start — back up your server
Everything you need to protect your first server. Your provider has already created your account and set your storage allowance — you handle the rest yourself, in about five minutes.
This section is written for you, the client. Your provider can hand you this page as-is. You do not need admin access to anything — you'll log in with the username and password they gave you.
Before you start
- The panel URL, username and password your provider sent you.
- root / sudo access to the server (or laptop/PC) you want to back up.
- The network ports below open so your server can reach the backup server.
Ports to open
All the connections your server needs are outbound — from your server to the backup server. If you run a strict egress firewall (CSF, iptables, a cloud security group, etc.), allow the following. Most setups permit outbound traffic by default, in which case there's nothing to change.
| Port | Protocol | Direction | What it's for | Required? |
|---|---|---|---|---|
443 | TCP | Outbound → backup server | Control channel — registration, licence check, heartbeats, on-demand backup triggers, and agent updates (the panel/API). | Yes |
8000 | TCP | Outbound → backup server | Backup data — the restic storage server where your snapshots are written and read. 8000 is the default; confirm the exact port with your provider, as some serve this over 443. | Yes |
443 | TCP | Outbound → download host | One-time only: fetching the installer script during setup. | Install only |
22 (or custom) | TCP | Inbound ← backup server | Optional. Only if your provider uses SSH to push ad-hoc backups. Scheduled and dashboard "Backup Now" runs do not need it. | Optional |
No inbound ports are required on your server for normal operation — the agent reaches out to the backup server on a schedule and polls for jobs. You only need the optional inbound SSH rule if your provider specifically told you they use SSH-based backups.
1 · Log in and find your dashboard
Open the panel and sign in
Go to the panel URL and log in with the credentials you were given. You'll land on your Dashboard — a view scoped entirely to your own account.
Read your dashboard
The dashboard shows three things at a glance:
- Account Storage — how much backup space you've used against your plan (e.g. 0% · 0 GB of 500 GB before your first backup).
- Your Servers — how many servers you've enrolled versus how many your plan allows (e.g. 0 / 3).
- Snapshots and Backed Up Data — filled in once backups start running.
2 · Add a server (get your install key)
Click "Add a Server"
On the dashboard (or the Your Servers page), click Add a Server. A Client Access Key is generated instantly — it looks like BB-XXXXXXXX-XXXXXXXX.
Copy the key
Copy the key from the pop-up. Each key enrols one server. If you have several servers, add one for each — they all count against your plan and share your storage allowance. A key you haven't used yet shows under Waiting for install, where you can copy it again or remove it to free the slot.
Hit your server limit? "Add a Server" is disabled once you've reached the number of servers on your plan. Remove an unused key to free a slot, or ask your provider to raise your limit.
3 · Install the agent on your server
SSH into your server as root (or use sudo) and run the one-line installer. It installs everything it needs — you don't need to install restic or anything else first.
sudo bash <(curl -fsSL https://backupbloc.com/downloads/agent/install-agent.sh | tr -d '\r')
sudo bash <(curl -fsSL https://backupbloc.com/downloads/agent/install-agent-mac.sh | tr -d '\r')
Download the Windows installer your provider gave you and run it as Administrator. Fill in the panel URL, choose Client access key, and paste your key. See the Windows agent section for the full wizard.
The installer asks a few questions. The important ones:
| Prompt | What to enter |
|---|---|
| Server hostname / Admin UI URL | The backup server address your provider gave you. |
| License type | Choose 1. Paste your BB-… Client Access Key when prompted. |
| Repository name | Give each server its own name — e.g. server1, web-01, or the machine's hostname. Don't leave it as default if you have more than one server. See the note below. |
| What to back up | Leave blank to back up the whole server, or enter specific paths (e.g. /var/www /home). |
| Schedule | How often to back up — daily is a good default. |
| Repo encryption password | Leave blank to auto-generate. Keep a copy of the value it prints somewhere safe — it protects your encrypted data. Each repository has its own password, so save one per server. |
The installer currently labels the key option as "Personal license key" — that's the same thing as your Client Access Key. Choose option 1 and paste your BB-… key.
Use a separate repository per server. When you have several servers, give each one a distinct Repository name instead of default. This keeps each server's backups in their own clearly-labelled repository — easy to find on the Repositories and Snapshots pages — and makes your storage usage report correctly per server. For example, back up four servers into server1, server2, server3, and server4 (or use each machine's hostname).
Remember each repository has its own encryption password — keep a copy of each. You may reuse the same passphrase across them if you prefer, but they are still separate repositories.
4 · Verify your first backup — and restoring
When the installer finishes it registers your server and kicks off the first backup automatically. Your server appears on your dashboard within a minute or two, and the first snapshot shows up under Snapshots. Your Account Storage will start to climb as data is stored.
To restore a file later: open Snapshots, pick the snapshot from the right date, browse into it, and download the file or folder you need. To back up on demand before making a risky change, use the Backup Now button.
That's it — your server is protected and will keep backing up on the schedule you chose. Repeat Add a Server for each additional machine, up to your plan's limit.
Understanding your storage & server limits
Your plan has two limits, both shown on your dashboard:
- Storage allowance — the total backup space across all your servers. As you approach it you'll get warnings; at 100% new backups pause until you free space (delete old snapshots) or your provider raises it.
- Server limit — how many servers you can enrol. When you're at the limit, remove an unused key or ask your provider to raise it.
Need more storage or servers? Contact your provider — they can raise either limit on your account.
descriptionOverview
BackupBloc has two pieces you install, plus an optional third for off-site resilience:
- Backup server — one host that runs
rest-server(repository storage), the Flask management API, the admin web UI, and nginx as the TLS-terminating reverse proxy. - Backup agent — one install per machine you want to back up. The agent is just
resticplus a systemd timer (Linux) or LaunchDaemon (macOS) that runs snapshots on a schedule and reports status back to the server. - DR server optional — a second server in another location that keeps an append-only, off-site mirror of your backups. See Disaster recovery.
All encryption happens on the agent side, so the server never sees plaintext. The server just stores encrypted, content-addressed blobs.
checklistRequirements
| Role | Minimum | Recommended |
|---|---|---|
| Server OS | Any systemd Linux (Debian/Ubuntu/RHEL/Fedora/Arch/Alpine/openSUSE) | Ubuntu 22.04 LTS or Debian 12 |
| Server hardware | 1 vCPU, 512 MB RAM | 2+ vCPU, 2 GB+ RAM |
| Server storage | Enough for repos | 2–10× total client data (dedup) |
| Server access | root / sudo | root, plus a public domain if you want Let's Encrypt TLS |
| Linux agent | Kernel 3.2+, systemd | Any modern systemd distro |
| macOS agent | macOS 11 Big Sur | macOS 13 Ventura+ with Homebrew |
| Network | Agent → server on TCP 8000 (REST) + 443 (admin UI) | Open 80 outbound to agents too if using Let's Encrypt |
Port 8000 is not for the public internet. The REST server should only be reachable from agent IPs you trust. Firewall it or bind it to a private interface.
hubArchitecture
┌─────────────────────── BACKUP SERVER ──────────────────────────┐
│ │
│ nginx :443 ──► backup-api :5000 (Flask, mgmt) │
│ nginx :443/restic ──► rest-server :8000 (restic repo I/O) │
│ │ │
│ ▼ │
│ /var/lib/restic-repos/ │
│ ├── default/ │
│ └── offsite/ │
└─────────────────────────────────────────────────────────────────┘
▲
│ HTTPS (restic backup + admin heartbeat)
│
┌─── AGENTS ──────────────────────────────────────────────────────┐
│ web-01 (Linux) db-01 (Linux) MacBook (macOS) │
│ systemd timer systemd timer launchd │
└─────────────────────────────────────────────────────────────────┘
dns1 · Backup server — prepare the host
Provision a fresh Linux VM or dedicated machine. Point DNS at it if you plan to use a hostname (required for Let's Encrypt). Open the ports in the table below inbound.
| Port | Purpose | Scope |
|---|---|---|
22 | SSH (installer + admin) | Your admin IP |
80 | HTTP → HTTPS redirect + ACME challenge | Public (only if using Let's Encrypt) |
443 | Admin UI + management API (HTTPS) | Admin IPs, or public |
8000 | restic rest-server (agent traffic) | Agent IPs only |
The installer is completely interactive — there are no environment variables or config files to pre-populate. It will also install restic, rest-server, nginx, Python 3, and any other dependencies itself.
terminalRun the server installer
Download and extract the server package
Pull the latest server package from the CDN and unpack it. The tarball already contains the installer plus the server/ and admin/ trees it needs (and the agent + DR installers).
curl -fsSL https://backupbloc.com/downloads/backupbloc-server.tar.gz -o backupbloc-server.tar.gz
tar -xzf backupbloc-server.tar.gz
cd backupbloc-server
Run the installer as root
sudo ./install-server.sh
Answer the prompts
The installer asks for each setting in turn. Press Enter to accept the default shown in brackets.
| Prompt | Default | Notes |
|---|---|---|
| Server hostname / IP | $(hostname -f) | What agents will connect to. Use the public DNS name if you expose this box. |
| REST server port | 8000 | restic speaks to this. Only agents need it. |
| Admin UI port | 8080 | nginx binds here. Put 443 if you want the UI on the standard HTTPS port. |
| Repository directory | /var/lib/restic-repos | Where backup data is stored. Use a mountpoint with plenty of room. |
| Admin username | admin | Used for the web UI and the rest-server htpasswd. |
| Admin password | auto-generated | If left blank a strong 20-char password is printed — save it before pressing Enter. |
| Enable TLS? | N | Strongly recommended. Pick Y in production. |
Pick a TLS source (if you enabled TLS)
- Let's Encrypt — free, auto-renewing. Requires a public domain and port 80 open. Installer runs
certbotin standalone mode, fixes thelive/andarchive/permissions so the restic-server user can read privkey.pem, and installs a renewal deploy-hook that restarts services automatically. - Self-signed — generated on the spot, valid 10 years. No domain needed. Agents will need
--insecure-tlsor the CA added manually. - Existing certificate — you supply the paths to your
fullchain.pemandprivkey.pem.
Confirm the summary
The installer prints everything it's about to do, then asks for a single Y to proceed. From that point it is fully automatic — roughly 2–3 minutes.
Note the final banner
When it finishes you'll see the admin URL, credentials, and a list of important paths. Copy these somewhere safe now — the password is not shown again.
Access Points:
Admin UI: https://backup.example.com:8080
REST Server: https://backup.example.com:8000
Credentials:
Username: admin
Password: xxxxxxxxxxxxxxxxxxx
task_altVerify the services
Three systemd units should be active (running):
systemctl status restic-rest-server restic-api nginx
If any are red, check their logs:
journalctl -u restic-rest-server -n 50 --no-pager
journalctl -u restic-api -n 50 --no-pager
The installer also creates a default repository at /var/lib/restic-repos/default/ with a random 32-char encryption password saved to /etc/restic-manager/default-repo.password. Agents created later default to this repository name.
loginFirst login
Open the admin UI
Browse to the URL from the installer's final banner. Log in with the admin username and password.
Activate your license
On first login the panel will prompt for a license key. Paste the BB-XXXXXXXX-XXXXXXXX key issued to you. The panel phones home to license.backupbloc.com once, caches the result, and will keep working through short control-plane outages via the 12 h grace window.
(Optional) rotate the admin password
Use Settings → Change password in the admin UI. The new password is written to /etc/restic-server/users (bcrypt) and reused by the REST server on the next restart.
verified_user2 · Backup agent — authorise first
Every agent needs to be authorised before it can register. Authorisation happens under Licenses in the admin UI, in two styles:
- Client Access Key — for any machine without a static IP (laptops, home PCs, VPSs, or servers behind NAT). Generate a
BB-XXXXXXXX-XXXXXXXXkey under Licenses → Client Access Keys and paste it into the installer. Set Bill to account when generating it to auto-assign the machine to a client's account. Client-role users can also self-generate these from their own dashboard's Add a Server button, up to their plan's server limit. - Server license (IP-bound) — for fixed servers with a static public IP. Pre-authorise the IP under Licenses → Server Licenses (IP) (also with an optional Bill to account). The installer reads the public IP at runtime, the server validates it, and credentials are returned automatically — no key entry.
You must create the licence (or the client must generate a Client Access Key) before running the agent installer, otherwise the bootstrap call will fail and the installer will stop.
Auto-association: when a licence carries a Bill to account owner, the machine is automatically assigned to that user's account at install — its storage counts against their quota and it appears on their dashboard, with no manual step.
memoryLinux agent install
Run the installer (one line)
SSH into the client as root and run the hosted installer. It downloads restic and everything else it needs:
sudo bash <(curl -fsSL https://backupbloc.com/downloads/agent/install-agent.sh | tr -d '\r')
Air-gapped / no outbound internet? Copy install-agent.sh from your server tree to the client and run it directly: scp install-agent.sh root@web-01:/tmp/ && ssh root@web-01 'bash /tmp/install-agent.sh'.
Answer the prompts
| Prompt | Meaning |
|---|---|
| Server hostname / IP | What you entered at the SERVER_HOST prompt when you installed the server. |
| REST port | 8000 by default. The installer auto-detects http vs https by probing. |
| Repository name | Created automatically on first install. Give each server its own name (e.g. server1 or the hostname) rather than sharing default — separate repositories keep each server's backups clearly identifiable and make per-server storage usage report correctly. Repos sharing a name are shared (restic dedups within them, but a client account's usage is summed per agent, so sharing inflates the account total). |
| Admin UI URL | Full base URL of the admin UI — used for self-registration + heartbeats. e.g. https://backup.example.com |
| License type | 1 for a Client Access Key (enter BB-XXXXXXXX-XXXXXXXX — the installer still labels this "personal license key"), 2 for a pre-authorised server IP. |
| Repo encryption password | Blank = auto-generate. Saved to /etc/restic-agent/repo.password — save a copy off-box too. |
| What to back up | Common paths / custom list / full-system. |
| Schedule | 6-hourly, daily, weekly, or custom cron expression. |
| Retention | How many daily / weekly / monthly snapshots to keep. |
| SSH port | Used by "Backup Now" in the admin UI to fire ad-hoc runs. |
Installer finishes — first backup kicks off
The installer registers the client with the server, initialises the repository if needed, writes /etc/restic-agent/config.env, and drops a systemd service + timer. The first run fires immediately so you can see a snapshot appear in the admin UI within a minute or two.
systemctl status restic-backup.timer
systemctl list-timers | grep restic
Jump to the macOS agent section below — the flow is similar but uses Homebrew + launchd instead of apt + systemd.
Jump to the Windows agent section below — a double-click installer that sets up restic, Volume Shadow Copy for open files, and Scheduled Tasks. No SSH required.
laptop_macmacOS agent install
Grant Full Disk Access first. System Settings → Privacy & Security → Full Disk Access → add /usr/local/bin/restic (Intel) or /opt/homebrew/bin/restic (Apple Silicon) and /bin/bash. Without FDA the agent will skip ~/Library and many app data directories silently.
Run the installer with sudo
In Terminal on the Mac:
sudo bash <(curl -fsSL https://backupbloc.com/downloads/agent/install-agent-mac.sh | tr -d '\r')
Offline alternative: scp install-agent-mac.sh admin@mac:/tmp/ then sudo bash /tmp/install-agent-mac.sh.
Answer the prompts
Same fields as the Linux installer: server hostname, REST port, repo name, license type (a Client Access Key usually, for laptops — the installer still labels the option "personal license key"), repo password, paths, schedule, retention.
Homebrew + restic are installed for you
If Homebrew is missing the installer pulls it down and runs brew install restic. A LaunchDaemon is written to /Library/LaunchDaemons/com.backupbloc.agent.plist and loaded — so jobs fire on schedule even when no user is logged in.
Confirm the LaunchDaemon is loaded
sudo launchctl list | grep backupbloc
log show --predicate 'process == "bash"' --last 5m --info
desktop_windowsWindows agent install
The Windows agent is a double-click installer (BackupBlocAgentSetup.exe). It installs restic, the BackupBloc agent (PowerShell, no other dependencies), and two Scheduled Tasks — a scheduled backup and a once-a-minute poller that powers Backup Now and restores. It works on Windows 10 / 11 and Windows Server 2016 or newer.
Open / locked files are handled. Before each run the agent creates a Volume Shadow Copy (VSS) snapshot and backs up from that, so files held open by running apps (SQL Server .mdf, Outlook .pst, etc.) are captured consistently. If VSS can't run it falls back to a direct backup and logs a warning.
Pilot builds are unsigned. Until an Authenticode certificate is applied, Windows SmartScreen shows an "unknown publisher" prompt — click More info → Run anyway. This will go away once the installer is code-signed.
Download and run the installer (as Administrator)
Download BackupBlocAgentSetup.exe and run it. It will elevate automatically.
Fill in the wizard
| Field | Meaning |
|---|---|
| Server host | Your BackupBloc server, e.g. backups.backupbloc.com. REST port defaults to 8000 (http/https auto-detected). |
| License key | Optional. A Client Access Key BB-XXXXXXXX-XXXXXXXX for a machine without a static IP. Leave blank for a pre-authorised server IP. |
| Folders to back up | Semicolon-separated, e.g. C:\Users;D:\Data. Defaults to C:\Users. |
| Schedule | Every 6 hours, daily, or weekly. |
It licenses, registers, and schedules automatically
The installer downloads restic, fetches the repository credentials via your license, writes its config to C:\ProgramData\BackupBloc\ (locked to Administrators/SYSTEM), and creates the BackupBloc Backup and BackupBloc Poller Scheduled Tasks (both run as SYSTEM). The machine then appears in the admin UI → Clients within a minute.
Save the repo password. If auto-generated it is written to C:\ProgramData\BackupBloc\repo.password. Keep a copy off-box — it is required to restore.
Not licensed yet?
If this machine's IP isn't licensed, the agent installs but backups stay disabled. Once your admin licenses it, run the bundled launcher (elevated) to activate:
"C:\Program Files\BackupBloc\Validate-BackupBlocLicense.cmd"
Managing the agent
# See the scheduled tasks
schtasks /Query /TN "BackupBloc Backup"
schtasks /Query /TN "BackupBloc Poller"
# Logs
type C:\ProgramData\BackupBloc\logs\backup.log
Uninstall from Settings → Apps (or Add/Remove Programs). The config + repo password in C:\ProgramData\BackupBloc are kept by default so a reinstall reuses the same repository.
No inbound SSH. Windows agents talk to the server over outbound HTTPS only. "Backup Now" and restores are delivered through the once-a-minute poller, so nothing needs to be opened on the client firewall. Self-updates are downloaded from the CDN and cryptographically verified (RSA signature) before they run — same signing key as the Linux agent.
Desktop tray app
The Windows agent includes a system-tray widget (the BackupBloc logo next to the clock; look under the ▲ "show hidden icons" arrow, and drag it out to pin it). It runs as the logged-in user and needs no admin rights for day-to-day use. Double-click it for the status window, or right-click for the menu:
| Action | What it does |
|---|---|
| Status… | Licence state + expiry, last backup time/result, schedule, folders, and a live progress bar while a backup runs (phase: Creating snapshot → Backing up → Completed). |
| Back up now | Kicks off a backup within ~a minute. |
| Change backup folders… | Add/remove folders and change the schedule (no admin prompt). |
| Refresh licence / Open web panel | Re-checks the licence, or opens this panel in the browser. |
The widget can't read the locked config directly, so the agent publishes a sanitized, no-secrets status.json to C:\ProgramData\BackupBloc\public\ and the widget drops small request files there for the SYSTEM poller to action — which is how it avoids UAC prompts. If a backup ever fails, the exact reason is written to C:\ProgramData\BackupBloc\public\last-run-error.txt.
Open files (VSS)
Before each run the agent creates a Volume Shadow Copy so files held open by running apps (databases, Outlook .pst, etc.) are captured consistently. The first snapshot of a session can take a minute while Windows starts the Volume Shadow Copy service — the tray shows "Creating snapshot (VSS)" during that time; it isn't stuck.
Each machine has its own private repository. Windows agents don't share the default repo — every machine backs up into an isolated restic repository named by a stable, globally-unique machine ID, with its own encryption password. Customers/users are therefore cryptographically separated (one machine's password can't decrypt another's), and client-role users only see their assigned machines in the panel.
fact_checkVerify the first backup
- Open the admin UI → Clients. The new host should appear with a green dot within ~60 seconds of install.
- Click it → Snapshots. The first snapshot should show up after the scheduled run (or immediately, since the installer triggers an initial run).
- Pick a snapshot → Browse files to confirm the content looks right. You can restore individual files or whole directories from here.
You're done. Repeat the agent install on every machine you want backed up. Server-side, the admin UI gives you live job logs, retention management, ad-hoc "Backup Now", and per-client snapshot browsing + restore.
databaseDatabase (MySQL) backups
Alongside file backups, each agent can dump and back up its MySQL / MariaDB databases on their own schedule. Database snapshots are stored in the same repository as file backups but tagged separately, so they show up under their own Databases page. You manage this per server — admins for any server, client users for the servers they own.
Prerequisite — the MySQL backup script must be installed on the agent. It ships with the agent installer, but a host installed before MySQL support was added won't have it. If the Databases page shows a ⚠ Script not installed badge next to a server (and disables its Backup Now button), re-run the agent installer on that host to add it:
sudo bash <(curl -fsSL https://backupbloc.com/downloads/agent/install-agent.sh | tr -d '\r')
Re-running the installer is safe — it keeps your existing repository and keys and just tops up the scripts.
Open the Databases page
In the panel, go to Databases. The top of the page lists existing database snapshots; the Configure & Trigger table below lists your servers with their last database backup, schedule, and status. Click Configure on the server you want.
Enable and detect databases
Tick Enable MySQL Backups. BackupBloc auto-detects the databases on the server (it tries every credential-free method — unix-socket auth, control-panel root passwords for cPanel/Webuzo/Plesk/DirectAdmin/CyberPanel/Hestia, debian.cnf, /root/.my.cnf). Choose which databases to back up, or leave them all selected. Use Re-detect if you add databases later.
If credentials are needed
If auto-detection can't find working credentials, a MySQL password required prompt appears. Enter the username (usually root) and password once — they're saved to the agent (/etc/restic-agent/mysql.conf, root-only) and won't be asked again. Click Save to Agent & Re-detect.
Set a schedule (and retention)
Turn on Enable schedule and pick a preset (e.g. every 6 hours, daily at 2 AM) or enter a custom cron expression. This writes a cron entry on the agent (/etc/cron.d/restic-mysql) that runs the backup automatically. Optionally set how many hourly/daily/weekly/monthly database snapshots to keep — leave blank to inherit the agent's install-time defaults. Click Save.
Run one now (optional)
Back on the Configure & Trigger table, click Backup Now to run an immediate database backup without waiting for the schedule. The status cell updates when it finishes; the new snapshot appears in the list at the top of the page.
To restore or download a database, use the snapshot list at the top of the Databases page — Restore pushes a database back to a server, and Download streams a .sql.gz dump. Hosting customers can also self-restore their own databases from inside their cPanel / Webuzo plugin.
restoreRestoring files
Restore from any snapshot to one of three destinations — the original client, the backup server itself, or a brand-new / different server (for rebuilding a machine that has failed). The same chooser appears whether you restore from the primary backup or from a DR copy.
Find the snapshot
Open Snapshots, pick the client and the snapshot you want. Click a snapshot to browse its files, or click Restore to open the restore dialog.
(Optional) narrow to one path
In Path Filter, enter a single file or folder (e.g. /home/user/site) to restore just that. Leave it blank to restore the whole snapshot.
Choose where to restore
| Destination | What happens |
|---|---|
| Original client | Files are pushed back onto the machine the snapshot came from. Linux/macOS agents receive it over SSH; Windows agents (which have no SSH) pick it up via the once-a-minute poller instead — automatic, no configuration. |
| This server | Files land in a folder on the backup server (under an allowed restore path) for you to inspect or copy. |
| New / other server | Restore onto a different or freshly-built machine — by registered agent, or by SSH details. See the steps below. |
Set the Restore to path, click Start restore, and watch the live progress bar.
Windows targets. Restoring to a Windows client defaults the target to C:\BackupBloc-Restore. restic recreates the original tree under it, so files land at <target>\C\Users\... (the drive letter becomes a folder) — that's expected, not a duplicate drive. Type any path you like in the target box to change it.
Restoring to a new / replacement server
This is the disaster-recovery path: a server failed, you've built a replacement, and you want its data back on it. Choose New / other server, then one of:
- Registered agent — the replacement already has the BackupBloc agent installed. Pick it from the list; the manager restores onto it.
- By SSH details — the replacement has no agent yet. Enter its IP/hostname and SSH port. First authorise the manager's key on it (the dialog shows the exact command):
mkdir -p /root/.ssh && echo 'ssh-ed25519 AAAA…' >> /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys
Set the destination path on the new server — use / for a full system recovery, or a staging folder (e.g. /var/tmp/recovery) to copy specific data across first. The manager SSHes in, installs restic if missing, then restores either directly (the new server pulls the repo over the network — fast) or via relay (the manager restores and streams the files over SSH) if the new server can't reach the backup server's REST port. The path is chosen automatically and shown live.
The destination path is on the target server and isn't restricted by the backup server's allowed-prefix list — restoring to / overwrites files in place. Use a staging folder if you only want to copy specific data across.
For the same flow starting from your off-site DR copy (when the primary is also gone), use DR → Manage replication → Restore — it offers the identical destination chooser. See Rebuild a failed server.
delete_foreverUninstall the agent
When you decommission a machine, run the uninstaller on the host and remove the client in the panel. Doing only one of those leaves the other side polling / accumulating noise.
Linux
One-line install/uninstall — downloads from the CDN and runs:
sudo bash <(curl -fsSL https://backupbloc.com/downloads/agent/uninstall-agent.sh | tr -d '\r')
If the host is air-gapped, copy uninstall-agent.sh across and run sudo bash uninstall-agent.sh.
The script stops + disables the systemd units (restic-backup.timer, restic-mysql-backup.timer, restic-restore-poller.timer), removes the cron entry if present, deletes the binaries under /usr/local/bin, and clears /etc/restic-agent + /var/log/restic-agent.
macOS
sudo bash <(curl -fsSL https://backupbloc.com/downloads/agent/uninstall-agent-mac.sh | tr -d '\r')
Tears down the LaunchDaemon (com.backupbloc.agent), removes the binaries, and clears the agent config + logs the same way.
Manual one-liner (if you can't fetch the script)
sudo systemctl disable --now restic-backup.timer restic-mysql-backup.timer restic-restore-poller.timer 2>/dev/null
sudo rm -f /etc/systemd/system/restic-{backup,mysql-backup,restore-poller}.{service,timer}
sudo rm -f /usr/local/bin/restic-{backup,mysql-backup,progress-reporter,restore-reporter,restore-poller,validate-license}
sudo rm -rf /etc/restic-agent /var/log/restic-agent /etc/cron.d/restic-restore-poller
sudo systemctl daemon-reload
Then in the panel
- Open Clients, find the row, click Delete.
- Choose Remove client, keep backups if you want to retain the snapshots (restorable forever) — or Remove + delete all backups to also delete the repository on the server.
- Either choice revokes the agent's license entry, so even if the uninstaller didn't run, the agent stops backing up on its next license re-check (within 24 h).
What about restic itself? The uninstaller leaves the restic binary in place — it's a general-purpose tool you may want for other things. Remove manually with sudo rm /usr/local/bin/restic (or via your package manager) if it's no longer needed.
Decommissioning shortcut. If you can't SSH to the host (e.g. it's already gone), removing the client in the panel is enough on its own — the agent's daily license re-check fails within 24 h, the local license.status flips to invalid, and every subsequent backup refuses to run. You'll receive a "License re-validation failed" notification each day until the agent is fully removed.
extensionControl-panel plugins
The cPanel and Webuzo plugins give every hosting account a self-service BackupBloc area inside their own control panel. From there an end user can browse their account's file snapshots, restore individual files or folders, restore their databases, and run an on-demand backup — all scoped to their own account, with no BackupBloc admin login and no SSH.
You install the plugin once on the hosting server (the same box that already runs the BackupBloc agent). It is a thin front end over the manager's existing API.
Per-account isolation is enforced server-side. The account name comes from the trusted control-panel session (never from the browser). Every file path is forced under that account's home directory (/home/<user>/…) and every database under the <user>_ prefix. One customer can never see or restore another's data.
How the token is protected (cPanel). On CloudLinux/CageFS the account UI runs as the jailed user, which must never hold the admin API token. So the cPanel plugin ships a small root broker service: the user's page drops a request in its own home (~/.bbspool), and the root broker — which derives the account from the request file's owner — holds the token, enforces the scoping, and does the actual API calls. The token is never readable by any account.
keyStep 1 · Create an API token
Both plugins authenticate to the manager with a long-lived API token. You generate this yourself — no BackupBloc admin needs to issue it for you.
Open the token panel
Log in to the BackupBloc panel and go to Security → API Tokens. (Full admins can alternatively use Settings → API Tokens.)
Create and copy the token
Enter a label (e.g. cpanel-server-1), click Generate Token, and copy the token — it is shown only once. You'll paste it into the plugin's settings in the next step.
Treat the token like a password. A token you generate under Security → API Tokens is scoped to your own account — it can only see and restore the servers you own, never anyone else's. Use a separate token per server so you can revoke one without affecting the others (revoke from the same screen).
dnsStep 2a · Install on cPanel / WHM
For a cPanel/WHM server (Jupiter theme) that already runs the BackupBloc Linux agent. CloudLinux + CageFS are supported.
Download and run the installer (as root)
cd /root
curl -fLO https://backupbloc.com/downloads/backupbloc-plugin-cpanel.tar.gz
tar -xzf backupbloc-plugin-cpanel.tar.gz
cd backupbloc-plugin-cpanel
bash install.sh
The installer deploys the account-facing plugin (the BackupBloc tile under Files), installs and starts the root broker service (backupbloc-cpanel-broker), registers the WHM admin page, and drops a config skeleton at /var/cpanel/backupbloc/plugin.conf (readable only by root).
Enter your settings in WHM
Open WHM → Plugins → BackupBloc and fill in:
| Field | Value |
|---|---|
| API URL | Your BackupBloc panel URL, e.g. https://backups.backupbloc.com. Plain https://, no trailing slash — do not append :8000 (that's the backup-data port and returns Invalid token — HTTP 401). |
| API Token | The token from Step 1 |
Click Save & Test (use this rather than "Test Connection Only"). It verifies the token and auto-detects this server's client_id by matching its hostname against your registered agents.
Confirm it appears for accounts
Log in to any cPanel account (or use WHM → List Accounts → login). Under Files you'll see a BackupBloc tile. Open it — the account's snapshots load.
Broker health check. systemctl status backupbloc-cpanel-broker should be active (running). It's what turns each account's request into a scoped manager call.
Uninstall: from the extracted folder, run bash uninstall.sh (add --purge to also remove /var/cpanel/backupbloc). Snapshots on the server are never touched.
dnsStep 2b · Install on Webuzo
For a Webuzo server that already runs the BackupBloc agent.
Download and run the installer (as root)
cd /root
curl -fLO https://backupbloc.com/downloads/backupbloc-plugin-webuzo.tar.gz
tar -xzf backupbloc-plugin-webuzo.tar.gz
cd backupbloc-plugin-webuzo
bash install.sh
The installer deploys the end-user portal page (/usr/local/webuzo/web/enduser/backupbloc) and the admin panel plugin (/usr/local/webuzo/plugins/backupbloc), and creates a root-only config at plugin.conf.
Enter your settings in the Webuzo admin panel
Open Webuzo Admin Panel → Plugins → BackupBloc Settings, enter the API URL and paste the API Token from Step 1, then Save. It verifies the token and detects this server's client_id.
Confirm it appears for accounts
Log in to a Webuzo end-user account — the BackupBloc entry appears in the panel, and the account's snapshots load when opened.
Uninstall: run bash uninstall.sh from the extracted folder.
restoreStep 3 · Browse & restore (for end users)
This is what your hosting customers do inside their own panel. Open BackupBloc (cPanel: the tile under Files; Webuzo: the panel entry). There are three tabs, plus a Return to cPanel link in the header.
Restore a file or folder
- On File Backups, pick a restore point and click Browse. These are the server's restore points; browsing shows only your account's files.
- Navigate into folders using the breadcrumb, then click Restore next to any file or folder.
- Confirm the prompt. The file is restored in place to its original location in your account, and a live progress bar tracks it.
Restore a database
- Open the Databases tab — it lists only your account's databases (the
<user>_ones). - Click Restore on the one you want and confirm. The database is restored from the selected snapshot.
Run a backup now
On the Run Backup tab, click Run Now to back up your account on demand. A new snapshot for your account appears in File Backups once it completes.
Only one backup runs at a time per server. If a server-wide backup is already in progress, "Run Now" tells you to try again shortly rather than queuing a second one.
cloud_sync3 · Disaster recovery (DR)
Disaster recovery keeps a second, off-site copy of your backups on a separate server — so even if the main backup server is lost, corrupted, or its repository is wiped, your data survives. A DR server holds an append-only mirror of selected repositories: new backup data is copied to it on a schedule, but anything deleted or pruned on the main server is never deleted on the DR. Accidental deletions and ransomware can't propagate.
The whole feature is managed from the existing admin UI under Disaster Recovery → DR Servers — there is no second panel to log in to.
Disaster Recovery is a paid add-on. If your plan doesn't include it, the DR Servers page shows an upgrade prompt instead of the management UI. Once DR is enabled on your licence, the page unlocks and everything below works. Whether DR is included is controlled by your licence — no reinstall or config change is needed when it's added.
Because BackupBloc stores every agent's repository centrally on the main server, replication runs main server → DR server. Your source machines (web-01, db-01, …) are never involved in DR and never need access to it.
schemaHow it works
┌──────────── MAIN BACKUP SERVER ────────────┐ ┌─────────── DR SERVER ───────────┐
│ /var/lib/restic-repos/ (canonical) │ │ /var/lib/restic-repos/ (mirror) │
│ │ │ SSH │ ▲ │
│ └── rclone copy (append-only) ───────┼────────┼────────┘ │
│ every 5 min … 6 h, per repo │ (SFTP) │ append-only rest-server :8000 │
└─────────────────────────────────────────────┘ └──────────────────────────────────┘
restore / browse ◄────────────────────────────── (restic over SSH)
- Append-only — replication uses
rclone copy(neversync), so files removed on the main server are kept on the DR. - Snapshots copied last — data and indexes are copied before snapshot files, so the DR copy is always a valid, restorable repository even mid-sync.
- All over SSH — the main server reaches the DR with its existing trigger key. No extra credentials travel the network in cleartext.
- Same encryption — the DR holds the identical encrypted repo, so the main server's existing repo password unlocks it for restores. Nothing new to manage.
checklistDR requirements
| Item | Requirement |
|---|---|
| DR host | A second server, ideally in a different location/provider. Any systemd Linux (Debian/Ubuntu/RHEL/Fedora). |
| Storage | At least as much free space as the repositories you'll replicate — plan for more, since the append-only copy keeps history the main server may prune. |
| Access | root / sudo on the DR host. The main server must be able to reach the DR over SSH (port 22 by default). |
| Network | Outbound from the main server → DR on the SSH port. The DR does not need to reach back into the main server. |
rocket_launchOption A — Set up a new DR node (recommended)
This provisions a blank server into a full DR node automatically: it installs restic, rclone, and an append-only rest-server, authorises the main server, and registers itself so it appears in your panel.
Open the DR page and start the wizard
In the admin UI, go to Disaster Recovery → DR Servers and click Set up new DR node. Give it a name (e.g. DR-Sydney) and click Generate install command.
Copy the one-time install command
The panel produces a ready-to-paste command containing a single-use enrollment token (valid for one hour). It looks like this:
bash <(curl -fsSL https://backupbloc.com/downloads/agent/install-dr.sh | tr -d '\r') \
--manager https://backup.example.com --token BB-DR-XXXXXXXXXXXX
Run it on the blank DR server as root
SSH into the new server and paste the command. It installs everything, opens the firewall for the rest-server port, authorises the main server's SSH key, and calls home to register. It finishes in a couple of minutes.
sudo bash <(curl -fsSL https://backupbloc.com/downloads/agent/install-dr.sh | tr -d '\r') \
--manager https://backup.example.com --token BB-DR-XXXXXXXXXXXX
Confirm it appears in the panel
Back in DR Servers, the new node shows up with a green Online status and ticks for rclone, restic, and rest-server, along with its free disk space. You're ready to assign replication.
The installer is hosted at backupbloc.com/downloads/agent/install-dr.sh. You can inspect it before running — it's a plain Bash script.
lanOption B — Add an existing server by SSH
If you already have a server you want to use (and prefer to install the tools yourself, or just point at an existing box), add it by SSH details instead. The main server reaches out to it.
Add the server
On the DR Servers page click Add DR server, enter a name, the DR host's IP/hostname, and SSH port, then save.
Authorise the main server's key (if prompted)
If the main server can't SSH in yet, the panel shows an Awaiting SSH key status and a one-line command to run on the DR host. It adds the main server's public key to /root/.ssh/authorized_keys:
mkdir -p /root/.ssh && echo 'ssh-ed25519 AAAA…' >> /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys
Then click Test connection. The status flips to Online.
A manually-added server must have restic and rclone installed for replication and restore to work. The card's tick marks show what's present. The installer in Option A handles this for you.
syncAssign replication
Choose which client repositories replicate to the DR server, and how often.
Open "Manage replication"
On the DR server's card click Manage replication.
Add a repo and pick an interval
Under Add replication, choose a client (its repository is selected automatically) and an interval, then click Add. An initial seed sync starts straight away.
| Interval options |
|---|
| 5 min · 15 min · 30 min · 1 hour · 2 hours · 3 hours · 6 hours |
Monitor and control each repo
Each replication row shows its last-sync status and lag (e.g. synced 8 min ago). Per row you can:
- Sync now — run an immediate replication.
- Pause / Resume — turn the schedule off without losing the assignment.
- Change interval — pick a new frequency from the dropdown.
- View log — see the per-repo replication history.
- Remove — stop replicating (data already on the DR is left intact).
Watch live progress
While a sync runs you get a live progress bar that pulses blue with a percentage, turns green on success and red on failure — both in the Manage replication rows and in the Active Replications panel at the top of the DR page (so you can see what's running without opening anything). It shows the phase (Copying data → Copying snapshots), live transfer rate and ETA, and refreshes on its own.
The first sync (the seed) copies the whole repository, so it can take a while for large repos. Replication is append-only and incremental — subsequent syncs skip everything already on the DR and only copy new data, so they're fast.
restoreRestore / failover from the DR
If you need data back from the off-site copy — for example a file that was deleted and later pruned from the main server — restore directly from the DR.
Browse the DR's snapshots
In Manage replication, click the Restore (history) icon on a repo. The panel lists the snapshots present on the DR copy, tagged FILES or DB.
Choose where to restore
Click Restore… on the snapshot, optionally enter a single path, then pick a destination:
| Destination | What happens |
|---|---|
| This manager | Files land in a folder on the backup server (under an allowed restore path) so you can inspect or copy them. |
| Registered agent | Files are restored onto a server that already has the agent installed — pick it from the list. |
| New server (SSH) | Rebuild a failed server: enter the new machine's IP + SSH port and restore straight onto it. See below. |
Progress is shown live for all three.
Because the DR holds the same encrypted repository, restores use the main server's existing repo password automatically — there are no extra keys to enter.
dnsRebuild a failed server onto a fresh machine
When a source server is lost entirely, build a replacement, install the OS, then restore its data onto it directly. This works from either the primary backup (main server's Snapshots → Restore) or the off-site DR copy (DR → Manage replication → Restore) — both offer the same New / other server destination.
Authorise the manager on the new server
The panel shows a one-line command in the restore dialog. Run it on the new server as root — it adds the manager's SSH key so the manager can connect and drive the restore:
mkdir -p /root/.ssh && echo 'ssh-ed25519 AAAA…' >> /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys
Enter the new server's details and restore
Pick New / other server → By SSH details, enter its IP/hostname and SSH port, and set the destination path — use / for a full system recovery, or a subfolder to stage the files first. Click Start restore.
What the manager does
It SSHes into the new server, installs restic if it's missing, then either:
- Direct — the new server runs
resticitself and pulls the repository over the network from the backup server. Fast, streams straight onto the new machine. - Relay — if the new server can't reach the backup server's REST port, the manager restores the files itself and streams them to the new server over SSH. Used automatically as a fallback, and always for DR-copy restores.
Prefer the agent route? You can instead install the agent on the new server (pointing it at the same repository), then choose Registered agent as the destination. Either method restores the data onto the new machine.
The destination path is on the new server and is not restricted by the manager's allowed-prefix list — restoring to / overwrites system files in place, so use a staging folder if you only want to copy specific data across.
emergency_homeRecovering when the whole site / main server is down
The flows above assume the backup server is reachable to drive the restore. But what if the entire data centre holding the source servers and the backup server goes down? For that, each DR node carries everything needed to recover on its own:
- Recovery bundle — alongside the backup data, the manager replicates the repository decryption passwords and config (users, agents, settings) to the DR node, into
/etc/backupbloc-recovery/. Without these the off-site copy would be unreadable ciphertext; with them the DR node can decrypt and restore by itself. This syncs automatically after each replication. - Standby recovery panel — the DR installer also puts a read/restore-only copy of the BackupBloc panel on the DR node. It does not replicate or accept backups, and it keeps working even when the primary and the licence server are unreachable.
Use the standby panel
Browse to the DR node
Open https://<dr-node-ip>/ (the installer prints the exact URL; it uses a self-signed certificate, so accept the browser warning). Log in with your normal admin credentials — they're part of the replicated bundle.
Restore from the local copy
A STANDBY banner confirms you're on the recovery panel. Use Snapshots (or DR → Manage replication → Restore) exactly as on the primary — browse files, restore a single file, a folder, or rebuild a whole server onto a fresh machine. All restores read from the DR node's local mirror.
Manual fallback (no panel)
If you prefer the command line, the data is right there on the DR node — decrypt and restore directly with restic:
export RESTIC_PASSWORD="$(cat /etc/backupbloc-recovery/<repo>.password)"
restic -r /var/lib/restic-repos/<repo> snapshots
restic -r /var/lib/restic-repos/<repo> restore latest --target /recovery
The standby panel is restore-only. When your primary comes back, resume normal operations there — don't run backups against the standby. To install the standby panel on an existing DR node, re-run the DR installer (it's added automatically; pass --no-panel to skip).
Because the recovery bundle includes repository passwords, treat your DR node as security-sensitive — it holds the keys to decrypt your backups, which is exactly what makes off-site recovery possible.
groupUser roles
BackupBloc has two built-in roles. Both roles log in to the same admin UI — the interface simply adapts to what each role is allowed to do.
| Role | What they see | What they can do |
|---|---|---|
| admin | Full admin panel — Dashboard, Clients, Plans, Repositories, Snapshots, Databases, Jobs, Logs, Licenses, Users, Settings | Everything: manage clients, repositories, users, schedules, licenses, trigger global backups, perform restores |
| client | A scoped view of their own account only — their Dashboard (account storage, servers used vs limit), Your Servers (read-only status), Repositories, Snapshots, and Backup Now. Server-wide pages (server dashboard stats, Logs, Settings) and all admin sections are hidden | Self-enrol servers up to their limit (Add a Server → Client Access Key), run on-demand backups, browse and restore their own snapshots, change their own password. They never see other tenants' data |
A client user is typically a website owner or developer who needs to snapshot their own server before making changes — without being exposed to the full admin interface.
Creating a client user
Open the Users page
In the admin UI, navigate to Users in the sidebar.
Add a new user
Click + Add User. Enter a username and password, then change the Role dropdown from admin to client.
Set a storage quota
The Account Storage Quota (GB) field caps how much backup data this user can accumulate across all their servers. Leave it at 0 for unlimited.
Set a server / agent limit
The Max Servers / Agents field caps how many servers the user may enrol. They self-generate a Client Access Key per server from their dashboard, up to this number; further keys are refused until one is freed. Leave at 0 for unlimited.
(Optional) pre-assign existing clients
The Assigned Clients section lets you tick servers that are already registered to hand them to this user. For new servers you don't need this — when the user installs an agent using a key bound to their account, it auto-associates. A server can belong to only one user.
Save and share credentials
Click Save. Send the username and password to the user — they log in at the same URL and land on their own Dashboard. From there they follow the Client quick start to enrol their servers. You can hand them that section directly.
shieldThe client user panel
When a client-role user logs in, the interface hides all admin-only navigation and takes them to their own Dashboard. Everything they see is scoped to their account — they never see other tenants or the provider's server-wide figures.
Dashboard
- Account Storage — a donut showing space used against their plan quota (or "Unlimited" if none is set).
- Your Servers — servers enrolled versus their limit, an Add a Server button that self-generates a Client Access Key, and a Waiting for install list of keys not yet used (with Copy / Remove).
- Snapshots and Backed Up Data totals, plus read-only status cards for each of their servers.
The Your Servers (Clients) page is read-only for clients: they can view a server's status but not change its schedule, quota, owner, or delete it — those stay with the admin. The Backup Now page (below) is reached from the sidebar or the header button. It is split into two columns:
| Left column | Right column |
|---|---|
|
Your Servers — live status cards for each of the user's assigned clients, showing last backup time and current state (Synced / In Progress / Failed). Backup form — type selector, paths tag-input, optional note, and the green Start Backup button. Inline status — after triggering, the form is replaced by a live progress ring that polls every 4 seconds and updates to Complete or Error automatically. |
Recent Snapshots — the last 12 snapshots scoped to this user's clients. Shows hostname, age, and size. Refreshes automatically when a new snapshot completes. |
The header also gains a green Backup Now button that navigates back to this page from any other section of the UI.
play_circleBackup Now — how it works
Backup Now creates an on-demand snapshot without waiting for the scheduled timer. It is available to both admin users (per-client trigger button on the Clients page) and client users (their dedicated panel).
Backup types
| Type | What it does | When to use it |
|---|---|---|
| Standard | Incremental — restic only reads and uploads files that have changed since the last snapshot. Fast and storage-efficient. | The default. Use this before any routine change (deploying code, updating a CMS, running database migrations). |
| Full | Forces restic to re-read every file from disk regardless of modification time (--force). Slower, but catches silent drift (clock skew, filesystem corruption). |
Use when you suspect data integrity issues or before a major upgrade where you want a fresh, unconditional baseline. |
Scoped paths
By default, Backup Now backs up everything configured for that client. You can narrow the scope by typing one or more absolute paths into the What to back up tag-input and pressing Enter after each one:
# Example — back up only the web root and the database export directory
/var/www/mysite
/var/backups/mysql
The paths are passed directly to restic backup. Leave the input empty to use the client's default configured paths.
Technical flow
- The UI posts
POST /api/backup/triggerwith{ client, full, paths }. - The server queues the request in
/etc/restic-manager/triggers.json. - The agent's restore-poller daemon (running every 30–60 seconds via systemd timer) polls
GET /api/backup/trigger/pending, finds the queued job, and ACKs it immediately to prevent duplicate runs. - The agent runs
restic backupwith the specified arguments and streams progress back viaPOST /api/backup/progress. - The UI polls
GET /api/backup/statusevery 4 seconds and updates the status ring in real time.
The poller runs every 30–60 seconds, so there may be a short delay before the backup starts. The status card will show Queued until the agent picks it up, then switch to Running.
securitySecurity hardening
The following hardening changes have been applied to the codebase. Each addresses a specific vulnerability class found during an internal security audit.
Shell injection via eval removed
Risk: The agent scripts (restic-backup, restic-mysql-backup, restic-restore-poller) previously used eval to build and run restic commands from environment variables. A malicious value in any variable (e.g. BACKUP_PATHS) could inject arbitrary shell commands that would be executed as root.
Fix: All eval calls replaced with explicit Bash arrays. Variables are word-split via read -ra and expanded as "${ARRAY[@]}", which is safe even if values contain spaces or shell metacharacters.
# Before — vulnerable
eval restic backup $BACKUP_PATHS --tag $CLIENT_ID
# After — safe
RESTIC_CMD=(restic backup)
read -ra _BP <<< "${BACKUP_PATHS:-}"
[[ ${#_BP[@]} -gt 0 ]] && RESTIC_CMD+=("${_BP[@]}")
RESTIC_CMD+=(--tag "${CLIENT_ID}")
"${RESTIC_CMD[@]}"
TLS certificate verification enforced
Risk: All curl calls in the agent scripts used -sk (skip TLS verification). This allowed a network-position attacker to MITM the connection between the agent and the backup server and intercept or modify responses.
Fix: The -k flag has been removed from every curl call. When a custom CA bundle is present at /etc/restic-agent/ca.crt (e.g. for self-signed server certificates) it is passed via --cacert; otherwise the system trust store is used.
CURL_TLS=()
[[ -f /etc/restic-agent/ca.crt ]] && CURL_TLS=(--cacert /etc/restic-agent/ca.crt)
# Every curl call now includes ${CURL_TLS[@]}
curl -s "${CURL_TLS[@]}" --max-time 15 \
"${API_BASE}/api/health"
Agent update scripts verified before execution
Risk: The restore-poller downloaded update-agent.sh from backupbloc.com and ran it directly with bash. A compromised CDN, DNS hijack, or HTTP downgrade could deliver a malicious script that would run as root on every agent.
Fix: The poller now downloads both the update script and a detached RSA signature (.sig), and verifies it against the public key at /etc/restic-agent/update-pubkey.pem before executing. The public key is installed both by the agent installer and by every update bundle, and the matching private key never leaves the release machine. If the signature is missing or fails to verify, the download is deleted and the update is skipped.
Signatures use RSA with SHA-256, verified by openssl dgst -sha256 -verify — which works on every OpenSSL (1.0.2 / 1.1.1 / 3.x) and LibreSSL, so it's portable across all agent OSes. Releases are signed by build-release.sh, which self-verifies the signature before the bundle is published.
PUBKEY=/etc/restic-agent/update-pubkey.pem
if [[ -f "$PUBKEY" ]] && \
openssl dgst -sha256 -verify "$PUBKEY" \
-signature "$_UPDATE_SIG" "$_UPDATE_SCRIPT"; then
bash "$_UPDATE_SCRIPT"
else
# Signature missing or invalid — refuse to execute
rm -f "$_UPDATE_SCRIPT" "$_UPDATE_SIG"
fi
SQL injection in MySQL backup script prevented
Risk: The restic-mysql-backup script interpolated the database name directly into a MySQL query string. A database name containing SQL metacharacters (e.g. a backtick or semicolon) could break out of the query context.
Fix: Database names are validated against a strict allowlist regex (^[a-zA-Z0-9_-]+$) before use. Any name that does not match is logged and skipped.
if [[ ! "$DB_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
log "WARN Skipping database with unsafe name: ${DB_NAME}"
continue
fi
Repository password endpoint authenticated
Risk: The POST /api/repos/<id>/password endpoint — used by the agent installer to register the repository encryption password — accepted any request without verifying that the caller was a known, registered agent. An unauthenticated attacker who could reach the API could overwrite any repository's password.
Fix: The endpoint now requires a valid client_id in the request body, checks it against the registered agents database (/etc/restic-manager/agents.json), and verifies that the target repository's config file actually exists before updating the password.
Login rate limiting
Risk: The POST /api/auth/login endpoint had no brute-force protection. An attacker could make unlimited login attempts.
Fix: flask-limiter (≥ 3.5.0) is now installed in the server virtualenv and applies a limit of 10 login attempts per minute per IP. If the package is unavailable (e.g. during an in-place update) the limit degrades gracefully to a no-op rather than crashing the API.
# /opt/restic-manager/venv/bin/pip install "flask-limiter>=3.5.0"
# Added to server/requirements.txt
Rate limits apply per source IP. If your agents or admin UI are behind a NAT that shares one external IP, the 10 req/min limit applies to the whole NAT group. Adjust the limit in backup-api.py if needed.
Session TTL reduced to 24 hours
Risk: Login session tokens previously lasted 7 days. A stolen token (e.g. from browser storage or a compromised machine) had a long exploitation window.
Fix: _SESSION_TTL reduced from 86400 * 7 to 86400 (24 hours). All existing sessions are unaffected until they next authenticate.
Restore path whitelist
Risk: The POST /api/restore/jobs endpoint accepted any absolute path as the restore target. An authenticated user (or a compromised session) could request a restore to /etc/restic-manager/ or another sensitive path, overwriting system config.
Fix: A RESTORE_ALLOWED_PREFIXES environment variable (comma-separated list of safe root paths) is checked before any restore job is accepted. Paths outside the whitelist are rejected with a 400 error.
# In /etc/restic-manager/config.env:
RESTORE_ALLOWED_PREFIXES=/home,/var/www,/opt/sites,/tmp/restore
If RESTORE_ALLOWED_PREFIXES is not set, restore jobs are accepted without path restriction (preserving backward compatibility with existing deployments). It is strongly recommended to set this variable in production.
Structured audit log
Risk: Previously there was no record of who performed sensitive operations (login, password change, backup trigger, restore request, user creation/deletion).
Fix: An _audit() function now appends a JSON line to /var/log/restic-manager/audit.log for every privileged action. Each entry includes a UTC timestamp, the action name, the actor (username or system), the source IP, and any relevant detail.
# Example audit.log entries
{"ts":"2026-05-09T03:12:44Z","action":"login_ok","actor":"admin","ip":"203.0.113.5","detail":""}
{"ts":"2026-05-09T03:14:01Z","action":"backup_trigger","actor":"admin","ip":"203.0.113.5","detail":"client=web-01 full=false"}
{"ts":"2026-05-09T03:44:17Z","action":"restore_job_queued","actor":"client1","ip":"10.0.0.2","detail":"snap=abc12345 target=/tmp/restore"}
{"ts":"2026-05-09T04:00:05Z","action":"user_deleted","actor":"admin","ip":"203.0.113.5","detail":"target=olduser"}
The log file is append-only (created with mode 0600, owned by root). Rotate it with logrotate or ship it to a centralised SIEM for alerting.
Keep audit logs off the backup server disk. Consider shipping them to a remote syslog or object store so an attacker who gains access to the server cannot tamper with the record of their activity.
folder_openFile layout reference
Server
/opt/restic-manager/api/ | Flask management API (backup-api.py, control_client.py, updater.py) |
/opt/restic-manager/admin/ | Static admin UI served by nginx |
/opt/restic-manager/venv/ | Python virtualenv (flask, cryptography, requests, …) |
/etc/restic-manager/config.env | Main server config (hostname, ports, TLS paths, admin user) |
/etc/restic-manager/control.json | Upstream control-server URL + grace-period + enabled flag |
/etc/restic-manager/update.json | Auto-updater channel, auto_apply, check interval |
/etc/restic-manager/update-pubkey.pem | Ed25519 key used to verify signed release bundles |
/etc/restic-manager/dr.json | DR servers, replication assignments, and pending enrolments |
/etc/restic-manager/trigger_key | SSH key the main server uses to reach agents and DR nodes |
/var/log/restic-manager/dr/ | Per-assignment DR replication logs |
/etc/restic-server/users | rest-server htpasswd (bcrypt) |
/var/lib/restic-repos/ | Repository data — big, keep on roomy storage |
/var/lib/restic-manager/updates/ | Auto-updater work dir (download/, stage/, rollback/) |
/var/log/restic-server/ | rest-server + API logs (also in journalctl) |
Linux agent
/etc/restic-agent/config.env | Server URL, repo, paths, retention |
/etc/restic-agent/repo.password | Repository encryption password (600 root) |
/etc/restic-agent/client-id | Server-assigned client UUID |
/usr/local/bin/restic | restic binary |
/etc/systemd/system/restic-backup.service | One-shot backup service |
/etc/systemd/system/restic-backup.timer | Schedule (OnCalendar or OnUnitActiveSec) |
macOS agent
/etc/restic-agent/config.env | Same config as Linux |
/Library/LaunchDaemons/com.backupbloc.agent.plist | launchd job (StartCalendarInterval) |
/usr/local/bin/restic or /opt/homebrew/bin/restic | Homebrew-installed restic |
/var/log/backupbloc/ | Per-run logs |
DR node
/var/lib/restic-repos/ | Append-only mirror of the replicated repositories |
/etc/restic-server/users | rest-server htpasswd (append-only mode) |
/etc/backupbloc-dr/version | DR node version marker |
/root/.ssh/authorized_keys | Holds the main server's trigger key (added at install) |
/usr/local/bin/restic, rclone, rest-server | Tools installed by install-dr.sh |
settingsService management
# --- SERVER ---
systemctl restart restic-rest-server # restic repo server
systemctl restart restic-api # Flask mgmt API
systemctl restart nginx # reverse proxy + UI
# --- LINUX AGENT ---
systemctl status restic-backup.timer
systemctl start restic-backup.service # run now
journalctl -u restic-backup.service -n 50
# --- macOS AGENT ---
sudo launchctl kickstart -k system/com.backupbloc.agent
sudo launchctl list | grep backupbloc
# --- DR NODE ---
systemctl status restic-rest-server # append-only repo server
tail -f /var/log/restic-manager/dr/*.log # replication logs (on the MAIN server)
bug_reportTroubleshooting
Server installer stops at Let's Encrypt
Make sure port 80 is open to the public internet and DNS for the domain resolves to this host. Certbot runs standalone and binds port 80 itself — nothing else can be there.
Server UI loads but login fails
Check journalctl -u restic-api -n 50. The most common causes are: cryptography missing from the venv (rerun the installer or pip install cryptography), or the admin hash in /etc/restic-server/users uses a format rest-server doesn't accept (only bcrypt and APR1-MD5 are supported — the installer picks one automatically).
Agent fails at "fetching REST credentials"
The license bootstrap endpoint returned an error. Either the IP hasn't been authorised (server license), the Client Access Key is wrong / revoked, or the agent can't reach the server on port 443. curl -v https://SERVER/api/health from the agent will tell you which.
Agent registers but no snapshots appear
- Trigger a run:
sudo systemctl start restic-backup.service(Linux) orsudo launchctl kickstart -k system/com.backupbloc.agent(macOS). - Check the log:
journalctl -u restic-backup.service -n 100or/var/log/backupbloc/. - If restic complains about unable to open config file, the repository wasn't initialised. On the server:
RESTIC_PASSWORD=<pass> restic init --repo /var/lib/restic-repos/<name>.
macOS agent skips user directories
Almost always Full Disk Access. Re-check System Settings → Privacy & Security → Full Disk Access — restic and /bin/bash must both be present and toggled on.
Certbot renewal didn't restart the services
The installer drops a deploy-hook at /etc/letsencrypt/renewal-hooks/deploy/restic-perms-restart.sh. Verify it exists and is executable. Run certbot renew --dry-run — it should print "deploy hook" in the output.
DR server stuck on "Awaiting SSH key"
The main server can't SSH into the DR yet. Run the authorise command shown in the panel on the DR host (it appends the main server's key to /root/.ssh/authorized_keys), confirm the SSH port matches what you entered, then click Test connection. From the main server you can sanity-check with ssh -i /etc/restic-manager/trigger_key root@DR_IP echo ok.
DR replication shows "failed"
Open Manage replication → View log for the repo. Common causes: the DR ran out of disk (free space is shown on the server card), rclone/restic not installed on a manually-added DR, or a transient SSH drop (the next scheduled sync retries automatically). The replication is append-only, so a failed run never damages the existing DR copy.
Restore from DR can't read the copy
The main server reads the DR repository over SSH with restic's SFTP backend. Ensure the DR is Online (Test connection) and that the repo password file exists on the main server at /etc/restic-manager/<repo>.password — it's the same password used for the live repository.