| title | Security |
|---|---|
| nav_order | 7 |
| description | Security practices for Borg UI deployments |
Borg UI controls backup and restore operations. Treat it as sensitive infrastructure.
This page is about hardening your deployment. For what the software itself stores, encrypts, and sends anywhere else, see What Borg UI Does With Your Data.
After installation:
- Change the default
adminpassword in Settings > Account. - Use a strong
INITIAL_ADMIN_PASSWORDfor new deployments. - Keep
/dataprivate and backed up. - Put the app behind TLS if it is reachable over a network.
- Keep Borg UI updated.
The /data volume contains:
- SQLite database
- generated secret key
- logs
- encrypted SSH key material
- job metadata
Anyone who can read or modify /data can affect the Borg UI instance.
Mounting /var/run/docker.sock gives the Borg UI container control over the host Docker daemon.
Only mount it if Docker hook scripts need it:
volumes:
- /var/run/docker.sock:/var/run/docker.sock:rwDo not mount it for normal backups. If hook scripts only need limited Docker container actions, prefer a Docker socket proxy and set Borg UI's Docker CLI endpoint with DOCKER_HOST=tcp://docker-socket-proxy:2375.
Recommended order:
- Built-in local auth for small/self-hosted deployments.
- Built-in OIDC for SSO.
- Trusted-header auth only behind a locked-down reverse proxy.
Never expose trusted-header auth directly to users.
Use built-in OIDC when Borg UI should talk directly to the identity provider.
Keep frontend and API on the same public origin. Split-origin setups need explicit CORS/cookie work and are not the default supported path.
See Authentication and SSO for setup details.
Trusted-header auth reads identity from proxy-set headers.
Safe only when:
- the Borg UI container is not directly reachable
- the proxy strips incoming user-controlled auth headers
- only the proxy can set the trusted headers
If direct access is possible, a user can spoof headers and impersonate another user.
See Authentication and SSO for header settings.
The /metrics endpoint should be private or token-protected.
Recommended:
- enable metrics only when needed
- require a token
- let Prometheus scrape it over a private network
- expose Grafana, not
/metrics, to users
Use Remote Machines and SSH keys instead of passwords.
For remote backup users:
- use a dedicated Unix user
- grant only required filesystem access
- restrict Borg keys with
borg serve --restrict-to-pathwhere practical - rotate keys when access changes
Breaking a Borg lock while another Borg process is active can corrupt or damage a repository.
Before breaking a lock, confirm there is no:
- running backup
- restore
- check
- prune
- compact
- mount
- external Borg process
If an admin is locked out and you have shell access to the container:
docker exec -it borg-web-ui python -m app.scripts.reset_password admin newpassword123Shell access to the container is equivalent to administrative access.
- terminate TLS at the proxy
- forward
Host,X-Forwarded-For, andX-Forwarded-Proto - support WebSocket/SSE-style long-running connections
- do not expose trusted-header auth directly
- register OIDC callback URLs using the public URL users actually open
See Reverse Proxy.
Update with:
docker compose pull
docker compose up -dKeep /data and Borg repository backups before major upgrades.
New non-admin server backup sources and R/r roots in --pattern flags are
checked when selected: they must be absolute, contain no .., resolve inside
LOCAL_MOUNT_POINTS, and neither contain nor be inside /app or DATA_DIR.
New dump, export, and btrfs staging metadata must also avoid those reserved
directories, but may live outside the mount points. New agent sources and
pattern roots, and restores from agent repositories, require an administrator.
See Access Control and
Configuration for the complete role and path rules.
Existing plans are not migrated or revalidated at runtime. Their stored paths, metadata, and pattern roots remain usable in manual and scheduled runs, including paths outside the newly allowed area. Operators can retain or remove those paths while editing other fields. Removing a path and adding it again makes it a new selection. Plans with no remaining repositories cannot retain unchecked paths. Copying sources from an administrator-configured repository remains permitted.
An administrator should review existing plans after upgrading. Use
GET /api/backup-plans/ with an administrator account to inspect every plan and
its repository links, then edit plans that exceed the intended access:
- Review both
source_directoriesand structuredsource_locations, including existing agent paths and the associated agent machine. - Review
R/rroots in plancustom_flagsand linkcustom_flags_override, including disabled links that could later resume. - Review database
dump_path, containerexport_path, and btrfs snapshotstaging_pathmetadata for reserved-directory access. - Remove or narrow sources and roots outside the intended mount points or covering the application or data directory. Review existing agent paths with the administrator responsible for that host.
These are selection-time checks, not complete filesystem confinement. A path
that is safe when selected can later be replaced by a symlink; execution does
not repeat the check. The contents of --patterns-from and --exclude-from
files are not validated by these new checks (their file paths retain the
existing mount-point validation). ZFS dataset/mountpoint and database
backup_paths retain their existing behavior. Metadata path strings, including
remote SSH locations' database dump_path and container export_path, are
checked against the server's reserved-path policy. With
script_execution_target=source, those metadata scripts still execute on the
SSH host; these checks do not confine its filesystem or symlinks. Managed agents
have no allowed-root configuration, which is why new operator agent paths and
agent restores are refused.
Non-admin Borg mount targets, including automatically suffixed occupied targets,
must resolve strictly below DATA_DIR/mounts before directory creation. SSHFS
staging paths are bounded for everyone: .. is normalized and a symlink escape
is refused before creating the target or mounting it. An administrator backup
that previously followed such an escape now fails instead of reading the
symlink's target on the server; ordinary administrator path selection is
unchanged.