-
-
Notifications
You must be signed in to change notification settings - Fork 7
Docker Installation
Run BBS in a Docker container — no OS dependencies to install. This is the fastest way to get BBS running and is ideal for testing, home labs, and production environments where you prefer containerized deployments.
BBS runs its own MariaDB, ClickHouse, Apache, SSH daemon, and cron scheduler inside a single container. It is not designed to share a container with another application, share its data volume with another service, or mount its data directory inside another container. If you want to back up another server or container, install the BBS agent there — not the full server.
- Docker Engine 20.10+ and Docker Compose v2
- 2 GB RAM minimum (4 GB+ recommended)
- Two available ports: one for the web UI (default 8080) and one for SSH/borg connections (default 2222)
ARM note: BBS runs on both x86_64 and ARM64 (Apple Silicon, Raspberry Pi 4+, etc.). However, ClickHouse — used for the file-level search catalog — does not support older ARM chips (ARMv8.0). BBS works fine without ClickHouse; you just won't have file-level browsing on the Restore page (you can still restore by typing paths manually).
Download the compose file and environment template:
curl -sO https://raw.githubusercontent.com/marcpope/borgbackupserver/main/docker-compose.yml
curl -sO https://raw.githubusercontent.com/marcpope/borgbackupserver/main/.env.example
cp .env.example .envEdit .env with your server's IP or hostname:
APP_URL=http://192.168.1.50:8080
WEB_PORT=8080
SSH_PORT=2222Start the container:
docker compose up -dThe pre-built image (marcpope/borgbackupserver) is pulled automatically from Docker Hub.
Get admin credentials from the container logs:
docker compose logs bbsLook for the credentials block:
========================================
ADMIN CREDENTIALS (SAVE THESE!)
========================================
URL: http://192.168.1.50:8080
Username: admin
Password: <random password>
========================================
Open your server's URL and log in.
If you pull the image directly from Docker Desktop without using docker-compose.yml, a first-run setup modal appears on your first admin login. It prompts you to enter your server hostname, web port, and SSH port so agents can connect. Set these to match the port mappings you configured when creating the container.
All configuration is done through a .env file (or environment variables). The docker-compose.yml reads variables from .env automatically — ports are defined once and used in both port mappings and container environment.
| Variable | Default | Description |
|---|---|---|
APP_URL |
http://localhost:8080 |
Public URL used by browsers and agents to reach BBS |
WEB_PORT |
8080 |
Host port for the web UI (mapped to container port 80) |
SSH_PORT |
2222 |
Host port for SSH/borg connections (mapped to container port 22) |
ADMIN_PASS |
(auto-generated) | Admin password — only used on first run. Omit to auto-generate (shown in logs) |
Important:
APP_URLmust use your server's real IP or hostname — notlocalhost— for agents on other machines to connect.
To set a specific admin password, add it to your .env file:
ADMIN_PASS=your-strong-password-hereThis is only applied on first run when the database is initialized. Changing it later has no effect — use the web UI to change passwords after initial setup.
For a production server with a real hostname and HTTPS (via a reverse proxy):
APP_URL=https://backups.example.com
WEB_PORT=443
SSH_PORT=2222You would place an HTTPS reverse proxy (nginx, Caddy, Traefik) in front of the container to handle SSL termination. The container itself serves HTTP on port 80.
BBS needs two ports: HTTP for the web UI and API, and SSH for borg backup data transfer. Both are controlled by variables in your .env file:
WEB_PORT=8080 # Web UI (HTTP) — mapped to container port 80
SSH_PORT=2222 # SSH for borg agent connections — mapped to container port 22The docker-compose.yml uses these variables in both port mappings and the container environment, so they stay in sync automatically. To change ports, just edit .env — no need to touch docker-compose.yml.
For example, to use standard ports:
APP_URL=https://backups.example.com
WEB_PORT=443
SSH_PORT=22All persistent data lives in a single Docker volume (bbs-data) mounted at /var/bbs inside the container:
| Path | Contents |
|---|---|
/var/bbs/home/ |
Borg repositories — one directory per client |
/var/bbs/mysql/ |
MariaDB database files |
/var/bbs/clickhouse/ |
ClickHouse catalog database (file-level search index) |
/var/bbs/config/ |
Application .env file (contains encryption key — do not lose) |
/var/bbs/backups/ |
Server self-backup archives |
/var/bbs/cache/ |
Borg cache (speeds up backup operations) |
/var/bbs/.ssh-host-keys/ |
SSH host keys (persisted so agents don't see host key warnings after updates) |
Each registered client gets its own SSH user and home directory under /var/bbs/home/. Borg repositories are created inside each client's home directory. This is all handled automatically when you add a client through the web UI.
Important: The
.envfile at/var/bbs/config/.envcontains theAPP_KEYwhich encrypts SSH keys and S3 credentials stored in the database. If this key is lost, all encrypted data becomes unrecoverable. This file is created on first run and stored on the persistent volume.
Named volumes are strongly recommended. Bind mounts require careful per-subdirectory ownership because BBS runs multiple services as different UIDs inside the container (MariaDB, ClickHouse, Apache, root). Named volumes avoid this entirely.
To store data on a specific disk or partition instead of a Docker volume:
volumes:
- /path/to/your/storage:/var/bbsRemove the volumes: section at the bottom of the compose file (the bbs-data: volume definition) when using a bind mount.
Ensure the parent directory exists before starting the container. The entrypoint creates the required subdirectories with the correct ownership on first run.
If the container exits immediately after "Starting MariaDB..." or you see Permission denied errors touching /var/bbs/*, the host directory is blocking the entrypoint's chown calls. This commonly happens with:
-
Rootless Docker / Podman — the container "root" is mapped to a subuid on the host. Use a host path owned by the host user running Docker, or add
:U(Podman) to the volume to auto-remap ownership. -
SELinux-enforcing hosts — add
:Zor:zto the volume mount:- /path/to/storage:/var/bbs:Z -
Bind-mounted network filesystems (NFS, SMB) — may silently reject chown. Use a local disk for the
/var/bbsmount and add network shares only as additional storage locations (see below).
BBS creates these subdirectories inside /var/bbs on first start, each owned by the specific service that uses it:
| Path | Owner (inside container) | UID |
|---|---|---|
/var/bbs/mysql/ |
mysql |
100 |
/var/bbs/clickhouse/ |
clickhouse |
999 |
/var/bbs/config/ |
root |
0 |
/var/bbs/home/ |
www-data |
33 |
/var/bbs/cache/ |
www-data |
33 |
/var/bbs/backups/ |
www-data |
33 |
/var/bbs/tmp/ |
www-data |
33 (mode 1777) |
/var/bbs/.ssh-host-keys/ |
root |
0 |
You normally do not need to set these manually — the entrypoint chowns them on first run. Only intervene if chowns are being blocked by your host setup.
BBS supports multiple storage locations, letting you spread repositories across different disks or NFS mounts. The default location (/var/bbs/home) stores client home directories. Additional locations are used for repository storage only.
To add a second disk or NFS share in Docker:
-
Add the volume mount to
docker-compose.yml:services: bbs: volumes: - bbs-data:/var/bbs # existing default volume - /mnt/disk2:/mnt/disk2 # additional local disk
For NFS, you can use a Docker NFS volume:
services: bbs: volumes: - bbs-data:/var/bbs - nfs-storage:/mnt/nfs-backup volumes: bbs-data: nfs-storage: driver: local driver_opts: type: nfs o: addr=192.168.1.100,rw,nfsvers=3,nolock device: ":/volume1/bbs-storage"
Replace
192.168.1.100and/volume1/bbs-storagewith your NFS server IP and export path. -
Restart the container:
docker compose up -d
-
Add the storage location in BBS:
- Go to Storage in the sidebar
- Click Add Location
- Enter a label and the path (e.g.,
/mnt/disk2or/mnt/nfs-backup— must match the path inside the container) - Click Create
-
Create repositories on the new location:
- Go to any client, click Add Repository
- Select the new storage location from the dropdown
Important for NFS: The NFS server must allow full read/write access from the Docker container. If using a Synology NAS, set the NFS Squash to "Map all users to admin". See Storage Setup for detailed NFS configuration instructions and troubleshooting.
You don't have to store all repositories locally. BBS supports creating borg repositories on remote SSH hosts like rsync.net, BorgBase, and Hetzner Storage Box. With remote storage, backup data flows directly from agents to the remote host — no local disk is used for repository data, only catalog metadata is stored in the BBS database.
This is especially useful with Docker deployments where local storage may be limited. See Remote Storage for setup details.
The application code and all dependencies are baked into the Docker image at build time — no downloads occur at runtime. The container's entrypoint script handles runtime setup on each start:
- Restores SSH host keys — from the persistent volume (creates them on first run)
- Starts MariaDB — initializes the data directory on first run
- Starts ClickHouse — catalog engine for file-level search (optional, skipped if not available)
-
Creates
.envconfiguration — auto-generated on first run, persisted on the data volume - Imports database schema — on fresh installs only
- Runs migrations — applies any pending database migrations
- Recreates SSH users — restores client SSH users from the database and persistent volume
- Starts SSH server — for agent borg connections
- Starts cron scheduler — runs the backup job queue every minute
- Starts Apache — serves the web UI
SSH host keys are stored on the persistent volume (/var/bbs/.ssh-host-keys/). This ensures agents don't see "host key changed" errors after a container rebuild or update. The keys are created on first run and restored on subsequent starts.
Pull the latest image and recreate the container:
docker compose pull
docker compose up -dThe new image includes the updated application code. On startup, any pending database migrations are applied automatically. Your data is stored on the Docker volume and is preserved across updates.
You can check for available updates from the web UI at Settings > Updates.
To pin to a specific release instead of always getting the latest:
image: marcpope/borgbackupserver:v2.15.0For production deployments with HTTPS, place a reverse proxy in front of the BBS container.
server {
listen 443 ssl;
server_name backups.example.com;
ssl_certificate /etc/letsencrypt/live/backups.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/backups.example.com/privkey.pem;
client_max_body_size 100M;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}backups.example.com {
reverse_proxy localhost:8080
}
Set APP_URL=https://backups.example.com in your docker-compose.yml so agents and the web UI use the correct URL.
The Docker volume contains everything — database, repositories, SSH keys, and configuration. To back up the entire volume:
docker compose stop
docker run --rm -v bbs-data:/data -v $(pwd):/backup alpine tar czf /backup/bbs-backup.tar.gz -C /data .
docker compose startTo restore a volume backup:
docker compose stop
docker run --rm -v bbs-data:/data -v $(pwd):/backup alpine sh -c "rm -rf /data/* && tar xzf /backup/bbs-backup.tar.gz -C /data"
docker compose startIf you have S3 credentials configured with "Sync server backups" enabled, you can restore a fresh Docker container directly from S3 through the web UI:
- Start a fresh container
- Log in and go to Storage > S3 Sync Settings
- Enter the same S3 credentials used by your previous server
- Scroll down to Restore from S3 Backup and click List Available Backups
- Click Restore on the backup you want
- New admin credentials are displayed — use them to log in
This restores the database, configuration, SSH keys, and recreates all Unix users. Repository data can then be restored per-repo using the S3 Restore feature on each client. See Server Backup and Restore for full details.
To build the Docker image locally instead of pulling from Docker Hub:
git clone https://github.com/marcpope/borgbackupserver.git
cd borgbackupserver
docker compose up -d --buildOr uncomment the build line in docker-compose.yml and comment out the image line:
# image: marcpope/borgbackupserver:latest
build: .BBS also runs in rootless Podman. The first-run setup modal detects both Docker and Podman containers.
Create ~/.config/containers/systemd/borgbackupserver.container:
[Unit]
Description=Borg Backup Server Container
After=network-online.target
[Container]
Image=docker.io/marcpope/borgbackupserver:latest
ContainerName=borgbackupserver
PublishPort=2222:22
PublishPort=8080:80
AutoUpdate=registry
Volume=/path/to/storage:/var/bbs:Z
[Service]
Restart=always
[Install]
WantedBy=multi-user.target default.targetThe :Z flag on the volume is required for SELinux (RHEL, Alma, Rocky, Fedora).
Enable auto-start without login:
loginctl enable-linger
systemctl --user daemon-reload
systemctl --user start borgbackupserverThanks to @sainf for testing and sharing the Podman setup.
Check logs:
docker compose logs bbsCommon causes:
- Port conflict — another service is using port 8080 or 2222
- Insufficient disk space for the Docker volume
Verify the SSH port is accessible:
ssh -p 2222 bbs-testclient@your-server-ipCommon causes:
-
SSH_PORTdoesn't match the host-side port mapping - Firewall blocking the SSH port
- Agent configured with wrong server hostname or port
Migrations run automatically on container start. If you encounter errors:
docker compose exec bbs bash
cd /var/www/bbs
for f in migrations/*.sql; do mysql -u bbs -pbbs bbs < "$f" 2>/dev/null; doneIf repositories on NFS storage fail with "Permission denied", check the NFS server's user mapping settings. See Storage Setup#NFS Troubleshooting for detailed solutions.
To completely reset BBS and start over:
docker compose down -v # removes container AND volume
docker compose up -d # fresh startWarning: This deletes all data including backups, configuration, and client registrations.
Next: Getting Started | Linux Agent Setup | Remote Storage | Storage Setup
📖 User Manual
Getting Started
Using BBS
- Dashboard
- Managing Clients
- Linux Agent Setup
- macOS Agent Setup
- Windows Agent Setup
- Repositories
- Storage Setup
- Backup Plans
- Restoring Files
- Database Backups
- Plugins
- Remote Storage
- S3 Offsite Sync
Monitoring
Administration
- Settings
- User Management
- Single Sign-On
- Two-Factor Authentication
- Updating BBS
- Server Backup and Restore
Reference