Skip to content

Release history

Borg Backup Server releases

Centrally manage BorgBackup across endpoints

All releases

100 shown

No immediate action
v2.60.2 Breaking risk

Windows NUL fix + report alignment

No immediate action
v2.60.1 Bug fix

Windows cache lock fix

No immediate action
v2.60.0 New feature

Live filesystem browser

Config change
v2.55.5 New feature
Auth Breaking upgrade

Repository list API + secret exposure

No immediate action
v2.55.4 Bug fix

ClickHouse fix + metadata preservation

No immediate action
v2.55.3 Bug fix

Offline agent auto‑retry fix

Review required
v2.55.2 New feature
Auth RBAC

Maintenance & S3 credential APIs

No immediate action
v2.55.1 Bug fix

Settings error fix + 24h schedule

No immediate action
v2.55.0 Mixed

Admin APIs + Dashboard update

No immediate action
v2.54.0 Mixed

Dashboard redesign + SSH key fix

Review required
v2.53.3 Breaking risk
Auth

ClickHouse logger removal

No immediate action
v2.53.2 Breaking risk

SMTP sending + dashboard‑legacy removal + stat cards

No immediate action
v2.53.1 Breaking risk

Agent update failure email backoff

v2.53.0 Breaking risk
⚠ Upgrade required
  • Docker image will be rebuilt automatically by GitHub Actions; no manual reinstall needed.
  • Migration script `084_apprise_mailto_mode.sql` backfills missing `mode=` on existing Apprise email services.
  • Deprecation: Dropbear‑specific SSH options (`UserKnownHostsFile`, `LogLevel`) are stripped at agent startup to avoid warnings.
Breaking changes
  • Agent version jumped from 2.29.13 to 2.53.0 to match the server; existing agents auto‑update on next poll.
Notable features
  • Auto‑retry offline‑induced backup failures (default 3 attempts) with configurable hysteresis and reconnect logging.
  • Apprise wizard now always emits explicit `mode=` for mailto URLs to enforce STARTTLS or SSL encryption.
  • Shell hook environment variables (`BBS_*`) are injected and optional repository credentials can be exposed via `BORG_PASSCOMMAND`.
Full changelog

Notable change: synchronized versioning

The agent and server now share the same version number. The agent jumped from 2.29.13 to 2.53.0 to match the server. Going forward both bump together. Existing agents will auto-update on their next poll.

Bug fixes

  • Apprise email STARTTLS — the wizard was emitting mailto://…:587 URLs without an explicit mode= parameter, and apprise's default for mailto:// falls through to insecure SMTP. AWS SES (and any RFC-compliant submission server on 587) rejects AUTH-before-STARTTLS with 530 Must issue a STARTTLS command first. The wizard now always emits ?mode=ssl|starttls|insecure so the encryption mode is unambiguous regardless of scheme. Migration 084 backfills existing services using a port-based heuristic. Reported as a "forward slash in password" issue (#250 follow-up); the slash was a red herring — apprise correctly URL-decodes %2F/.
  • Apprise dispatch failures no longer flood the dashboard — push delivery problems now log at warning instead of error, so they don't conflate with backup-job failures in the Errors tile and Jobs (24h) chart. last_emailed_at is now stamped on every email attempt, not just success, so a misconfigured mailer can't loop and retry on every occurrence.
  • Recent Activity rows on client detail pages are now clickable — they navigate to the queue detail for that job, matching how /queue already worked.
  • Repository size for remote SSH repos — chained fallback now tries du -sk over SSH first (most accurate, captures FS overhead, segment files, indexes, and uncompacted data), falls back to borg info's cache.stats.unique_csize for borg-only shells like BorgBase, and finally to SUM(deduplicated_size). Combines #245 (credit @c0dr1ver) with the v2.52.2 fix from #258 (#259).

#249 — laptop offline mid-backup

A laptop that disconnected mid-backup used to leave the user in a bad spot: the in-flight backup got marked failed silently, the agent's reconnect wasn't visible in the log, no email arrived (dedup), and there was no retry. This release addresses all of it:

  • Auto-retry on offline-induced failure (default on, 3 attempts max) — when the offline sweep marks a backup failed because the agent went offline, the same plan is re-queued automatically. The agent picks it up on the next poll after reconnect. Real errors (borg path missing, repo locked, encryption failure) come in via a different path and are not retried, so a genuinely broken backup can't loop forever. Configurable in Settings → General → Agent.
  • Hysteresis on offline notifications (default 5 minutes) — the agent's status still flips to offline at the 90s threshold (so dashboards and queues react quickly), but the user-visible notification, email, and push wait until the agent has been continuously offline for at least 5 minutes. Brief network blips and short laptop suspends never become alerts. Configurable in Settings → General → Agent.
  • Reconnect log line — the offline → online transition now writes a server_log info row alongside the existing notification, so the log timeline shows the recovery, not just the outage.
  • Dedup-escalation re-email — if a backup_failed notification accumulates new occurrences and 6+ hours have passed since the last email, the email re-fires. Previously dedup silently swallowed every repeat.
  • SMTP-not-configured banner on Settings → Email — surfaces when an email_on_* toggle is enabled but SMTP isn't configured, so users aren't silently un-notified.

#247 — Dropbear SSH on agents

Enigma2, BusyBox, and embedded Linux clients use Dropbear's dbclient, which doesn't recognize OpenSSH-only options like UserKnownHostsFile and LogLevel and complains for each one. Backups completed fine but the warnings were loud. The agent now detects Dropbear at startup (parses ssh -V), strips the OpenSSH -o flags from BORG_RSH, and uses -y -y for hostkey bypass instead.

#250 — Shell hook context vars + opt-in credential exposure

The wiki documented BBS_* env vars but the agent code never set them — subprocess.run() was called without an env= parameter, so hooks only inherited the agent's plain environment.

  • BBS_ARCHIVE_NAME, BBS_REPO_PATH, BBS_BACKUP_PLAN, BBS_CLIENT_NAME, BBS_BACKUP_STATUS, BBS_DIRECTORIES, BBS_JOB_ID are now actually injected.
  • New per-config opt-in "Expose repository credentials to script (advanced)" sets BORG_PASSCOMMAND (pointing at a mode-0600 temp file) and BORG_REPO. We use BORG_PASSCOMMAND rather than BORG_PASSPHRASE because env vars are inherited by every subprocess the script spawns — BORG_PASSCOMMAND is only consulted when something explicitly shells out to read it (which is borg itself by design), so the leak surface is much smaller. The temp file is deleted as soon as the script exits. The wiki has full details and a streaming pg_dump example.

Migrations

Run automatically via bbs-update:

  • 083_offline_retry_and_notify_escalation.sql — adds retry_count, parent_job_id to backup_jobs; last_emailed_at to notifications
  • 084_apprise_mailto_mode.sql — backfills mode= on existing Apprise email services

Notes

  • Docker image will be rebuilt automatically by GitHub Actions.
  • The agent will auto-update from 2.29.x to 2.53.0 on the next poll. No manual reinstall required.
v2.52.2 Bug fix
Notable features
  • Admin summary API (`GET /api/v1/summary`) returns plans, enabled flag, repo ID/name, and last terminal backup result
  • Queue and Schedule pages visual refresh
Full changelog

Bug fixes

  • Dashboard Errors (24h) tile now picks up backups that failed because the agent went offline. The scheduler's offline-job sweep was logging at level=warning while the dashboard counted only level=error rows, so those failures were silently invisible. (#243)
  • Repository size for catalog-synced and remote SSH repos now matches borg info <repo> (cache.stats.unique_csize) instead of summing each archive's incremental deduplicated_size — the old approach drifted to nonsense values after pruning or compacting. Local repos continue to use du. (#258)
  • Job detail page: the empty dark "black box" below the Stats card on completed jobs is gone. The two cards no longer force equal heights, so each sizes to its own content. (#244)
  • Schedules week view: when a backup failure happened in a previous week, the failed-block marker no longer silently disappears once the week rolls over. Failed runs in today's lane also stay clickable instead of shrinking to a 1-pixel sliver as time elapses.
  • Queue page header counters and metric tiles now refresh in sync with the live tables every 10 seconds. Previously the slot counters, 24h totals, and average duration drifted to stale values within seconds of page load.

Hardening

  • ClickHouse log growth in Docker installs is now capped. A Poco log-rotation glitch could cause stderr.log to grow ~12 GB/day on some installs (regression of #79); the container now self-heals on startup if either log is over 100 MB and a cron entry truncates them every 5 minutes when over 50 MB. The fix bounds disk usage without re-introducing the silent-startup-failure regression from #88. (#252)
  • Admin summary API (GET /api/v1/summary) now reports the last terminal backup result instead of occasionally returning a queued/running job, exposes enabled, repository_id, and repository_name per plan, and uses a windowed query that scales linearly with job history.
  • Schedule view queries are bounded to the last 30 days. Previously the page scanned every historical job for every plan on each load.

New

  • GET /api/v1/summary admin API — single token-authenticated call that returns each client with its plans and last backup result. (#254)
  • Queue and Schedule pages received a visual refresh aligning their styling. (#251, #253)

Notes

  • No agent changes in this release — AGENT_VERSION stays at 2.29.11.
  • The Docker image will be rebuilt automatically by GitHub Actions; the ClickHouse log cap is only present in the rebuilt image, not via in-place server update.
v2.52.1 Bug fix

Fixed dashboard activity chart erroneously showing error bars when no log entries exist and corrected upgrade page top cropping.

Full changelog

Bug fixes

  • Dashboard activity chart still showed phantom error bars (#240) — the v2.52.0 fix matched the tile count to /log entries, but the "Jobs (Last 24h)" chart was still drawing red bars from failed_jobs + unresolved alerts, so the chart could show error bars at hours where the Log page was empty. Chart now reads from the same server_log query the tile and Log page use.
  • Upgrade page top cropped, can't scroll up (#239) — the auth layout used align-items: center, which works for the login form (small, fits in viewport) but pushed the top of a tall upgrade page (steps + release notes) above the visible area with no way to scroll up to it. Override to flex-start for the upgrade page only; login pages keep their centered layout.

Other

  • Enigma2 set-top-box detection in the agent installer (#230)install.sh detect_os() now recognizes Dreambox / OpenATV / OE-Alliance / OpenPLi devices via /proc/stb/info/model and falls back through three image-info sources (OE-Alliance /etc/image-version, OpenATV /usr/lib/enigma.info, Python boxbranding). Detection only — Enigma2 still hits "Unsupported OS" in the package-install step (manual borg + python3 install required), but the OS is now correctly named in the install summary. Thanks @MegaV0lt.

Versions

  • Server: 2.52.1
  • Agent: 2.29.11 (unchanged)
v2.52.0 Breaking risk
Breaking changes
  • settings.value column type changed from TEXT to MEDIUMTEXT in migration 082_settings_value_mediumtext.sql
Full changelog

What's new

Schedules page redesign (#225)

Replaces the per-hour load histogram with a duration-driven horizontal timeline, where each block's width tracks the estimated backup duration. Day view gets a "concrete" treatment for the already-completed portion of each job, a bright current-time line, larger tooltips, uppercase job/client labels, and a unified blue palette to match the dashboard. Big thanks to @c0dr1ver for the design and implementation.

Bug fixes

  • Branding upload broken (#238) — uploading a navbar icon, login logo, or app icon failed with Data too long for column 'value'. Migrated settings.value from TEXT (65 KiB) to MEDIUMTEXT (16 MiB).
  • CentOS / RHEL agent stuck offline (#237) — Cloudflare's bot rules block Python's default Python-urllib User-Agent, so a BBS install fronted by Cloudflare returned 403 to every agent request. Agent now sends User-Agent: BBS-Agent/<version>. If you have stricter Cloudflare WAF/Bot-Fight rules, add a Page Rule (or WAF skip rule) for /api/agent/*.
  • Windows / macOS backups always flagged "completed with warnings" (#236) — agents tripped had_warnings on every backup because OS-level lockouts on system files (NTUSER.DAT, SIP-protected caches, iCloud Mobile Documents) were treated as actionable warnings. Added the locale-independent error codes ([WinError 5], [Errno 13], [Errno 1], [Errno 11]) to the agent's routine-warnings list.
  • Dashboard "Errors (24h)" tile out of sync with linked log page (#235) — the count summed log errors + failed jobs + alerts but the linked /log page only shows server-log entries, so the count and the linked page could disagree. Count now matches what /log shows. Operational alerts remain visible on /notifications and the navbar bell.

Other

  • Server-side job finalize-race logging (#227 follow-up) — when the agent's stall detector marks a running server-side job as failed mid-flight, the scheduler now writes a warning row to server_log so admins can see it in the activity log, not just in journalctl. Credit @SAY-5 (PR #228) for the diagnostic improvement.
  • Trimmed dead histogram computation from ScheduleController after the timeline view replaced it.

Versions

  • Server: 2.52.0
  • Agent: 2.29.11

Migration

Adds migration 082_settings_value_mediumtext.sql. Runs automatically via bbs-update.

v2.51.1 Breaking risk

Fixed backup progress display, long path overflow, OIDC login on PHP 8.4, archive delete stall handling.

Full changelog

Bug-fix release. Server 2.51.0 → 2.51.1, agent 2.29.8 → 2.29.9.

Fixes

  • Backup progress showed nonsense like "83 GB of 17 B" (#234): borg 1.4 fires progress_percent events during backup for internal phases where current/total are item counts, not bytes. The agent was forwarding those as bytes_total. Now restore-only.
  • Long file paths still overflowed the queue-detail card (#233): earlier #108 / #209 fixes wrapped text inside the progress card, but the page column itself (.main-content, a flex child) lacked min-width: 0, so unbreakable strings could blow past the viewport regardless. Site-wide fix.
  • Dashboard "Errors (24h)" tile linked to all errors (#232): the link had no time filter, so clicking the 24h count showed errors days old. LogController now accepts ?hours=N and the tile passes hours=24.
  • OIDC login broken on PHP 8.4 (#231): jumbojett/openid-connect-php deprecation warnings (implicit nullable params) hit the response body before the redirect Location header, triggering "headers already sent". Drop E_DEPRECATED for the OIDC call so upstream's noise stays out of our output.
  • Archive delete marked completed despite agent stall-abandon (#227): archive_delete was missing from the agent's stall-detection exclusion list, so agents flagged it abandoned ~30 min into a long delete; then scheduler's unconditional finalize UPDATE clobbered the resulting 'failed' with 'completed'. Both fixed (exclusion list + finalize scoped to WHERE status='running').
  • Upgrade page stretched to full viewport on wide displays (#226): wrapped in a 1100px max-width centered container so steps and release-notes paragraphs stay readable.
v2.51.0 Bug fix
⚠ Upgrade required
  • AGENT_VERSION bumped to 2.29.8; agents auto‑update.
  • If custom Branding is used, note the updated navbar‑icon/login‑logo limits and restored Login Page Theme override.
Notable features
  • Consistent card headers across all UI pages
  • Tighter type sizes site‑wide (tables, headers, topbar)
  • New Branding settings: single App Icon upload and static‑asset caching
Full changelog

The brand-cleanup work continues. This release is primarily UI polish — consistent visual language across the app, smaller and tighter type, less noise in the activity log, plus a few real bug fixes. No database changes; safe in-place upgrade.

UI overhaul

  • Card headers — one consistent style. Settings (light blue), Clients (transparent), and Dashboard (dark gradient) used three different palettes for what should be the same thing. Standard .card-header is now a subtle neutral surface in both themes; the dark navy gradient on Dashboard / Schedules / Archive Detail collapses to the same neutral in light mode so a light page is never broken up by a dark block.
  • Tighter type, site-wide. Card headers shrunk from ~16px to 14px. Table headers switched from ALL-CAPS 13.6px to mixed-case 14px. Table body cells capped at 14px (was Bootstrap's 16px default). Topbar page title and username dropdown nudged down ~2px each.
  • Storage Locations page — three floating <h5> section titles ("Local Storage", "Remote SSH", "S3 Offsite Sync") replaced with proper card-headers. Each section now has a card frame with the action button (Add Location / Add SSH Host) inside the header, matching the rest of the app.
  • Schedules page colors toned down. Per-agent block colors went from hsl(h, 55%, 45%) to hsl(h, 32%, 38%) so they sit in the dark-navy palette instead of looking like a saturated bar chart.
  • Server Health card (dashboard) — fixed-width label and value columns so every progress bar starts and ends at the same x-coordinate across rows. Smaller, tighter type. No more dead whitespace between bar and short values like "0.48".
  • Recent Activity on the client status tab — list reformatted as a proper table (Status / Task / Repo / Time / Date), 14px text, 20 rows instead of 10. table-responsive so it scrolls horizontally on phones.
  • Sidebar version pill — was illegible in light mode (gray-on-dark-navy). Now uses light-on-dark colors hardcoded since the sidebar is always dark.
  • Mobile login gets a small header logo above the form (uses your branded navbar icon if set, otherwise the favicon mascot). Form pane fills the screen on phones.
  • Light theme login is back. Auth layout now respects the Login Page Theme override in Branding settings (which is also re-enabled — was temporarily disabled while the light variant was missing).

Branding system

  • New "App Icon / Favorite Icon" upload in Branding settings. Single 512×512 transparent PNG; BBS resizes it on demand to every favicon / Apple touch / PWA size needed. No more uploading half a dozen sizes manually. Bundled mascot is the default fallback.
  • Navbar Icon description now mentions it doubles as the small mobile-login header logo.
  • Topbar mascot image was 1536×1024 / 2.4MB shipping for a 115px display. Resized to 400×266 / 82KB — ~30× smaller. (#223)
  • Static-asset caching added via mod_headers in public/.htaccess: 7-day cache for images and fonts, 1-day for CSS/JS. Previous behavior issued a conditional GET on every page load.

Bug fixes

  • Activity log noise filtered. Routine borg warnings (Permanently added <host> to known hosts, file changed while we backed it up, stat: [Errno 2] on transient files) no longer flag a backup as "completed with warnings" or fire a notification. Added -o LogLevel=ERROR to BORG_RSH to suppress the SSH known-hosts notice at the source. Agents pick this up via the auto-update mechanism. (#225 / #223)
  • Upgrade page no longer cropped by the login mascot artwork. The post-upgrade result page now suppresses the split-pane art and uses the full frame for the upgrade log. (#222)
  • Long file paths in /notifications now wrap properly instead of overflowing the message column off the right edge.
  • Queue page duration (PR #207 by @c0dr1ver) — durations ≥ 1 hour now format as Hh Mm Ss instead of dropping the hour. Completed-row durations also get a colored progress bar showing relative job length.
  • Files in Archive count (#192) — tile now headlines the catalog-derived count (what's actually navigable in the restore browser); borg's nfiles shown as a small caption only when it differs by more than 1% (typical on systems with heavy hardlinks where borg over-counts).

Heads-up

If you've customized the Branding section, the navbar-icon and login-logo upload limits changed in v2.50.0. The new App Icon section is additive — your existing logos still work. The Login Page Theme override is functional again in light mode.

AGENT_VERSION bumped to 2.29.8; existing agents will pick this up via the server-pushed update mechanism. No manual reinstall needed.

v2.50.1 Bug fix
Notable features
  • Mobile login screen displays a header logo on phones
Full changelog

Patch release with two fixes and one small UI improvement on top of v2.50.0.

Fixes

  • Backup jobs no longer fail when borg's cache lock cleanup hiccups (#214). Borg occasionally raises borg.locking.NotLocked at the very end of a successful backup when releasing its local cache lock — the archive is already written and intact, but the cleanup error caused BBS to mark the job as failed. The agent now detects this specific case and reports the job as completed with warnings instead of failed. The full traceback is preserved in the job log so the underlying cause is still visible. Bumps AGENT_VERSION to 2.29.7; existing agents will pick this up on the next server-pushed update.

  • Smaller PNG payloads across the UI (#220). All bundled PNG assets compressed via pngquant — no visual change, just smaller files and faster page loads. Thanks to the contributor for the PR.

UI

  • Mobile login screen now shows a header logo. The split-pane art is hidden on phones, so the form pane was previously untethered from any branding cue. Phone widths now show an 88px logo above the form. Falls back through: branded navbar icon (if set in Branding settings) → bundled mascot favicon as default. The larger login-page logo is intentionally not used on mobile because it's sized for a 500px column and would crowd a phone-width pane.

Heads-up

If you saw the SSH lockout from the brief chown root:root authorized_keys change earlier today: that was reverted before this release was tagged. v2.50.1 ships the corrected behavior — authorized_keys is back to user-owned mode 600, which is the supported configuration.

v2.50.0 New feature
⚠ Upgrade required
  • Custom branding users should test on non‑production first due to changed upload limits and disabled Login Page Theme override
  • Login page is dark-only temporarily while design settles
Notable features
  • Redesigned login page with split-pane layout, glassy ribbon, dark theme across all auth screens
  • Files-in-archive count headlines catalog-derived total; shows borg `nfiles` caption when differing
Full changelog

A fresh start on branding

This release kicks off a brand cleanup that's been a long time coming. There's a new mascot logo, a redesigned login screen, and rebuilt favicon/PWA icons — all wired through a new branding system that lets you upload larger custom logos and see the new defaults in the preview pane.

What's new

  • New mascot logo in the topbar, on the login screen, and across favicons (browser tab, Apple home-screen, PWA install).
  • Redesigned login page — split-pane layout with the mascot artwork on the left, a glassy feature ribbon, drifting binary-stream background, and a unified dark theme across login / forgot-password / 2FA / reset-password.
  • Branding settings rework — bigger upload limits (Navbar Icon up to 360×200, Login Logo up to 800×800), preview pane that mocks the actual rendering, and the default-fallback previews now show what the app actually ships with rather than a stale legacy icon.
  • Files-in-archive count (#192) now headlines the catalog-derived total (what's actually navigable in the restore browser), with borg's nfiles shown as a small caption when it differs noticeably — it can run 2-3× higher on systems with heavy hardlinks.

Heads-up if you rely on custom branding

The branding system still needs more work. Upload limits and preview rendering have changed, the login page is dark-only for now (the Login Page Theme override is temporarily disabled while the design settles), and not every screen has been re-checked against custom logos yet.

If you've customized BBS with your own logo or login theme, please test this release on a non-production install first to see how things look before rolling it out on a production server. If the new defaults work for you, you'll have the smoothest upgrade path.

What's coming

There's still a lot of interface work ahead. Expect more polish landing in the coming weeks — small fixes, larger redesigns, and more thoughtful defaults across the dashboard, schedules, and settings screens. This release is the starting point, not the finish line.

I hope you like the new logo. It's here to stay.

v2.29.5 Bug fix
Notable features
  • Telegram thread/topic support via optional Thread field in Apprise notifications
  • Dropbear SSH key conversion for embedded systems
Full changelog

Server bumps from 2.29.1 → 2.29.5 to re-align with the agent (also at 2.29.5).

Fixes

  • Queue list / detail badges (#208): Running, Sent, and Queued now use Bootstrap 5.3 text-bg-* tokens to match the Dashboard — no more cyan-on-white or white-on-yellow.
  • Queue detail overflow (#209): when borg's status_message contains a long file path, the running progress card no longer stretches past the viewport. The original #108 fix only covered Directories / Borg Options / log content.
  • Import existing repository (#210): the local-storage branch had been dropped from the dispatch in an earlier refactor, so POSTing the import form for a local repo returned no body and the browser sat on a blank /repositories/import. Restored the else branch that calls importLocal().
  • Queued agent updates blocking backups (#206): management tasks (update_borg / update_agent) bypassed the slot-count check on the way in but still incremented the counter on the way out, so a batch of queued updates for offline agents could fill the imaginary slot budget and block backups. Counter now stays in sync with the bypass.
  • Schedules view (#202, thanks @c0dr1ver): current-time indicators on the schedule view, dashboard duration formatting fixed for jobs over an hour, and missed-schedule alerts surfaced on the dashboard.

Features

  • Telegram thread/topic support (#205, thanks @c0dr1ver): Apprise notification setup now has an optional Thread field that emits tgram://bot_token/chat_id:thread, for groups with topics enabled.
  • Dropbear SSH key conversion (#201, thanks @MegaV0lt): install.sh now detects a Dropbear ssh and runs dropbearconvert so the server-issued OpenSSH key works on embedded systems (OpenATV / Enigma2 receivers, etc.). Plus a follow-up agent change so the same conversion runs whenever the agent re-downloads the key after an auth failure (e.g. after a server-side rotation).
v2.29.1 Bug fix
Notable features
  • Containerized agent image (Docker) supporting multi-arch and self‑updates
Full changelog

Highlights

🐋 New: Containerized agent image

The BBS agent now ships as a Docker image for hosts where the native installer isn't practical — TrueNAS Scale, Synology DSM, unRAID, UGreen NAS, and any appliance OS where you can't persist a system-installed package.

docker hub: marcpope/borgbackupserver-agent:latest
  • Multi-arch (linux/amd64 and linux/arm64)
  • Includes borg, ssh, and clients for MySQL, PostgreSQL, and MongoDB so one image covers every backup type
  • Self-updates from the server on protocol bumps; reset by pulling a new image tag
  • Fully documented in the Docker Agent Setup wiki page

Bring it up with a docker-compose.yml that mirror-mounts the host paths you want backed up — see the wiki for examples. Addresses #193.

Bug fixes

  • Restore jobs now report failure correctly. Previously borg extract warnings (exit code 1) were treated as success and a path-filter mismatch returned 0 with no error — both showed a green check on the queue page. Restores now fail loudly when borg reports warnings or extracts nothing.
  • Restore stats are accurate. The Stats panel for restore jobs (Files Total / Files Processed / Bytes Total / Bytes Processed) used to be empty or show byte counts in the file fields. All four are now populated correctly using borg extract --list output.
  • Restore triggers land on the queue detail page. Previously, manually triggering a file/MySQL/Postgres/Mongo restore redirected back to the client page, forcing you to navigate to Queue and find the new job. Now restores land directly on /queue/<job_id>, matching the existing manual-backup behavior.
v2.29.0 New feature
Notable features
  • Agent and server versions now match at 2.29.0
Full changelog

Fixes

  • Server Health layout (#199): label column now content-sized (left-aligned, ellipsis past 140px) so short labels like CPU/Memory hug the card edge and the progress bars keep the rest of the row on narrow cards.
  • Badge consistency (#200): solid-color badges across the app now use Bootstrap 5.3's text-bg-* tokens so text contrast is automatic and uniform. Semantic "info" (log-level badge, Upgrade Agents pill, notification Info/New pills) switches from cyan to blue where the cyan+white combo was hard to read.
  • Manual borg updates now register (#198): the agent re-reports system info hourly, so clients that can't use auto-update (e.g. armv7l without pip) will have their version picked up on the server within an hour of a manual install. AGENT_VERSION bumped to 2.29.0 to match.

Polish

  • Footer: "Open Source & Made with ❤ by Marc Pope — Sponsor" replaces the standalone heart/Sponsor link; footer text size trimmed a notch.

Agent and server versions now match at 2.29.0.

v2.28.9 Bug fix

Fixed Windows installer directory creation, clamped dedup savings display at 99.9%, increased Borg lock wait time to 10 minutes, and added encryption control for mail settings.

Full changelog

Bug Fixes

  • Windows installer (#195): Fresh installs failed with a DirectoryNotFoundException when writing the SSH-path marker file. The installer now creates C:\ProgramData\bbs-agent\ up front before any writes.
  • Dedup savings display (#191): Rounding lifted 99.95%+ to 100% even when the repo still held bytes on disk. The dashboard, storage cards, email reports, and archive detail now clamp at 99.9% when dedupe size is non-zero.
  • borg lock timeout (#194): Agent-side borg create and borg extract ran without --lock-wait, so borg's 1-second default turned any brief lock contention into an immediate Failed to create/acquire the lock ... (timeout) failure. Agent commands now pass --lock-wait=600 (10 minutes), matching the server-side operations.
  • Mail settings (#197): The Email Settings UI had no encryption control and the Mailer hardcoded STARTTLS for port 587 only, so SMTP servers on port 465 (implicit TLS) just hung. Added an Encryption dropdown (STARTTLS / SSL/TLS / None) with a port-based default, and taught the Mailer and Test SMTP button to honor it. Also fixed Test SMTP passing the still-encrypted password to AUTH LOGIN.
v2.28.8 Bug fix
Notable features
  • CachyOS support in agent installer
Full changelog

Fixes

  • ClickHouse self-heal (#189): container start now also repairs mode bits (not just ownership) and stops hiding chown errors. Resolves directory_iterator: Permission denied / ASYNC_LOAD_WAIT_FAILED on the file_catalog table and surfaces real problems on FUSE filesystems (Unraid /mnt/user shfs) where ownership changes are silently dropped.
  • Failure email mislabels the task (#185): every failed task (update_borg, check, prune, compact, catalog_rebuild, …) now sends an email with the correct label instead of always saying "Backup Failed".
  • Email time in user's timezone (#186): the Time: line in notification emails is now formatted in each recipient's configured timezone with the zone abbreviation (e.g. CEST) instead of always UTC.
  • Upgrade gate blocked by offline agents (#184): the server-upgrade precheck now ignores jobs stuck sent to an offline agent, and skips management/server-side task types — matching the filter the queue scheduler already uses.
  • Daily borg-update spam on unsupported arches (#187): agents whose architecture has no GitHub or server-hosted binary (armv7l, some BSDs) are no longer auto-queued for daily updates they can't complete. Manual Update Borg from the client page still works.
  • Backup duration formatting (#190, thanks @c0dr1ver): completion logs and notifications now show 1h 12m / 27m 5s instead of raw seconds.
  • CachyOS support (#188, thanks @ChrSchu90): agent installer recognizes CachyOS alongside Arch/Manjaro/EndeavourOS.
v2.28.7 Bug fix
Notable features
  • Auto-update skips agents already at the target version
  • Per-user low‑storage alert thresholds (set per profile)
  • Activity icons on Dashboard Recently Completed page
Full changelog

Bug fixes, enhancements, and UI polish. Agent bumped to v2.25.1 for the restore-progress change.

Fixes

  • Daily email report counted only one repo per client (#175): the report picked a single "last backup" per client, so clients with two or more backup plans reported only one plan's size and file count. It now aggregates the latest completed/failed backup per plan per client and sums size and files across all of them. Clients with a mix of ok and failed plans now show as "Partial" (orange) so it's visible at a glance.
  • "Last generated" timestamp always showed the current time (#176): the scheduler regenerates the day's report every minute to keep numbers fresh, and an earlier fix was bumping created_at on every regenerate. Now only the manual "Generate Report Now" button updates the timestamp; scheduled refreshes leave it alone.
  • Restore stuck on "Starting task..." (#168): borg extract now runs with --progress and the agent forwards progress_percent events so the UI shows a live progress bar during restore, matching the backup experience.
  • Schedule page descenders clipped on hover (#171): the :hover transform was scaling the block, which clipped the bottom pixel row of letters like g/p/y. Replaced with a stronger box-shadow — same lift cue without the clipping.
  • Repo detail status badges hard to read (#169): warning/info badges now use text-dark for contrast instead of default white-on-light.

Enhancements

  • Auto-update stops re-queueing up-to-date agents (#174): the daily update_borg sweep compares each agent's current borg version against the target and skips the ones already at or past it, instead of re-installing the same binary every day. Log line now breaks out queued vs already-current vs incompatible counts.
  • Per-user low-storage alert thresholds were already added in 2.28.5 — if you missed it, each user now sets their own trigger (percent used or free GB) under Profile → Account → Low-Storage Alerts.
  • Activity icons on Dashboard Recently Completed (#172): same task-type icon vocabulary as the Queue page — backup / prune / compact / restore / update / etc. — so the two pages read consistently.
  • Server Health drops redundant root row (#178): when /var/bbs sits on the root filesystem, the widget no longer shows / and /var/bbs as two identical rows.
  • Clients sorted alphabetically (#182): the Clients list is now sorted by name instead of creation order.
  • Version prefix consistency (#170): the Clients page showed raw version numbers where the rest of the app said v<num>; aligned them.
v2.28.6 Bug fix

Fixed Windows installer failure when borg.exe moved to archive root.

Full changelog

Windows installer hotfix.

Fix

  • Windows install fails with "borg.exe not found" (#180): borg-windows v1.4.4-win6 changed its zip layout — borg.exe now lives at the archive root instead of under a borg/ subdirectory. The installer was hardcoded to the old layout and bailed out. Installer now locates borg.exe wherever the release puts it and derives the PATH addition from that location. Pre-install cleanup also wipes everything under $BorgDir except ssh\ so upgrades from the old layout can't leave stale files behind.

UI

  • Server Health panel label column widened and right-aligned; inter-column gap tightened.
v2.28.5 New feature
Notable features
  • Per-user low‑storage alert thresholds with percentage used or free space in GB, plus a Disabled option; notifications are scoped per user.
Full changelog

Bug fixes + new feature.

New

  • Per-user low-storage alert thresholds (#156): each user now picks their own trigger under Profile → Account → Low-Storage Alerts. Two modes — percentage used or free space in GB — plus a Disabled option. Notifications and emails are scoped per-user; everyone gets the alert tuned to their own preference rather than sharing one server-wide number. Existing values backfill from the old server-wide threshold on upgrade so nobody's alert behavior changes unexpectedly.

Fixes

  • Windows restore to original location (#167): Borg archives from Windows clients store paths with the drive letter as the first segment (C/Users/...). The catalog indexer prepends a leading slash for display (/C/Users/...), so paths reaching the restore endpoint look like /C/Users/.... The drive-letter detection only matched the no-slash form, fell through, and the agent extracted files relative to its working directory (C:\ProgramData\bbs-agent\) instead of the original drive. Regex now accepts an optional leading slash and routes correctly back to the drive root.
  • Dashboard Server Health label column (#166): partition names like /var/bbs were clipped by a 64px label column. Widened to 110px so names fit and the bars align further right.
v2.28.4 Bug fix
⚠ Upgrade required
  • Upgrade to v2.28.4 (or later) – migration 079 ensures the `api_key` column is nullable with explicit NULL default, resolving PDOException on client addition.
Full changelog

Hotfix.

Fix

  • Adding a new client fails with "Field 'api_key' doesn't have a default value" (#173): migration 077 (part of the API-key hardening in 2.28.0) used SQL syntax that some MariaDB/MySQL versions interpret as "preserve the existing NOT NULL, just change the default." On those installs the column never actually became nullable, so every attempt to add a new client after the hardening rewrote the INSERT to use api_key_hash instead throws a fatal PDOException.

Migration 079 re-runs the change with explicit NULL DEFAULT NULL syntax that's unambiguous across versions and SQL modes. Idempotent on installs where 077 already took effect.

All v2.28.x Docker users should upgrade.

v2.28.3 Breaking risk
Notable features
  • Clients page UI: renamed "Backup Activity" to "Activity", split failed jobs into red/grey categories
  • Archive detail page redesign with metric-tile stats, improved readability and performance
Full changelog

Bug fixes and UI polish.

Fixes

  • Windows installer failed with "Missing closing brace" parse errors (#165): the .ps1 script contained UTF-8 em-dashes inside double-quoted strings. Windows PowerShell reading the file without a BOM defaults to the legacy code page (usually cp1252), where the UTF-8 byte sequence for includes 0x94 — interpreted there as a right curly quote — which prematurely terminated strings and derailed parsing into the brace errors users saw from lines much later in the file. Both install-windows.ps1 and uninstall-windows.ps1 are now pure ASCII.
  • Borg/agent updates failed on offline clients (#144): scheduled update_borg and update_agent jobs were being auto-failed the moment the agent's heartbeat timed out. Those task types now stay queued until the client next polls, so laptops asleep at the scheduled update hour quietly receive the update when they come back. A 7-day safety valve expires management jobs that have been abandoned.
  • Missed-schedule spam (no issue): each cron tick was incrementing occurrence_count and resetting read_at on the same unresolved missed-schedule row, so agents offline for days accumulated thousands of occurrences and the notification refused to stay marked read. Deduped at the scheduler level, plus a one-time cleanup migration clamps any existing inflated counts back to 1.

UI

  • Clients page (#141): "Backup Activity" renamed to "Activity". Failed jobs split into red "Backup Failed" (real data risk) and amber "Other Failed" (update jobs, plugin tests, etc.), so a laptop that was offline at update time no longer paints the chart the same color as an actual backup failure.
  • Archive detail page redesign (#132, #133): stat row rebuilt with the metric-tile pattern from the dashboard and card headers use the signature blue gradient. "Files" is now "Files in Archive" with a tooltip explaining the pre-scan vs manifest difference. Status Breakdown footer is now readable with a proper "Grand Total" row that includes directories/symlinks. Excluded-file badges swapped to a readable subtle variant. Largest Files no longer shows excluded entries (node_modules etc. that weren't backed up).
  • Archive detail page load time: deferred the "deleted vs previous archive" anti-join to a background fetch, and rewrote it as LEFT ANTI JOIN — page renders immediately even on multi-million-file archives.
  • Dashboard tweaks: Server Health progress bars switched to Bootstrap's standard .progress with an always-visible centered % overlay. File Catalog stats table uses hairline row separators, gets a transparent background, and expands to full width on mobile. Doughnut tooltips escape the 110×110 canvas as HTML overlays on <body> and show size + rows.
  • Notifications page: tightened row heights, removed the redundant "Notifications" H4 (already labeled in the nav).
  • Mobile bottom nav: 5-tab layout (Home / Clients / Queue / Settings / More) with a bottom-sheet submenu for Schedules, Log, Users, Storage, Logout. Active state highlights More when the current page is one of the inner items. Lighter top border + stronger upward shadow for definition.
  • Badge styling: site-wide consistency — medium weight (not bold), Title Case.
  • Client detail header: hostname uses a signpost icon so the globe icon clearly belongs to the IP.

❤️ Sponsor this project if you find it useful.

v2.28.2 Bug fix

Fixed ClickHouse start failures on Docker by repairing ownership and making errors visible.

Full changelog

Bug fixes and UI polish.

Fixes

  • ClickHouse silently fails to start on Docker (#158): upgrade-bitten volumes had /var/bbs/clickhouse/* owned by www-data left over from an older image build. ClickHouse refuses to run when the process user doesn't match the data owner and the error was being swallowed. Entrypoint now scans service data directories on each start and repairs ownership if it finds a mismatch. Also stops suppressing ClickHouse's stderr and adds an explicit post-start health check so the next time anything like this happens it's visible.
  • Server-side jobs could execute twice (#163): a long-running compact/prune could be re-picked by the next cron-fired scheduler instance before the first one marked the row running. The sent → running transition is now a conditional UPDATE; if the row was already claimed by another scheduler, the iteration is skipped.
  • Missing compact output in task log (#162): borg compact now runs with --verbose so the "compaction freed about X GB" summary is captured into the task log.
  • Dashboard doughnut tooltip clipped (#164): the File Catalog canvas is only 110×110, so the default canvas-rendered tooltip got cut off. Switched to an external HTML tooltip appended to <body> that escapes the canvas entirely, and the body now shows size and rows instead of duplicating the name.

UI

  • Server Health progress bars on the dashboard switched to Bootstrap's .progress / .progress-bar with an absolute-positioned percentage overlay — always centered, always visible regardless of bar width.
  • File Catalog card: stats table uses hairline row separators instead of striped backgrounds; Top Repositories list tightened up; table background made transparent so it matches the card body.
  • Client detail header: hostname uses a signpost icon so the globe icon is unambiguously the IP address.
v2.28.1 Breaking risk
⚠ Upgrade required
  • Bare metal: run `sudo /var/www/bbs/bin/bbs-update /var/www/bbs`
  • Docker: pull the `v2.28.1` image tag
  • Perform a hard refresh (Cmd/Ctrl+Shift+R) after upgrade to apply the dropdown CSS changes
Full changelog

Bug fixes.

Fixes

  • Backup report (#150, #152): the Server section now aggregates disk usage across all configured storage locations instead of only the default partition, and the on-disk footprint is computed from per-repo sizes instead of a JOIN-inflated archive sum. Regenerating a same-day report now bumps its timestamp, so the viewer header reflects the refresh.
  • Server-side compact/prune lock timeout (#159): server-side borg operations (compact, prune, check, info, list, delete) now wait up to 10 minutes for the repo lock instead of failing immediately when an agent backup or another task is still holding it. Previously any overlap produced Failed to create/acquire the lock ... (timeout).
  • Dropdown and tooltip clipping (#161, and a long list of predecessors): replaced the per-element z-index patches that kept getting rediscovered with a global fix. Every dropdown toggle now gets data-bs-strategy="fixed" and every tooltip gets container=body + boundary=viewport automatically. Also removed the transform: translateY(-1px) hover effect on card elements — the transform was creating a stacking context that trapped position: fixed descendants, defeating the z-index escape entirely. Replaced with box-shadow intensification for the same visual feedback.

Upgrade

Bare metal: sudo /var/www/bbs/bin/bbs-update /var/www/bbs
Docker: pull the v2.28.1 image tag.

Hard-refresh (Cmd/Ctrl+Shift+R) after upgrade to bypass the CSS cache for the dropdown fix.

v2.28.0 Breaking risk
Security fixes
  • Proactive hardening: tighter input validation for internal APIs/UI, restricted privileged helper inputs, and canonical filesystem path checks prevent traversal‑style attacks (no CVE ID provided)
Notable features
  • Automatic migration of agent authentication tokens to a stricter at‑rest storage scheme on next heartbeat
Full changelog

Claude Opus 4.7 (Anthropic's top model) was used to audit the codebase for security issues. Several low-to-medium findings have been addressed in this release. No confirmed exploits in the wild — this is proactive hardening.

Highlights:

  • Agent credentials — authentication tokens now use a stricter at-rest storage scheme. Migration is automatic on each agent's next heartbeat; no intervention needed.
  • Server-side privileged operations — the internal helper used for borg and SSH maintenance now accepts a narrower set of inputs, reducing the blast radius of a hypothetical admin-account compromise.
  • Input validation — several gaps in internal APIs and UI handlers have been closed against crafted input.
  • Filesystem boundaries — paths passed to privileged operations are validated against canonical forms to prevent traversal-style bypasses.

No breaking changes. No reinstall required.

Recommended: update promptly.

Bare metal: Settings → Updates → Update.

Docker:

```
docker compose pull
docker compose up -d
```

(Docker Hub multi-arch build takes ~15 min after tag.)


❤️ Sponsor this project if you find it useful.

v2.27.1 Security relevant
Security fixes
  • Dockerfile added `apt-get dist-upgrade` and a second upgrade pass after ClickHouse repo install to reduce vulnerability scan findings.
Full changelog

We are making a set of security updates. This first change helps keep your installed packages more up-to-date.

Patch release — base-image CVE hygiene. No functional changes.

  • Dockerfile: apt-get dist-upgrade added and a second upgrade pass after ClickHouse repo install. Should meaningfully reduce the Docker Hub vulnerability scan count.

Pull:

docker compose pull
docker compose up -d

Bare metal: Settings → Updates → Update.

v2.27.0 New feature
⚠ Upgrade required
  • Docker users: run `docker compose pull` then `docker compose up -d`. Bare‑metal installations should use Settings → Updates → Update.
  • Automatic UID/GID migration now runs when PUID/PGID/MYSQL_PUID/CH_PUID change; tracked via /var/bbs/config/.ownership and logs timestamped migrations.
Notable features
  • Fully redesigned homepage with hero tiles, job charts, storage location cards, recently completed table, server health auto‑refresh, and demoted ClickHouse/MariaDB panels.
  • New Schedules page featuring hour‑of‑day histograms, day‑view timeline, day pills, block context menus for editing time or plan, and inline validation.
Full changelog

Stable release of the v2.27.0 series. :latest is now updated — docker compose pull / bbs-update will upgrade.

We are relasing this for progress, in no way complete. Just wanted to get a bunch of these changes out there while we can. More to come this weekend.

🎉 New dashboard

Fully redesigned homepage addressing #146, #98, #104, #116, and #128.

  • Hero tiles — Clients, Running, Recovery Points (replaces the Clickhouse-rows emphasis), Errors. Colored tinted backgrounds for quick visual parsing.
  • Jobs (24h) chart + Backup Summary (original data, on-disk footprint, dedup savings %, last backup time) + Server Health (CPU/memory/partition horizontal bars).
  • Storage Locations — every configured local location AND every remote SSH host as its own card with per-location disk %, used/free, repo count, and on-disk size. Auto-sizes to a single card at 50% width or a grid of N columns matching your configured locations.
  • Recently Completed table with a filter dropdown — checkboxes for Backup / Restore / Prune / Compact / S3 Sync / Other, saved in localStorage, filters via AJAX.
  • Server Health auto-refreshes every 15 seconds (CPU / memory / partitions via a lightweight endpoint).
  • File Catalog (ClickHouse) and MariaDB panels demoted to compact cards at the bottom. ClickHouse shows Top Repositories list + donut; MariaDB shows a 3×2 stat grid.
  • Old dashboard preserved at /dashboard-legacy.

📅 Schedules page (#145)

New Schedules item in the sidebar with:

  • Hour-of-day histogram (1h buckets) stacked per client, segments hoverable/clickable.
  • Day-view timeline with half-hour labels, median-duration block heights from backup history.
  • Day pills (Mon–Sun) that swap both views at once.
  • Click a block or histogram segment → context menu: Change Time / Edit Plan / Disable.
  • Inline Change Time modal with add/remove time rows and server-side validation.
  • Edit Plan deep-links into the client page with the edit panel pre-expanded.

🐳 Docker bind-mount automation (#143, #121)

  • Automatic UID/GID migration when PUID/PGID/MYSQL_PUID/CH_PUID change — no manual chown required.
  • State tracked in /var/bbs/config/.ownership so migrations only run when values actually change.
  • Preflight guards reject PUID=0, collisions with other services, and collisions with existing SSH-client UIDs.
  • Detailed timestamped migration logs with file counts and elapsed time.
  • Validated on Synology btrfs.
  • Thanks @addvanced for the original PR.

✨ Other improvements

  • #151 Non-breaking space between numbers and units throughout — 7 GB not 7GB, no wrapping on narrow screens.
  • #153 Success events no longer clutter the in-app notification bell. Email/push settings unchanged. Opt back in at Settings → Notifications.
  • #155 Bare-metal installer now installs cron (missing on ubuntu-minimal) and fixes prompt/output ordering issues.
  • #157 /api/v1/storage includes disk capacity and usage metrics per location.
  • #142 Daily report email subject prefixed with [BBS] for consistency.
  • Page title in the top navbar bar with per-page icons; version pill above sidebar Logout.
  • MySQL label → MariaDB; File Catalog shows (ClickHouse).
  • Schedules page edge-label clipping, timezone honoring, subdued navy gradient card headers.

📦 Pull

Docker:

docker compose pull
docker compose up -d

Bare metal: Settings → Updates → Update.


❤️ Sponsor this project if you find it useful.

v2.26.7 Bug fix

Fixed dashboard breakage, chart sizing issue, and prevented exposure of remote storage details to non-admin users.

Full changelog

Fixes (non-admin users)

  • Dashboard was silently broken for non-admin users. An undefined `$chStats` variable (only set inside the admin-only Row 3 block) was referenced further down in the pie-chart JS guard. On PHP 8 that triggered a Whoops error page injection right into the middle of an inline `` tag — the stray `` and `` tags broke JS parsing, which killed Chart.js, the Bootstrap dropdown handlers, and all polling loops. Non-admins saw an empty Jobs (24h) chart and a non-working profile dropdown.
  • Jobs (24h) chart rendered at 0px for non-admin users even after the above fix. Non-admins get the chart alone at `col-12` with no siblings to define the row height, so `h-100` + `maintainAspectRatio: false` collapsed the canvas to 0. Wrapped it in a `min-height: 200px` container.
  • Daily backup report exposed Remote Storage section (Hetzner / rsync.net quotas) to non-admin users. That's infrastructure detail — now gated behind the admin check that was already computed in the renderer.

❤️ Sponsor this project if you find it useful.

v2.26.6 Bug fix

Repo size scan now only occurs when the repository is modified, preventing idle disks from being awakened.

Full changelog

Fixes

  • Repo size scan no longer keeps idle disks awake (#135) — previously a scheduler loop ran du on every local repository every 5 minutes. That's gone. Size now only gets measured when BBS itself modifies the repo (backup, prune, compact, or archive delete). On an idle home server with no active jobs, disks are never touched. A one-time bootstrap still runs du for any repo whose size hasn't been measured yet (fresh installs and newly-added repos).

❤️ Sponsor this project if you find it useful.

v2.26.5 Bug fix
Notable features
  • Archive detail page redesigned to match repo detail layout
  • Queue detail now shows prune-specific stats from borg output
Full changelog

Fixes

  • Template selector broken in backup plan form (#139) — a previous fix for empty session files was too aggressive and logged out browser-initiated API calls. Sessions are now only skipped for agent Bearer-token endpoints.
  • Push notification log entries now include the client (#136) — Log page's Client filter now works correctly for per-client push notification events.
  • Dashboard counts no longer full-scan ClickHouseuniqExact(agent_id) over the entire file catalog was running every 60 seconds on every dashboard load, pegging ClickHouse. Replaced with cheap MySQL queries and system.parts metadata.
  • S3 manifest generator no longer pegs ClickHouse — OFFSET-based pagination was O(N²) on large catalogs. Switched to keyset pagination.

Improvements

  • Archive detail page redesigned to match the repo detail page — same breadcrumb/header style, reordered stat tiles (Total Size, Dedup Size with savings %, Files, Duration), dark-mode-friendly badges, and Restore Files / Restore Databases deep links that pre-select the archive and mode.
  • Queue detail shows prune-specific stats — Archives kept/pruned parsed from the borg output, instead of the generic backup stats card.

❤️ Sponsor this project if you find it useful.

v2.26.4 Bug fix

Fixed client storage size reporting to match actual disk usage and cleaned up excessive empty PHP session files.

Full changelog

If you find BBS useful, please consider sponsoring this project. It takes significant work to build and maintain — your support helps keep it going.


Bug Fixes

  • Client detail page showed wrong storage size — Reverse of #118. The recalculation from SUM(archives.deduplicated_size) undercounted actual disk usage (excluded borg metadata and uncompacted chunks). Local repos now use the scheduler's 5-minute du scan as the source of truth, matching what du -h reports on the filesystem. (#135)

  • Hundreds of thousands of empty PHP session files — Every API request from an agent created a new empty session file, and Docker installs had no cleanup mechanism. Sessions now only start for UI requests (agents use Bearer token auth), and the scheduler prunes old session files hourly. (#131)

  • Upgrade progress step count — Fixed off-by-one: was showing [1/10] then [2/9] through [9/9]. Now consistently /9. (#136, thanks @MegaV0lt)

v2.26.3 New feature
Notable features
  • Redesigned upgrade progress screen with collapsible stepped list and status icons
  • OIDC Redirect URL override setting for reverse‑proxy deployments
Full changelog

If you find BBS useful, please consider sponsoring this project. It takes significant work to build and maintain — your support helps keep it going.


New

  • Redesigned upgrade page — The upgrade progress screen now shows a clean stepped list with status icons instead of a raw terminal log. Each step is collapsible to show details if needed.
  • OIDC Redirect URL override — New optional setting under Settings → SSO. Use when BBS sits behind a reverse proxy and agents use an internal URL but SSO must use a different public hostname. Leave blank for auto-detection (existing behavior). (#125)

Bug Fixes

  • False "upgrade failed" messages — When PHP-FPM restarts mid-update the === Update complete === marker could get dropped from the log, making successful upgrades appear as failures. Now detects completion based on reaching the final step marker. (#128)
  • Can't delete archive from recovery points list — The row-level click handler was swallowing the trash button's click, navigating to the archive detail page instead of opening the delete confirmation. (#126)
  • Update step labels — Changed "Setting up" / "Fixing" to "Checking" since these steps are idempotent verification rather than repair.
v2.26.2 Bug fix

Fixed root filesystem backup hangs caused by the pre-count scanner traversing cross‑filesystem directories.

Full changelog

If you find BBS useful, please consider sponsoring this project. It takes significant work to build and maintain — your support helps keep it going.


Bug Fixes

  • Root filesystem backups hanging — The pre-count file scanner was walking into /proc, /sys, NFS mounts, and other cross-filesystem directories even though borg skips them with --one-file-system. A hung or slow mount would stall the count indefinitely and borg would never start. The pre-count now checks filesystem device IDs and stays on the same mount, matching borg's behavior. (#129)

  • pip3 borg updates on modern Python — Added --break-system-packages flag for PEP 668 compliance (required by Homebrew Python 3.11+ and Debian 12+). Also validates pip3 is functional before attempting the install — broken pip now reports a clear error instead of a raw traceback.

  • Agent auto-update not pushing pip fixes — Agent version was not bumped after adding the pip fixes, so the server thought agents were already current and never pushed the update.

Agent

  • bbs-agent v2.25.0
    • count_files() respects --one-file-system via st_dev checks
    • pip3 validated before use, --break-system-packages for PEP 668
    • Cleaner error messages for pip failures
v2.26.1 Bug fix
Notable features
  • Progress bar redesign: bytes moved to upper‑right, file name left‑justified, trailing slash fixed in PHP and JS
Full changelog

If you find BBS useful, please consider sponsoring this project. It takes significant work to build and maintain — your support helps keep it going.


Bug Fixes

  • Stall detection actually works now — The heartbeat stall query used INTERVAL ? MINUTE which MySQL doesn't support with prepared statement placeholders. The query silently returned no results, so stalled jobs were never killed. Also now detects jobs that hang before any progress is reported. (#130)

  • Progress bar redesign — Bytes processed moved to upper right, current file left-justified below the bar as a single truncated line. Trailing slash on file paths finally fixed in both PHP and JS rendering paths.

v2.26.0 New feature
Notable features
  • Archive Detail Page with summary cards, file changes table, largest files list, tabbed file browser, deleted‑files detection, and on‑demand ClickHouse analytics
  • Plan names displayed above raw archive names in the recovery points list
Full changelog

New Features

  • Archive Detail Page — Click any recovery point in the repo view to see a full breakdown of that backup:

    • Summary cards: file count, original/dedup size, dedup savings %
    • File Changes: stacked progress bar + table showing Added, Modified, Unchanged counts with proper labels for all borg status codes (directories, symlinks, hardlinks, etc. shown separately)
    • Largest Files: top 20 files by size with full path
    • File Browser: tabbed interface (All / Added / Modified / Deleted / Unchanged) with search and server-side pagination via ClickHouse — handles backups with millions of files
    • Deleted files detection: compared against the previous archive
    • Backup directories and database backup info
  • Plan names in recovery points list — The repo detail page now shows the backup plan name above the raw archive name, making it easier to identify which plan created each recovery point.

All file stats are computed on-demand from ClickHouse, which handles analytical queries over millions of rows near-instantly.

v2.25.2 Bug fix
Notable features
  • Server-hosted Borg 1.4.4 binary (GPG‑signed) replaces the 1.4.3 version
Full changelog

New

  • Server-hosted borg 1.4.4 binary — GPG-signed standalone binary for glibc 2.17+ (CentOS 6/7, Ubuntu 14.04+, Debian 8+). Available under Settings → Borg Management when "Use Server Binaries" is enabled. Replaces the 1.4.3 binary.

Bug Fixes

  • Progress bar trailing slash — The current file path in the backup progress bar no longer shows a trailing / on directory entries.
v2.25.1 Bug fix

Fixed Docker tar.gz download failures and OIDC redirect_uri protocol handling behind TLS‑terminating proxies.

Full changelog

Bug Fixes

  • Download .tar.gz failed in Docker — PHP's sys_get_temp_dir() returns /var/bbs/tmp/ in some Docker setups, but the ssh-helper only accepts /tmp/. Borg extract was silently rejected, producing "No files were extracted." Now uses /tmp explicitly. (#81)
  • OIDC redirect_uri behind reverse proxies — The OAuth redirect_uri used http:// when BBS runs behind a TLS-terminating reverse proxy (nginx, Traefik, Kubernetes ingress). Now respects the standard X-Forwarded-Proto header. (#125)
v2.25.0 Bug fix
⚠ Upgrade required
  • Agents affected by the Python 3.4 incident require a one‑time manual re‑run of the install script from the BBS dashboard to apply the self‑recovery wrapper.
Notable features
  • Configurable stall timeout under Settings → Agent (10 min–24 h, default 2 h)
  • Report frequency option: daily or weekly with day‑of‑week picker
  • Log page client filter dropdown and server log auto‑prune at 30 days
Full changelog

Bug Fixes

  • Stall detection — Fixed a structural bug where stall detection never fired during running jobs because the agent's main loop was blocked. The heartbeat channel now carries stall signals and can kill a hung borg process. Configurable timeout under Settings → Agent, default 2 hours. (#117)
  • Dashboard storage mismatch — Repo size_bytes is now refreshed after prune and archive delete, so the dashboard matches the client detail page without needing to visit it first (#118)
  • Imported repos show 0 filescatalog_sync now reads nfiles from borg info during import. Existing repos with 0 file counts are auto-backfilled from ClickHouse on next page view (#115)
  • Repo name sanitization on import — Import now sanitizes names the same way as create, preventing leading slashes and special characters (#114)
  • Same-path rename — Renaming /homehome no longer fails with "target already exists" (#114)
  • False "update failed" — PHP-FPM restart failure no longer aborts the update script (#93)
  • Agent offline severity — Downgraded from critical/error to warning (#111)

New Features

  • Report frequency — Users can choose daily or weekly report emails with a day-of-week picker (#120)
  • Log page client filter — Dropdown to filter log entries by client alongside the existing level filter
  • Log retention — Server log auto-pruned at 30 days, backup jobs at 90 days
  • Stall timeout setting — Configurable under Settings → Agent (10 min – 24 hours, default 2 hours)

Agent

  • bbs-agent v2.24.3
    • Heartbeat thread now processes stall detection and cancel signals from the server
    • Stalled borg processes are killed automatically when the server detects no progress
    • Cancel via UI now works even when borg is completely frozen (relayed through heartbeat)

Agent Update Safety

v2.24.10 included the self-recovery wrapper (bbs-agent-start.sh) and syntax validation. Agents bricked by the Python 3.4 incident need a one-time manual fix (re-run the install script from the BBS dashboard). After that, the wrapper prevents this class of failure permanently.

v2.24.10 Breaking risk
⚠ Upgrade required
  • Agents previously bricked by v2.24.4–v2.24.9 require a one‑time manual re‑run of the install script; after that the new startup wrapper provides auto‑recovery.
  • Python 3.4 compatibility fix added for bbs-agent.
Notable features
  • Log page now includes client filter dropdown alongside level filter
  • Backup jobs and server logs are auto‑pruned (jobs 90 days, logs 30 days)
  • Agent offline notifications downgraded from critical to warning
Full changelog

Bug Fixes

  • Job cancellation hang — Killing borg on cancel now kills the entire process tree (including the SSH transport child) so the agent doesn't hang waiting on a dead pipe (#91)
  • Catalog import on FUSE/shfs — Catalog files are now explicitly chgrp'd to www-data with a chmod 644 fallback, plus diagnostic logging to trace permission issues on Unraid and similar FUSE mounts (#84)
  • Daily catalog rebuild loop — Archives with 0 indexable files no longer trigger a rebuild every 24 hours (#112)
  • Import repo first-click failure — Removed 2>&1 from verify-repo helper so borg's cache initialization messages don't corrupt the JSON output (#99)
  • Shell hook script arguments — Pre/post script paths now support arguments (e.g. /path/script.sh before) via shell-style parsing (#107)
  • Queue detail overflow — Long file paths and command lines now wrap instead of overflowing the layout (#108)
  • Prune imported archives — Prune can now clean up imported archives on single-plan repos (#109)
  • False "update failed" — PHP-FPM restart failure during update no longer aborts the script before printing the completion marker (#93)
  • Repo name sanitization on import — Import now sanitizes repo names the same way as create, preventing leading slashes and special characters (#114)
  • Same-path rename — Renaming a repo where old/new paths resolve to the same directory (e.g. /homehome) now just updates the DB name (#114)
  • Download .tar.gz diagnostics — "No files were extracted" now includes borg output, exit code, and a root-level view of the tmp dir in the server log (#81)
  • Last Backup stat tile — Font size now matches the other stat tiles (#103)
  • OIDC SSO — Fixed addScope TypeError when configuring multiple scopes (#95)

Agent

  • bbs-agent v2.24.2
    • Self-recovery from bad updates: syntax validation (ast.parse) before replacing the running script, automatic .bak backup, and a new startup wrapper (bbs-agent-start.sh) that downloads a fresh copy from the server if the .py is broken
    • Shell hook scripts support command-line arguments
    • Cancel kills the entire borg process group (not just borg) to prevent orphaned SSH children
    • Windows: bundled Git-for-Windows SSH to avoid the built-in ssh.exe stdin forwarding hang
    • Python 3.4 compatibility fix for **kwargs unpacking

Server / UI

  • Log page — Added client filter dropdown alongside the existing level filter
  • Log retention — server_log auto-pruned at 30 days, backup_jobs at 90 days
  • Borg Management page — Windows/macOS agents no longer show a false "incompatible" warning; tooltip improved for Linux agents without a matching server binary
  • Agent offline notifications — Downgraded from critical to warning severity (#111)

Agent Update Safety

A Python 3.4 syntax incompatibility in v2.24.4–v2.24.9 caused agents on CentOS 6/7 to crash on update with no auto-recovery path. This release adds three layers of defense:

  1. Syntax validationast.parse checks downloaded code before replacing the running script
  2. Backup.bak copy saved before every replacement
  3. Startup wrapperbbs-agent-start.sh runs before the Python agent, downloads a fresh copy from the server if the .py is broken, and restores from backup if the server is unreachable

Agents that were bricked by the earlier update need a one-time manual fix: re-run the install script from the BBS dashboard on the affected machine. After that, the wrapper prevents this class of failure permanently.

v2.24.3 Bug fix

Fixed first‑click repository import failure caused by cache initialization corrupting JSON output.

Full changelog

Bug Fixes

  • Repository import: Fixed first-click import failure caused by borg cache initialization messages corrupting the JSON output (#99)
  • Script plugin: Pre/post script paths now support arguments (e.g. /path/script.sh before) via shell-style parsing (#107)
  • Queue detail page: Long file paths and command lines now wrap inside table cells instead of overflowing the layout (#108)
  • Prune: Imported archives on single-plan repositories are now eligible for prune cleanup (#109)
  • Client detail page: Recent Activity error messages now show an ellipsis when truncated and tooltip shows up to 500 chars (#107)
  • Last Backup stat tile: Font size now matches the other stat tiles (#103)
  • OIDC SSO: Fixed addScope TypeError when configuring multiple scopes (#95)

Agent

  • bbs-agent v2.24.0: Shell hook scripts now support command-line arguments
v2.24.0 New feature
Notable features
  • Generic OpenID Connect (OIDC) single sign‑on with provider list, config UI, new user handling options and optional IdP logout
  • All JavaScript timestamps now use the BBS profile timezone for consistent display
Full changelog

What's New

OIDC Single Sign-On (#70)

Generic OpenID Connect SSO support — works with Keycloak, Authentik, Azure AD, Google Workspace, Okta, Auth0, Authelia, and any OIDC-compliant provider.

  • Settings > Authentication tab for configuration
  • SSO button on login page alongside existing password login
  • New user handling: deny access, pending admin approval, or copy permissions from a template user
  • SSO users skip 2FA (rely on identity provider for auth strength)
  • Optional OIDC logout (sign out of IdP when logging out of BBS)
  • Documentation

Job Cancellation (#91)

Cancel now properly stops running backups. The server signals the agent via the progress response, the agent kills the borg subprocess within ~5 seconds, and a break-lock job is automatically queued to clean up any stale locks.

JS Timezone Consistency (#87)

All JavaScript time formatting now uses the BBS profile timezone instead of the browser's local timezone. Prevents times from jumping after AJAX refresh when the browser timezone differs from the profile setting.

Fixes

ClickHouse Stability (#88)

Reverted thread pool limits and stderr redirect that were causing ClickHouse to fail to start on some installations. Logging cap (warning level, 10MB rotation) remains.

ClickHouse Docker Startup (#83)

Increased startup health check wait from 15 to 30 seconds for slower VMs.

Installer Fix (#86)

ClickHouse table creation deferred until after BBS code is downloaded — was failing because it referenced schema files before git clone.

Borg Update Retry Loop (#79)

Auto-update no longer re-queues failed borg updates every 30 seconds. Waits 24 hours before retrying.

v2.23.0 New feature
Notable features
  • Settings > Branding tab allows uploading custom navbar icon, login page logo (max 475x100px), and forcing dark or light theme on the login screen
  • System-wide default theme (dark/light) configurable in Settings > General applies to login page and new users
Full changelog

What's New

Branding (#85)

New Settings > Branding tab for customizing the BBS interface:

  • Navbar Icon — upload a square PNG to replace the default BBS logo in the top-left corner (resized to 120x120px max)
  • Login Page Logo — upload a wide PNG for the login screen (resized to 475x100px max)
  • Login Page Theme — force dark or light mode on the login page regardless of user preferences (useful when your logo only works on one background)

Images are stored in the database and persist across Docker rebuilds. Client-side resizing with live preview before saving.

System-Wide Default Theme (#85)

Admins can set the default theme (dark/light) in Settings > General. Applies to the login page and new users. Users can still override in their profile.

Fixes

Borg Update Retry Loop (#79)

When a borg update failed (e.g. disk full), the auto-update logic re-queued a new attempt every 30 seconds — creating hundreds of failed jobs and error notification emails. Now waits 24 hours before retrying a failed update.

ClickHouse Thread Count (#83)

ClickHouse thread pools capped to reasonable limits (max 32 threads, background pools 1-2 each). Default ClickHouse spawned ~800 threads regardless of workload. Should drop to ~50-80 threads.

v2.22.1 Bug fix

Force MySQL session timezone to UTC, fixing timestamp display inconsistencies.

Full changelog

Fixes

Timezone: Force MySQL Sessions to UTC

Completes the timezone fix from v2.21.2. PHP was already forced to UTC, but MySQL's NOW() and CURRENT_TIMESTAMP were still using the server's local timezone on non-UTC systems. Now sets time_zone = '+00:00' on every MySQL connection so all timestamps are consistent. This should fully resolve time display issues on servers in non-UTC timezones (e.g. Europe/Prague).

New jobs after updating will show correct times. Old timestamps stored before this fix may still be offset.

v2.22.0 Bug fix
Notable features
  • Recovery Points table on repo detail page showing archive metadata and delete via job queue
  • Restore dropdowns now display the backup plan that created each archive
Full changelog

What's New

Archive Management

The repo detail page now shows a Recovery Points table listing all archives with name, date, file count, original and deduplicated sizes. Individual archives can be deleted via a confirmation modal — deletions are queued as jobs and wait their turn in the normal job queue.

Restore Point Context

Restore point dropdowns on the Restore tab now show which backup plan created each archive (e.g. "Thursday, Apr 2 at 8:16 PM — daily-full"), making it easy to distinguish archives when multiple plans back up to the same repo.

Fixes

Shell Hook Post-Scripts Always Run (#79)

Post-scripts now run regardless of whether the backup succeeded or failed. Previously, if a backup failed, the post-script was skipped — leaving services that were stopped by the pre-script in a down state. Other plugin cleanup (dump file deletion) still only runs on success.

ClickHouse Logging Capped (#79)

ClickHouse's file-based logging is now limited to warning level, 10MB max file size with 2 rotations, and stderr redirected to /dev/null. Prevents ClickHouse from filling the container disk with unbounded stderr logs (7+ GB observed in the wild) when its own log rotation fails.

v2.21.2 Bug fix

Fixed PHP timezone mismatch with MySQL timestamps.

Full changelog

Fixes

Timezone: Force PHP to UTC

PHP's date() function was using the server's local timezone (e.g. Europe/Prague = UTC+2) while MySQL stores timestamps in UTC. This caused all timestamps written by PHP — started_at, completed_at, heartbeats, log entries — to be offset by the server's timezone. Now forces date_default_timezone_set('UTC') at both the web entry point and scheduler, so all PHP timestamps match MySQL.

Scheduler: Fix PHP warnings on SSH repo paths

is_dir() was being called on raw SSH-format repo paths (ssh://user@host/./repo), triggering PHP "unable to find ssh wrapper" warnings. Now uses the proper local path resolver.

ReportService: Fix undefined variable warnings

$dayStart and $dayEnd were used in the daily report error log query without being defined. Now properly initialized from the report date.

v2.21.1 Bug fix
Notable features
  • Added S3 Storage Class, Server‑Side Encryption (SSE‑S3/KMS), and Bandwidth Limit options to global settings
Full changelog

Fixes

Non-Default Storage Location Fix (#76)

Fixed repos failing to create on non-default storage locations (NFS mounts, external drives, etc.) with "repo path must be under /var/bbs/ or a registered storage location". The local path resolver was returning a double-slash prefix (e.g. //var/TB10/... instead of /var/TB10/...) which failed the security check.

S3 Offsite Sync Options (#77)

Added missing options to the global S3 settings page:

  • Storage Class — Standard, Standard-IA, Intelligent-Tiering, Glacier, Deep Archive
  • Server-Side Encryption — AES-256 (SSE-S3) or AWS KMS (SSE-KMS) with optional KMS Key ID
  • Bandwidth Limit — Throttle upload speed (e.g. 50M for 50 MB/s)

Per-plugin custom S3 configs can also override storage class and encryption.

Storage Page Layout Fix

Fixed broken card layout on the Storage Locations page caused by a leftover HTML element.

v2.21.0 Breaking risk
Breaking changes
  • Storage locations become immutable after creation; changes require deletion and recreation.
  • Setup wizard storage path locked to `/var/bbs/home` and no longer editable.
  • Removed `storage_path` from the editable settings POST whitelist.
Notable features
  • InterWorx Control Panel backup plugin with Structure Only, Full Backup, and Partial Backup modes.
  • Plugin form enhancements: `select` dropdown fields and `show_when` conditional visibility.
Full changelog

What's New

InterWorx Control Panel Backup Plugin

New pre-backup plugin for InterWorx hosting control panel servers. Runs backup.pex before borg archives to capture domain configurations, websites, databases, and email.

Three backup modes:

  • Structure Only — Manifest XML only, no data files (fastest, ideal when borg already backs up the data directories)
  • Full Backup — All website files, databases, and email
  • Partial Backup — Select individual components (web, db, mail) with options to exclude logs and stats

Configurable domains, output directory, compression level, and automatic cleanup after borg archiving.

Storage Location Security (#71)

  • Storage locations are now immutable once created — delete and recreate to change. Prevents accidental path changes that would break existing repos.
  • Storage path in setup wizard is locked to /var/bbs/home and no longer editable. External storage should be added as Storage Locations after setup.
  • Removed storage_path from the editable settings POST whitelist.

Plugin Form Improvements

  • Generic plugin forms now support select dropdown fields and show_when conditional visibility. Any plugin can use dropdowns with fields that appear/hide based on the selected value.
v2.20.1 Bug fix

Fixed timezone‑related duration calculations and timestamp handling across the server.

Full changelog

Fixes

Timezone-Safe Duration Calculation

On servers where PHP's timezone differs from MySQL's (e.g. PHP set to Europe/Prague while MySQL uses UTC), job durations were calculated incorrectly — off by the timezone offset (e.g. showing 2h for an instant operation). Now uses MySQL's TIMESTAMPDIFF() for all duration calculations to keep everything in the same timezone. Also fixed completed_at timestamps being written in PHP local time instead of MySQL time.

24-Hour Time Format Fixes

  • Added missing conversion patterns for g:i:s A (with seconds) and g:ia (lowercase am/pm) used on queue detail and chart pages
  • Job list on client Status tab now shows completion time instead of queue time for finished jobs

Dashboard Elapsed Time

Fixed running job elapsed time display on the dashboard to correctly treat started_at as UTC.

v2.20.0 New feature
Notable features
  • MongoDB backup/restore plugin with UI integration, per‑database dumps, gzip compression, and one‑click restore
  • Per‑user configurable 24‑hour time display across timestamps, charts, queues, and email reports
Full changelog

What's New

MongoDB Plugin (#68)

Community-contributed MongoDB backup/restore plugin by @sainf. Supports per-database dumps with mongodump, gzip compression, automatic cleanup, and one-click restore via mongorestore.

  • Full backup/restore workflow integrated into the UI
  • Plugin configuration with auth database, database selection, and exclusions
  • Documentation

24-Hour Time Format (#69)

Per-user preference to switch between 12-hour (1:30 PM) and 24-hour (13:30) time display. Set in Profile > Time Format. Applies to all timestamps, chart labels, queue pages, and email reports.

Fixes

Database Restore Improvements (All Plugins)

  • Targeted extraction — Restores now extract only the requested database files from archives instead of the entire dump directory. Dramatically faster on large archives. (MySQL was fixed in v2.19.1, PostgreSQL and MongoDB fixed in this release)
  • Safety backup before replace — Before overwriting a database during restore, the current version is dumped as a safety net (MySQL, PostgreSQL, and MongoDB)
  • Accurate job timing — All database restore types now report running status immediately so Started At and Duration are accurate
  • PostgreSQL restore dispatch fixrestore_pg tasks were missing from getTasksForAgent(), potentially causing PG restores to get stuck in "sent" status

Other Fixes

  • Job list on client Status tab now shows completion time instead of queue time for finished jobs
v2.19.1 Bug fix

Fixed MySQL restore performance by extracting only requested databases and added a safety backup before replacement.

Full changelog

Fixes

MySQL Restore Performance (#67)

  • Extract only requested databasesborg extract now targets specific dump files instead of the entire dump directory which caused unnecessary overhead and disk I/O.
  • Safety backup before replace — Before overwriting a database during a replace restore, the agent dumps the current DB to dump_dir/dbname_pre_restore.sql.gz as a safety net.
  • Accurate job timing — Agent now reports running status immediately when the restore begins (during borg extract), so Started At and Duration reflect actual elapsed time.

Path Resolution Fix

  • getLocalRepoPath() now parses the directory name from the SSH path URL instead of relying on $repo['name'], fixing prune, compact, catalog, restore, and S3 sync failures after repository renames.
v2.19.0 New feature
Notable features
  • Full Admin REST API supporting Clients, Repositories, Backup Plans, Jobs, Queue, Plugins, and Storage resources
Full changelog

What's New

Admin REST API (#64)

A full provisioning API for automated infrastructure-as-code workflows with Ansible, Terraform, or CI pipelines.

Authentication: Bearer token via Authorization: Bearer bbs_tok_...

  • Token management in Settings > API tab or via bin/bbs-token CLI

Endpoints:

| Resource | Endpoints |
|----------|-----------|
| Clients | GET/POST list/create, GET/PUT/DELETE detail/edit/delete |
| Repositories | GET/POST list/create, PUT rename, DELETE delete (local + remote SSH) |
| Backup Plans | GET/POST list/create, PUT edit, DELETE, POST pause/resume/trigger |
| Jobs | GET list (paginated, filterable), GET detail |
| Queue | GET global active queue |
| Plugins | GET list, GET schema, GET/POST configs per client |
| Storage | GET local + remote SSH locations |

Full documentation: API Wiki

Fixes

  • Fix getLocalRepoPath() to parse directory name from the SSH path URL instead of relying on $repo['name'] — prevents path mismatches after renames for prune, compact, catalog, restore, and S3 sync operations
v2.18.7 New feature
Notable features
  • Rename local repositories via the detail page (blocked during active jobs), with on-disk directory renamed and database path updated.
  • New repository names are sanitized to remove special characters, replacing spaces with hyphens for filesystem safety.
  • Backup plan templates now support Borg options such as compression, exclude caches, one file system, noatime, numeric IDs, skip xattrs, and skip ACLs.
Full changelog

What's New

Repository Rename (#61)

  • Rename local repositories from the repo detail page (pencil icon next to name). Renaming remote repos is not possible in some services, so naming is limited to local (and NFS) type repos.
  • Blocked while jobs are active on the repo
  • Directory renamed on disk, path updated in database — borg handles relocation seamlessly

Repository Name Sanitization

  • New repo names are sanitized for filesystem safety (special characters stripped, spaces become hyphens)
  • Prevents problematic characters like brackets, spaces, and symbols in directory names

Backup Plan Template Options (#63)

  • Templates now support borg options (compression, exclude caches, one file system, noatime, numeric IDs, skip xattrs, skip ACLs)
  • Options are applied automatically when selecting a template during plan creation or editing

Fixes

  • Fix dropdown menus getting clipped on schedule and repo cards
  • Fix template options not applying to plan form checkboxes when template has no options saved
  • Default prune hourly retention set to 0 for new plans (only useful when backing up more than once per hour)
  • SMTP username/password now optional for notification services (#57)
  • Fix installer header box alignment (#62)
v2.18.6 New feature
⚠ Upgrade required
  • Bare metal/VM installs must restart ClickHouse (sudo systemctl restart clickhouse-server) after update to apply reduced idle CPU metrics polling interval.
Notable features
  • Remote SSH storage monitoring shows disk usage via df -k over SSH
  • Low storage notifications now monitor remote SSH repository space
Full changelog

New Features

  • Remote SSH storage monitoring (#49) — The Storage Locations page now shows disk usage for remote SSH repositories (rsync.net, Hetzner Storage Box, etc.) with progress bars. Polled every 15 minutes via df -k over SSH. Providers that don't support disk queries (BorgBase) show "Quota unavailable."
  • Low storage notifications for remote hosts — Storage alert threshold now also monitors remote SSH storage. You'll receive notifications when remote hosts are running low on space.
  • Per-agent server host and SSH port overrides (#54) — Agents connecting from outside the local network can use a different hostname and SSH port. Set in Edit Client modal.

Bug Fixes

  • Fix bbs-update crashing on some PHP installationsphp_ini_scanned_dir() doesn't exist on all PHP builds, which caused the update to abort mid-way (steps 5-9 never ran). Users who updated to v2.18.3-v2.18.5 should re-run the update to complete the remaining steps.
  • Fix race condition: stall detection abandoning jobs being delivered (#55)
  • Fix daily report showing 0 completed/failed backups — Now counts backups since the last report instead of a timezone-dependent "today" window.
  • Fix plugin test timeout (#50) — Test was using a hardcoded 30s timeout instead of the configured value (default 300s).
  • Fix repo card dropdown menu clipped by parent container
  • Fix PHP warnings on malformed request URIs (#53)
  • Fix schedule showing Overdue while backup is running

Improvements

  • Increase PHP max_execution_time to 300s — Default 30s was too short for large catalog imports and API operations under load. Set for both Docker and bare metal installs.
  • Increase agent API request timeout to 60s — Was 30s, could silently fail under server load.
  • Reduce ClickHouse idle CPU usage — Internal async metrics polling reduced from 1s to 60s. Note: Bare metal/VM installs need to restart ClickHouse (sudo systemctl restart clickhouse-server) after updating.
v2.18.3 Bug fix
Notable features
  • Per-agent server host and SSH port overrides for external connections
Full changelog

New Features

  • Per-agent server host and SSH port overrides (#54) — Agents connecting from outside the local network can now use a different hostname and SSH port. Set in Edit Client → Server Host Override / SSH Port Override. Empty = use global settings. No agent code changes needed. See #54 for detailed setup instructions with SSL considerations.

Bug Fixes

  • Fix race condition: stall detection abandoning jobs being delivered (#55) — The tasks endpoint delivered a job and asked the agent "are you running this job?" in the same response. Jobs being delivered are now excluded from stall detection.
  • Fix container disk full from temp files (#52) — Catalog TSV files, MySQL temp tables, and ClickHouse processing temps now write to /var/bbs/tmp on the persistent volume.
  • Fix PHP warnings on malformed request URIs (#53)
  • Fix schedule showing Overdue while backup is running
v2.18.2 Bug fix

Fixed race condition causing abandoned jobs to not record archives during delivery.

Full changelog

Bug Fixes

  • Fix race condition: stall detection abandoning jobs being delivered (#55) — The tasks endpoint delivered a job and asked the agent "are you running this job?" in the same response. The agent reported "not running" (it just received it), the server marked it abandoned, and the backup completed successfully but the archive was never recorded. Jobs being delivered are now excluded from stall detection.
  • Fix container disk full from temp files (#52) — Catalog TSV files, MySQL temp tables, and ClickHouse processing temps now write to /var/bbs/tmp on the persistent volume instead of the container's overlay filesystem.
  • Fix PHP warnings on malformed request URIs (#53) — parse_url returned null for network-path URIs, causing deprecation warnings and a broken 404 response.
  • Fix schedule showing Overdue while backup is running — The scheduler now advances next_run even when skipping a duplicate, so the dashboard no longer shows "Overdue" for plans with an active backup.
v2.18.0 Bug fix
Notable features
  • Management tasks (update_borg/update_agent) bypass queue slots
  • Stale job detection now only fails sent/running jobs, leaving queued jobs intact
  • Shell script plugin test reports exit code and output
Full changelog

Bug Fixes

  • Fix long-running backups killed at 24 hours despite active progress (#51) — PHP and MySQL timezone mismatch on Docker caused last_progress_at to always appear stale. All database timestamps now use MySQL's clock directly.
  • Fix queue blocking: offline agents no longer block all backups — Two stuck jobs for offline agents could fill the 4-slot queue and prevent all other agents from running backups for hours. Slot counting now excludes jobs for offline agents and management tasks.
  • Fix Rebuild Full not recovering wiped archives (#47) — Now properly re-reads all recovery points from the borg repository with sizes, then rebuilds the file catalog.
  • Fix borg warnings wiping all archive records (#47) — Separated stderr from JSON stdout in bbs-ssh-helper, added JSON validation guards.
  • Fix client creation on NAS/NFS storage paths (#48) — SSH home directories are created on local filesystem when storage_path points to a NAS mount.
  • Fix allowed-storage-paths lost on Docker container restart (#47) — The file is now regenerated from the database on every container start.
  • Fix auto-catalog-rebuild infinite loop — Added 24-hour cooldown.

Improvements

  • update_borg/update_agent bypass queue slots — Management tasks run freely without waiting for backup slots. They only wait if the agent has an active backup.
  • Stale job detection no longer fails queued jobs — Only sent/running jobs are failed when an agent goes offline. Queued jobs wait for the agent to come back.
  • Shell script plugin test now runs scripts (#46) — Reports exit code and output instead of just checking file existence.
  • FreeBSD borg updater support — Added pkg install borgbackup and pip3→pip fallback.
  • Better error diagnostics — Catalog sync, SSH provisioning, and borg list failures now report actual errors.
v2.17.14 Bug fix

Fixed catalog sync failures on NAS/non-default storage locations and improved error diagnostics.

Full changelog

Bug Fixes

  • Fix catalog sync failing on NAS/non-default storage locations (#47) — The /etc/bbs/allowed-storage-paths file (which authorizes bbs-ssh-helper to access repo paths outside /var/bbs/) lives inside the container filesystem and was lost on every container recreation. Now regenerated from the database on container start. This was the cause of "repo path must be under /var/bbs/ or a registered storage location" errors after upgrading.
  • Better error diagnostics for catalog sync failures — Error messages now include the actual borg output instead of a generic "not valid JSON" message.
v2.17.13 Bug fix

Fixed "Rebuild Full" not recovering wiped archives, enabling proper restoration of lost backup data.

Full changelog

Bug Fixes

  • Fix "Rebuild Full" not recovering wiped archives (#47) — Rebuild Full now properly re-reads all recovery points from the borg repository, repopulates archive records with sizes, then rebuilds the file catalog. Previously it only rebuilt the file catalog from an already-empty archives table.
  • Fix client creation on NAS/NFS storage paths (#48) — SSH home directories are now created on the local filesystem when storage_path points to a NAS mount.
  • Better error diagnostics — Catalog sync failures now report the actual borg error instead of a generic "not valid JSON" message.
  • Fix auto-catalog-rebuild loop — Added 24-hour cooldown to prevent infinite re-queuing.
v2.17.12 Bug fix
Notable features
  • Shell script plugin test now executes scripts and reports exit code + output
  • Server logs list attached plugins per backup job for debugging
Full changelog

We apologize for the issues in recent releases. This release addresses several bugs that were introduced and fixes recovery tooling that should have been working correctly from the start.

Bug Fixes

  • Fix "Rebuild Full" not recovering wiped archives (#47) — The Rebuild Full button was only rebuilding the ClickHouse file catalog from the MySQL archives table. If the archives table was empty (from the bug in v2.17.9), it had nothing to work with. Rebuild Full now properly re-reads all recovery points from the actual borg repository, repopulates archive records with correct sizes, then rebuilds the file catalog.
  • Fix borg warnings wiping all archive records from database (#47) — The bbs-ssh-helper borg-list command merged stderr into stdout, corrupting JSON output when borg emitted warnings. Both the post-prune archive sync and catalog sync deleted all archive records when JSON parsing failed. Fixed by separating stderr and adding JSON validation guards.
  • Fix client creation failing on NAS/NFS storage paths (#48) — SSH home directories require chown which fails on NAS mounts. Client creation now detects when storage_path points to a NAS storage location and creates SSH home dirs on the local filesystem instead.
  • Fix auto-catalog-rebuild infinite loop — Added 24-hour cooldown to prevent the scheduler from re-queuing catalog rebuilds every minute when some archives can't be indexed.

Improvements

  • Shell script plugin test now runs scripts (#46) — Previously only checked file existence/permissions. Now actually executes scripts and reports exit code + output.
  • Server log shows attached plugins per backup job for easier debugging.
  • Better error messages — SSH provisioning failures now log the actual error from bbs-ssh-helper.
v2.17.9 Bug fix
Notable features
  • Agent inhibits system sleep during backup tasks
Full changelog

Bug Fixes

  • Fix backup paths with spaces — Directory paths containing spaces (e.g. /var/lib/.../Application Support/...) were being split on whitespace instead of newlines, breaking the borg command. Fixed in both the server-side command builder and the agent's file counter. (#45)

New Features (since v2.17.7)

  • Prevent OS sleep during backups — The agent now inhibits system sleep while a task is running, preventing Windows laptops (and macOS/Linux) from going to sleep mid-backup. (#43)

Timezone Fixes (since v2.17.6)

  • Fix schedule timezone not persisted on plan update, causing duplicate daily backups
  • Fix timezone not propagated to schedules on profile change
  • Force MariaDB to UTC to fix restore point timestamp display on non-UTC Docker hosts (#42)

Enjoying Borg Backup Server? Consider sponsoring this project on GitHub.

v2.17.7 Bug fix

Fixed backup schedule timezone persistence and MariaDB timestamp handling bugs.

Full changelog

Bug Fixes

  • Fix schedule timezone not persisted on plan update — editing a backup plan now saves the user's timezone to the schedule, preventing SchedulerService from recalculating next_run with a stale timezone (which caused backups to run twice per day)
  • Fix timezone not propagated to schedules on profile change — changing timezone in /profile now updates all matching schedules and recalculates next_run immediately
  • Fix MariaDB using host timezone for CURRENT_TIMESTAMP — Docker entrypoint now sets MariaDB default-time-zone to UTC via config file, fixing restore point timestamps displaying hours off on non-UTC hosts

Thanks to @erycsonero for reporting and diagnosing these issues (#42).

v2.17.6 New feature
⚠ Upgrade required
  • SSH port setting is no longer overwritten on every container restart (entrypoint fix)
Notable features
  • Docker first-run setup modal auto-detects containers and prompts admin for hostname, web port, SSH port
  • Cleaner docker-compose.yml with variable substitution in .env and added .env.example
  • Podman support added alongside Docker container detection
Full changelog

What's New

  • Docker first-run setup modal — Auto-detects Docker/Podman containers and prompts admin to configure hostname, web port, and SSH port on first login
  • Cleaner docker-compose.yml — Uses variable substitution so ports are defined once in .env; added .env.example
  • Entrypoint fix — SSH port setting no longer overwritten on every container restart
  • Full borg compression support — Backup plans now support all borg compression specs (lzma, auto, obfuscate, etc.) via free-text input with suggestions (thanks @faultoverload)
  • Podman support — Container detection works for both Docker and Podman
v2.17.5 Mixed
Notable features
  • FreeBSD agent support via `pkg` installer and `rc.d` service (tested on FreeBSD 15.0‑RELEASE)
  • SSH port configurable in Settings > Server Host for Docker deployments
Full changelog

Bug Fixes

  • Add SSH port setting to settings page — Docker setups that map SSH to a non-standard external port (e.g. 2222) had no way to configure this, causing all agent SSH connections to fail with "Permission denied." The SSH port field was hardcoded to 22 with no UI to change it. Now editable in Settings > Server Host section.

New Features

  • FreeBSD agent support — The agent installer now supports FreeBSD via pkg, with an rc.d service script. Tested on FreeBSD 15.0-RELEASE.
v2.17.4 New feature
Notable features
  • FreeBSD agent installer via pkg, rc.d service and fetch support (tested on FreeBSD 15.0-RELEASE)
Full changelog

New Features

  • FreeBSD support — The agent installer now supports FreeBSD. Installs borg and python3 via pkg, creates an rc.d service using daemon(8), and supports FreeBSD's built-in fetch for downloads. Tested on FreeBSD 15.0-RELEASE.
    • Prerequisite: pkg install bash, then: fetch -o - https://server/get-agent | /usr/local/bin/bash -s -- --server URL --key KEY

Bug Fixes

  • Fix Windows SSH key permissions (PowerShell approach) — Previous icacls-based fixes failed on some Windows configurations. Now uses PowerShell to build a completely fresh ACL from scratch with only SYSTEM and Administrators read access. Falls back to icacls with well-known SIDs if PowerShell is unavailable.
  • Fix SSH key parsing on FreeBSD — The installer's JSON parser now correctly finds versioned Python (e.g. python3.11) when python3 symlink doesn't exist.
v2.17.3 Bug fix

Fixed Windows SSH key permission issues causing OpenSSH to reject keys due to leftover ACEs.

Full changelog

Bug Fixes

  • Fix Windows SSH key permissions (take 3) — The previous icacls-based approach failed to fully clean up inherited ACEs on some Windows configurations, causing OpenSSH to still reject the key with "Permission denied." Now uses PowerShell to build a completely fresh ACL from scratch with only SYSTEM and Administrators read access — no leftover ACEs are possible. Falls back to icacls with SIDs if PowerShell is unavailable.
  • Added diagnostic logging — After setting key permissions, the agent now verifies the key is readable and logs the actual ACL if it's not, making future debugging much easier.
v2.17.2 Bug fix

Fixed Windows SSH key permission handling for non‑English systems and auto‑remediated misconfigured keys on agent start.

Full changelog

Bug Fixes

  • Fix Windows SSH key permissions on non-English systems — The agent's SSH key lockdown used English group names (Users, BUILTIN\Users, etc.) in icacls commands, which fail on non-English Windows installations (e.g. Spanish). Now uses well-known SIDs for locale independence. Also removes leftover CREATOR OWNER ACE that caused OpenSSH to reject the key as "too open."
  • Re-apply key permissions on every agent startup — Keys created by older agent versions with incorrect permissions are now automatically fixed on startup, without requiring manual intervention.
v2.17.1 Bug fix
Notable features
  • Progressive Web App (PWA) support prompting mobile users to add BBS to home screen
  • Mobile‑only Settings tab linking directly to Storage Locations
  • Option in Settings to opt out of anonymous usage statistics
Full changelog

Bug Fixes

  • Fix Windows SSH host key mismatch after server_host update — After the v2.17.0 fix that stripped the web port from server_host, stale entries in the Windows known_hosts file caused SSH connections to fail with "REMOTE HOST IDENTIFICATION HAS CHANGED." All SSH connections (borg, catalog pipe, SSH test) now use UserKnownHostsFile=/dev/null (NUL on Windows) to prevent stale host key issues after Docker rebuilds or server configuration changes.
  • Fix dashboard slow loading — ClickHouse and server health stats are now cached with 60s TTL and refreshed in the background, keeping the dashboard snappy.
  • Fix queue detail cancel button — Cancel button no longer loses its form reference during AJAX status polling.
  • Optimize queue/dashboard queries — Truncate large error_log TEXT columns in list views to prevent MySQL temp table bloat.

Improvements

  • Add to Home Screen (PWA) — Mobile users are prompted to add BBS to their home screen for app-like access.
  • Storage Locations link on mobile — Settings page now includes a mobile-only tab linking to Storage Locations.
  • Added option to opt out of anonymous usage statistics in Settings.
v2.17.0 Bug fix
Notable features
  • Added PWA manifest and mobile 'Add to Home Screen' banner
  • Added Storage Locations link on settings page for mobile users
  • Stripped web port from SSH commands in docker setups
Full changelog
  • Fix cancel button on queue detail page losing form reference during AJAX poll
  • Improve dashboard performance: split fast/slow AJAX endpoints, ClickHouse stats cached 60s
  • Add PWA manifest and mobile "Add to Home Screen" banner
  • Add Storage Locations link on settings page for mobile users
  • Stripped web port from host in SSH commands (was appending it causing an error in docker setups)
v2.16.0 Breaking risk
Breaking changes
  • Removed redundant storage section from Settings
Notable features
  • New UI toggle in Settings > General to manually enable/disable maintenance mode
  • Top bar badge indicates Maintenance Mode status with link to Settings
  • Auto‑enable maintenance mode after CLI (`bbs-restore`) and S3 web restores to pause new backup jobs
Full changelog

Maintenance Mode

  • New UI toggle in Settings > General to manually enable/disable maintenance mode
  • Top bar badge shows "Maintenance Mode: On" with link to Settings when active
  • Auto-enabled after restore — both CLI (bbs-restore) and S3 web restore automatically enable maintenance mode to prevent the scheduler from queuing new backup jobs before repositories are restored
  • Server-side jobs (catalog rebuild, prune, compact, etc.) still run during maintenance mode — only new backup/restore jobs are paused

Restore Improvements

  • bbs-restore now clears stale job queue and enables maintenance mode after database import
  • S3 web restore enables maintenance mode after successful restore
  • Generate random MySQL password for new Docker installs

Performance & Stability

  • Streaming catalog rebuild for remote SSH reposborg list output is now streamed line-by-line instead of buffered into memory, fixing out-of-memory crashes on large repositories (tested with 850K+ files per archive)
  • Streaming ClickHouse inserts — TSV data is streamed to ClickHouse via curl instead of loading entire files into memory
  • Slimmed down bbs-update-run for faster server updates
  • Fix Windows agent going offline after self-update (agent v2.14.1)

UI Cleanup

  • Removed redundant storage section from Settings (now fully managed in Storage page)
v2.15.0 Breaking risk
⚠ Upgrade required
  • If the Windows agent went offline after updating, manually restart the service with `sc start BorgBackupAgent` or reboot the machine.
Notable features
  • Catalog rebuild now syncs archives from borg before rebuilding
  • 'Include beta versions' checkbox added to update checker
Full changelog

What's New

NFS & Multiple Storage Location Fixes (#25)

  • Fix borg init on NFS storage: Repository initialization now runs through bbs-ssh-helper as root, resolving "Permission denied" errors when creating repos on NFS-mounted storage locations (Synology NAS, TrueNAS, generic Linux NFS)
  • Fix repo operations on non-default storage paths: borg-list, borg-list-archive, borg-cmd (prune, compact, check, etc.) now support repositories on any registered storage location, not just /var/bbs/
  • Fix chown failures on NFS: Ownership operations are now non-fatal — NFS with user mapping doesn't support chown, and it's not needed when all users map to the same NFS admin user
  • Fix storage-paths not written on init failure: .storage-paths file is now updated before borg init so SSH access works even if initialization needs to be retried
  • Fix NFS owner detection: bbs-ssh-helper now handles "UNKNOWN" UIDs from NFS stat by looking up the correct user from /etc/passwd
  • Extracted is_allowed_path() helper in bbs-ssh-helper for consistent path validation across all commands

Windows Agent Fix

  • Fix Windows agent going offline after self-update: The agent update mechanism used sc stop/sc start to restart the Windows service, which raced with the launcher's own restart loop and could leave the service permanently stopped. The agent now exits cleanly and lets the launcher handle the restart automatically.

⚠️ Windows Agent Users: If your Windows agent went offline after updating, the service has stopped and cannot pull the fix on its own. You need to restart the service manually by running sc start BorgBackupAgent in an elevated command prompt, or restart the Windows machine. Once the service is running again, it will automatically update to the fixed agent version.

Catalog Rebuild Improvement

  • Sync archives from borg before catalog rebuild: Catalog rebuild now runs borg list first to discover any archives in borg that aren't yet in the database, ensuring rebuilds always work with fresh data

Documentation

  • Updated Storage Setup wiki with comprehensive NFS setup instructions for Synology NAS, TrueNAS, and generic Linux — including Docker NFS volume configuration and troubleshooting
  • Updated Docker Installation wiki with NFS/multi-storage setup, ARM/ClickHouse notes, and .env persistence details

Previous Beta Changes (since v2.14.0)

  • Fix storage-paths not updated when default location differs from SSH home
  • Add manual path restore/download when ClickHouse is unavailable (#30)
  • Add 'Include beta versions' checkbox to update checker
  • Fix Windows agent not restarting after sleep/wake
v2.14.0 New feature
Notable features
  • Configure multiple storage locations via the new **Storage** page, consolidating Remote SSH and S3 Sync settings
  • Import existing Borg repositories without re‑running full backups through the Repositories tab
  • Windows Agent now handles sleep/wake events and fixes a stop‑request race condition (bumped to version 2.13.4)
Full changelog

What's New

Storage Locations

Configure multiple storage paths so different repositories can live on separate disks or mount points. Manage all storage from the new Storage page, which also consolidates Remote Storage (SSH) and S3 Sync configuration.

Import Existing Repositories

Bring previously created borg repositories under management without re-running a full backup. Available from the Repositories tab on any client.

Restore Without ClickHouse (#30)

When ClickHouse is unavailable (e.g., ARMv8.0 hardware), the Restore tab now shows a manual path entry UI instead of failing silently. Users can type file/directory paths or select "Restore entire archive" — no catalog browsing required.

Update Checker Improvements

  • The update checker now shows only stable releases by default, with an Include beta versions checkbox for those who want to opt in
  • Pre-releases and drafts are filtered out of the upgrade prompt

Windows Agent: Sleep/Wake Fix

  • Fixed a race condition in the Windows service launcher where a stop request during the restart delay was ignored, causing the agent to restart then immediately get killed
  • Added power event handling so the service detects wake-from-sleep and restarts the agent subprocess automatically
  • Agent version bumped to 2.13.4

Bug Fixes

  • SSH storage path stability — Added ssh_home_dir to the agents table so changing the default storage location no longer breaks existing SSH access to repos
  • SSH repo path port leak — Fixed non-default storage locations leaking the SSH port into the repository path
  • Download permission errors — Fixed "permission denied" on extracted files during server-side downloads by running a separate sudo permission fix after extract
  • Non-default storage locations — Fixed server-side borg extract, prune, compact, and repo size calculations using the wrong path for repos on non-default storage locations
  • Repository verification — Switched from borg info to borg list --json for more reliable repo verification during import
  • S3 endpoint validation — Validate S3 endpoint URLs before saving and testing
  • S3/rclone timeouts — Added timeouts to S3 rclone commands to prevent Apache worker hangs
  • UI fixes — Fixed dropdown menus clipped on repo and schedule cards; fixed catalog log files not cleaned up after import
  • Download handler — Cleared output buffer before sending download headers to prevent corrupt archives
v2.13.4 Bug fix

Fixed catalog rebuild deleting other repositories' data and treating their archives as orphaned on clients with multiple repos.

Full changelog

Bug Fixes

  • Fix catalog rebuild deleting other repos' data on same client — full rebuild was dropping the entire agent partition in ClickHouse, wiping catalog data for all repos on that client. Incremental rebuild also treated other repos' archives as orphaned. Both now scope operations to the specific repository's archive IDs, fixing the infinite rebuild loop when a client has multiple repositories (#27)
v2.13.3 Bug fix
Notable features
  • Allow queuing different maintenance actions on a repository
  • Maintenance buttons remain enabled during active jobs
Full changelog

Bug Fixes

  • Fix server-side jobs failing when agent is offline — catalog rebuild, prune, compact, and other server-side tasks no longer fail just because the agent is offline (#21)
  • Fix queue detail showing "Waiting for progress data from agent" for server-side tasks — now correctly shows "Running on server..."
  • Fix JS isServerSide list — was missing most server-side task types, causing incorrect UI behavior for catalog rebuild, repo check, repair, etc.

Improvements

  • Allow queuing different maintenance actions — previously any active job on a repo blocked all other maintenance buttons; now you can queue a compact while a check is running, etc.
  • Maintenance buttons no longer disabled during active jobs — the controller prevents duplicate actions of the same type server-side
v2.13.2 Bug fix

Fixed a stall detection loop that prevented catalog rebuilds from completing.

Full changelog

Bug Fixes

  • Fix catalog rebuild abandon loop — stall detection no longer asks the agent about server-side tasks (catalog rebuild, prune, compact, etc.), preventing a fail/retry loop that made catalog rebuilds impossible to complete (#21)
  • Fix zombie job cleanup for server-side tasks — the 24h timeout safety net now also excludes server-side task types
  • Track progress for server-side catalog jobs — catalog sync and catalog rebuild now update last_progress_at during processing for accurate progress tracking
v2.13.1 Bug fix

Fixed Windows SSH key permission handling to prevent "UNPROTECTED PRIVATE KEY FILE" errors.

Full changelog

Bug Fixes

  • Fix Windows SSH key permissions — the agent now properly strips all ACLs (including explicit BUILTIN\Users entries) before setting read-only access for SYSTEM and Administrators, fixing "UNPROTECTED PRIVATE KEY FILE" errors on Windows (#23)
v2.13.0 New feature
Notable features
  • Duplicate backup plan option creates a paused copy via the plan's 3‑dot menu
  • Repository passphrase can be revealed and copied to clipboard on the repository detail page
Full changelog

New Features

  • Duplicate backup plan — new "Duplicate" option in the plan's 3-dot menu; the copy is created paused to prevent accidental duplicate schedules
  • Repository passphrase reveal — click-to-reveal and copy-to-clipboard for the repo passphrase on the repository detail page

Bug Fixes

  • Detect pipx/pip borg installs — the agent now checks ~/.local/bin/borg so borg installed via pipx or pip is detected even when running as a systemd service (#15)
v2.12.3 Bug fix

Fixed UID collisions on fresh installs and prevented backups from failing due to catalog streaming errors.

Full changelog

Bug Fixes

  • Fix UID collision with system groups on fresh install — New SSH users were allocated UIDs in the system range (100–999) via useradd --system, causing the first user on a fresh container to collide with the crontab group (UID 997). Users now get UIDs ≥ 1000. Existing installs with bad UIDs will self-heal on next container restart. (#13)

  • Don't fail backups when only catalog streaming fails — Catalog streaming is a non-critical UI feature; a SSH pipe failure no longer marks the entire backup as failed. The job stays completed with a warning note that includes the actual SSH error for easier diagnosis. (#12)

v2.12.2 Bug fix
⚠ Upgrade required
  • Pull the latest image (marcpope/borgbackupserver:latest) and restart the compose stack to apply the ownership fix.
Full changelog

Fix: Per-user cache directory ownership reset on container restart

Fixes server-side prune failing with Permission denied after container restart.

What happened

The entrypoint ran chown -R www-data:www-data /var/bbs/cache, which reset per-user borg cache directories (e.g., /var/bbs/cache/bbs-batam) to www-data ownership. Since prune runs as the SSH user, it lost access to its own cache directory.

Fix

  • /var/bbs/cache is now only chown'd at the top level (not recursive), matching the fix already applied to /var/bbs/home
  • Per-user cache directory ownership is restored during the SSH user recreation loop on startup

Upgrade

docker pull marcpope/borgbackupserver:latest
docker compose up -d
v2.12.1 Bug fix
⚠ Upgrade required
  • Pull the latest image and restart the compose stack: `docker pull marcpope/borgbackupserver:latest && docker compose up -d`
Full changelog

Hotfix: Container crash on startup after upgrading from pre-2.12.0

Fixes a startup crash introduced in v2.12.0 where the container would restart in a loop with:

chown: invalid group: 'bbs-XXXXX:bbs-XXXXX'

What happened

Older versions ran chown -R www-data:www-data /var/bbs/home, changing all home directory ownership to www-data (UID 33). When v2.12.0's new user recreation logic ran, it detected UID 33 from the directory and tried to create SSH users with that UID — which silently failed because UID 33 already belongs to www-data. The subsequent chown with the non-existent username crashed the entrypoint.

Fix

  • Detects when a directory's UID already belongs to another system user and allocates a fresh UID instead
  • Verifies user creation succeeded before attempting ownership changes
  • Uses numeric IDs for .ssh ownership to avoid group name lookup failures

Upgrade

docker pull marcpope/borgbackupserver:latest
docker compose up -d
v2.12.0 Bug fix
Notable features
  • Included `bbs-ssh-gate` binary in the Docker image
Full changelog

SSH Authentication Fix for Docker Deployments

This release fixes a critical bug where all SSH agents stopped authenticating after a Docker container restart (#11).

What was broken

Four issues in the container entrypoint combined to break SSH authentication on every restart:

  • User recreation failed silently — the entrypoint searched for directories matching bbs-* but home directories are named by agent ID (e.g., 1, 2). SSH users were never recreated in the container's /etc/passwd, so sshd rejected all connections.
  • File ownership was clobbered — a recursive chown reset all .ssh/authorized_keys files to www-data ownership before users were recreated. OpenSSH requires these files to be owned by the connecting user.
  • sshd started too early — the SSH server launched before users existed in the system, guaranteeing auth failures during the startup window.
  • Legacy SSH config was lost — the PubkeyAcceptedAlgorithms +ssh-rsa setting (required by OpenSSH 10 in the container) was only written during updates, not during container startup.

What's fixed

  • SSH users are now recreated from the database with correct UID mapping
  • File ownership is set correctly per-user (home dir user:www-data, .ssh/ dir user:user)
  • sshd now starts after all users and SSH config are in place
  • Legacy SSH compatibility config is written on every container start
  • bbs-ssh-gate is now included in the Docker image (was previously missing)

Upgrade

Pull the latest image and restart your container. No agent reconnection needed — existing agents will authenticate automatically once the container starts with the fix.

docker pull marcpope/borgbackupserver:latest
docker compose up -d
v2.11.0 New feature
Notable features
  • Server‑Agent stall recovery automatically marks stuck jobs as failed with clear explanation
  • Resilient status reporting with exponential backoff retries for completion reports
  • Idempotent status endpoint safely handles duplicate reports
Full changelog

Welcome to Update v2.11.0

[!NOTE]
Still looking for feedback testing Windows Clients: See update v2.10.0

Stall Detection & Self-Healing Job Queue

Backups should never silently disappear. In v2.11.0, BBS gains intelligent stall detection that ensures every job reaches a definitive outcome — even when things go wrong between the agent and server.

What's New

Server-Agent Stall Recovery — If a job completion report is lost due to a transient server error (database restart, network blip, etc.), BBS now detects and recovers automatically. The server monitors job progress and asks the agent directly: "Are you still working on this?" If the agent confirms it's moved on, the job is marked as failed with a clear explanation — no more zombie jobs sitting in "running" forever.

Resilient Status Reporting — The agent now retries completion reports with exponential backoff. A momentary server hiccup no longer means a lost backup result. Five retry attempts over ~2.5 minutes ensure the report gets through.

Idempotent Status Endpoint — Status reports are now safe to retry. Duplicate reports (from retries where the response was lost) are detected and handled gracefully — no duplicate archives, notifications, or prune jobs.

24-Hour Safety Net — As a last resort, the scheduler automatically fails any job that's been running for over 24 hours with no progress. This catches edge cases even on agents that haven't updated yet.

Multi-Client Access Controls

Filtered Notifications — The notification bell and notification list now respect user-level client access. Users only see alerts for the clients they manage.

Filtered Logs — The server log page is filtered by accessible clients, keeping multi-tenant views clean and scoped.

Agent v2.11.1

  • Retry logic for all status reports (exponential backoff, 5 attempts)
  • Responds to server stall checks via check_jobs polling mechanism
  • Reports abandoned for jobs no longer in progress

*To update: go to Settings > Updates in the Borg Backup Server Software.

v2.10.0 New feature
Notable features
  • Native Windows PowerShell installer for BBS agent (zero‑dependency, automatically installs borg-windows)
  • Cross‑platform BBS agent (`bbs-agent.py`) runs natively on Windows with full backup, restore, and self‑update support
  • Windows Service integration (`BorgBackupAgent`) with auto‑start and failure recovery
Full changelog

Windows Support (Pre-Release)

[!CAUTION]
Windows support is currently pre-release software. We've built a special, native version of BorgBackup for Windows
and have been testing it. Please report any issues.

Native Windows you ask?

Borg Backup Server (BBS for Short) can now back up Windows machines! This release adds preliminary Windows agent support with a zero-dependency PowerShell installer. No Python, WSL, or Cygwin needed.

What's included:

  • One-line PowerShell installer — automatically downloads and installs Borg for Windows, the BBS agent, and configures a Windows Service
  • Tabbed Install UI — the client Install tab now has Linux/macOS and Windows sub-tabs with platform-specific install commands
  • Cross-platform agent — the BBS agent (bbs-agent.py) now runs natively on Windows with full backup, restore, and self-update support
  • Windows Service — the agent runs as a proper Windows Service (BorgBackupAgent) with auto-start and failure recovery
  • Catalog & file browsing — Windows backup archives are fully browsable in the Restore tab with correct directory trees
  • File download — download individual files or folders from Windows backups directly from the dashboard

Additional changes:

  • Portable archive paths — Windows backups use forward slashes and drive letter prefixes (C/Users/...) for cross-platform compatibility
  • In-place restore support with --strip-components to correctly restore files back to their original drive locations
  • Multi-drive restore warning when selected files span multiple drives without a custom destination

Known limitations:

  • Windows support is pre-release — recommended for non-production workloads
  • Requires borg-windows (installed automatically by the agent installer)

We'd love your feedback on how Windows support works for you! Please report any issues or share your experience at https://github.com/marcpope/borgbackupserver/issues

v2.9.1 Bug fix

Fix .env persistence across container recreation preventing decryption of encrypted data.

Full changelog

Bug Fixes

  • Fix .env not persisted across Docker container recreation (#10) — The .env file (containing the APP_KEY used to encrypt SSH keys and S3 credentials) was stored inside the container's ephemeral filesystem. After docker compose down/up, a new APP_KEY was generated, making all encrypted data undecryptable. The .env is now stored on the /var/bbs persistent volume with a symlink back to the expected path. Existing containers are migrated automatically on next start.

  • Fix rclone test connection failures in Docker — The S3 test connection was missing essential environment variables (HOME, PATH, RCLONE_CONFIG), causing rclone to fail with getent not found and .rclone.conf not found errors.

v2.9.0 Breaking risk
⚠ Upgrade required
  • Removed one-time migration restore script; use bbs-restore for similar functionality
Security fixes
  • Install rclone from official binary on bare metal installs to fix Go runtime CVEs in Debian package
Notable features
  • Rsync.net Remote Storage Wizard with SSH key instructions, borg version selector (borg12/borg14), and connection testing
  • Configurable Server Backups section to enable/disable daily backups, set retention count, and optionally include ClickHouse catalog data
  • Comprehensive Backup & Restore now includes SSH host keys, sudoers config, and cron jobs with backward‑compatible restore
Full changelog

New Features

  • rsync.net Remote Storage Wizard — Setup wizard for rsync.net with SSH key instructions, borg version selector (borg12/borg14), and connection testing
  • Configurable Server Backups — New "Server Backups" section in Settings > General to enable/disable daily backups, set retention count, and optionally include ClickHouse catalog data
  • Comprehensive Backup & Restore — Server backups now include SSH host keys, sudoers config, and cron jobs in addition to database and .env. Restore script handles all new contents with backward compatibility

Improvements

  • Install rclone from official binary on bare metal installs (fixes Go runtime CVEs in Debian package)
  • Backup script accepts --keep N and --with-catalogs flags
  • Settings General tab layout reorganized (Agent moved under Server)

Housekeeping

  • Removed one-time migration restore script (bbs-restore covers this use case)
v2.8.4 Security relevant
Security fixes
  • CVE‑2026‑24049 — Upgraded Python wheel package to v0.46.3 (CVSS 7.1)
  • CVE-2026-24049
Notable features
  • Added HTTP security headers: X-Frame-Options, X-Content-Type-Options, Referrer-Policy
  • Session cookies now include the Secure flag automatically when accessed over HTTPS
Full changelog

Security Hardening

  • Added HTTP security headers — X-Frame-Options, X-Content-Type-Options, and Referrer-Policy headers are now set on all responses to protect against clickjacking and MIME-sniffing attacks
  • Secure session cookies on HTTPS — Session cookies now include the Secure flag automatically when the server is accessed over HTTPS (HTTP/LAN deployments are unaffected)
  • Fixed wheel CVE-2026-24049 (CVSS 7.1) — Upgraded Python wheel package to v0.46.3 in Docker image
v2.8.3 Mixed
Security fixes
  • CVE-2026-24049 (CVSS 7.1) — pinned wheel>=0.46.2 to mitigate vulnerability
Notable features
  • Replaced Parsedown with league/commonmark for GitHub Flavored Markdown support
Full changelog

Bug Fixes

  • Fixed "Rebuild Full Catalog" — resolved a database error when triggering a full catalog rebuild (catalog_rebuild_full was missing from the task_type ENUM)

Improvements

  • Replaced Parsedown with league/commonmark — fixes PHP 8.4 deprecation warnings in release notes display; uses the actively-maintained CommonMark library with GitHub Flavored Markdown support

Security & Dependency Updates

  • Pinned wheel>=0.46.2 to address CVE-2026-24049 (CVSS 7.1)
v2.8.2 Security relevant
Security fixes
  • rclone upgraded to v1.73.1 — addresses multiple CVEs from previous version
Full changelog

Security & Dependency Updates

  • Upgraded rclone to v1.73.1 (from Debian-packaged v1.65), installed from official binary to address multiple CVEs noted in docker hub
  • Bind mount documentation added to docker-compose.yml for users storing data on external disks
v2.8.1 Security relevant
Security fixes
  • Upgraded Docker base image to PHP 8.4 (Debian Trixie) and added `apt-get upgrade` in the build process to apply latest system security patches.
  • Updated Apache to version 2.4.66 and OpenSSL to the latest Debian Trixie release.
  • ClickHouse updated to the latest stable release with a patched Go runtime.
Full changelog

Security Updates

  • Upgraded Docker base image from PHP 8.1 to PHP 8.4 (Debian Trixie)
  • Added apt-get upgrade to Docker build to pull latest security patches for all system packages
  • Updated Apache to 2.4.66 and OpenSSL to latest Trixie release
  • ClickHouse updated to latest stable release with patched Go runtime
v2.8.0 Breaking risk
Notable features
  • Full Disk Access integration for macOS agent via .app bundle
  • Dynamic launchd plist generation detecting python3 paths on Apple Silicon and Intel
  • Modern launchctl usage with bootstrap/bootout commands
Full changelog

Improved macOS Agent Support

  • Full Disk Access integration — compiled wrapper binary packaged as a .app bundle so macOS properly displays it in System Settings → Privacy & Security → Full Disk Access
  • Dynamic launchd plist generation — installer detects the correct python3 path (supports both Apple Silicon /opt/homebrew and Intel /usr/local)
  • Fixed duplicate log lines — agent no longer double-logs when running under launchd or systemd
  • Fixed hostname detection — macOS agents now report the correct hostname instead of IPv6 reverse DNS
  • Modern launchctl commands — uses bootstrap/bootout instead of deprecated load/unload
  • Step-by-step FDA instructions — installer output guides users through granting Full Disk Access

Docker Improvements

Docker support is still in beta, changes may break the installation, it's getting close to stable.

  • Docker Updates — We've added many improvements to the Docker Setup. Seeking feedback.
  • ClickHouse data persistence — ClickHouse data now stored on the persistent Docker volume at /var/bbs/clickhouse/, surviving container recreation. Previously restarting or upgrading could lose catalog data (not backup data) if not stored on a persistent volume. >2.6 and higher moved this to /var/bbs volume which is mapped to a persistent volume.
v2.7.5 New feature
⚠ Upgrade required
  • Run `docker compose pull` followed by `docker compose up -d` when upgrading from previous versions to apply the new persistent storage layout.
  • Existing ClickHouse data at /var/lib/clickhouse/ inside containers will no longer be used; migrate any critical files to the new volume path (/var/bbs/clickhouse/) before upgrade.
Notable features
  • ClickHouse catalog data stored at /var/bbs/clickhouse/ on the Docker volume for persistence
  • Docker-specific upgrade instructions shown in Settings > Updates page (docker compose pull/up)
  • Application code and dependencies baked into the Docker image, eliminating runtime Git clone
Full changelog

Docker Improvements

  • ClickHouse catalog data now persists on the Docker volume — Previously, ClickHouse stored data at /var/lib/clickhouse/ inside the container, meaning the entire file catalog was lost on container recreation. Data is now stored at /var/bbs/clickhouse/ on the persistent volume, matching the existing pattern for MariaDB at /var/bbs/mysql/.

  • Docker-specific upgrade instructions — The Settings > Updates page now detects Docker environments and shows docker compose pull / docker compose up -d instructions instead of the bare-metal upgrade button.

  • Application code baked into Docker image — Code and dependencies are now included in the image at build time. Containers no longer clone from GitHub at runtime, eliminating silent upgrades on restart.

  • ClickHouse idle disk I/O fix — Disabled ClickHouse system log tables that were causing ~13 GB of disk writes and constant CPU usage on idle installs.

v2.7.0 New feature
⚠ Upgrade required
  • Old ClickHouse system log tables are automatically dropped on upgrade, reclaiming disk space
Notable features
  • Application code baked into Docker image at build time; runtime no longer clones from GitHub
  • Container restarts stop silent software upgrades; users must pull new image to upgrade
  • Settings page displays Docker‑specific upgrade instructions when running in Docker
Full changelog

What's New in 2.7.0

Docker Improvements

  • Application code is now baked into the Docker image at build time — no more cloning from GitHub at runtime
  • Container restarts no longer silently upgrade the software
  • Users upgrade by pulling a new image: docker compose pull && docker compose up -d
  • Settings page shows Docker-specific upgrade instructions when running in Docker
  • Developer Sync hidden in Docker (no git repo available)

ClickHouse Performance Fix

  • Disabled ClickHouse system log tables to eliminate idle disk I/O and CPU usage
  • Old system log tables automatically dropped on upgrade to reclaim disk space

Beta — feedback welcome: [email protected]