Installation
Borg UI is distributed as a Docker image.
Use Docker Compose unless you only need a quick local test.
Pick one Compose workflow:
- No Redis: simplest setup. Uses in-memory archive cache.
- With Redis: recommended for normal installs.
- External Redis: use Redis from another host, stack, or managed service.
For Portainer or Unraid, use the same settings and see the platform notes below.
Option 1: No Redis (Simple)
Use this for small installs or occasional archive browsing. Backups and restores work normally. Archive browsing cache is kept in memory and is lost when the app restarts.
Create docker-compose.yml:
services:
app:
image: ainullcode/borg-ui:latest
container_name: borg-web-ui
restart: unless-stopped
ports:
- "${PORT:-8081}:${PORT:-8081}"
volumes:
- borg_data:/data
- borg_cache:/home/borg/.cache/borg
- /etc/localtime:/etc/localtime:ro
- ${LOCAL_STORAGE_PATH:-/home/youruser}:/local:rw
environment:
- PORT=${PORT:-8081}
- PUID=${PUID:-1001}
- PGID=${PGID:-1001}
- TZ=${TZ:-UTC}
- LOCAL_MOUNT_POINTS=${LOCAL_MOUNT_POINTS:-/local}
- REDIS_HOST=disabled
volumes:
borg_data:
borg_cache:Option 2: With Redis (Recommended)
Use this for normal deployments. Redis makes repeated archive browsing faster and keeps cache across app container restarts.
Create docker-compose.yml:
services:
app:
image: ainullcode/borg-ui:latest
container_name: borg-web-ui
restart: unless-stopped
ports:
- "${PORT:-8081}:${PORT:-8081}"
volumes:
- borg_data:/data
- borg_cache:/home/borg/.cache/borg
- /etc/localtime:/etc/localtime:ro
- ${LOCAL_STORAGE_PATH:-/home/youruser}:/local:rw
environment:
- PORT=${PORT:-8081}
- PUID=${PUID:-1001}
- PGID=${PGID:-1001}
- TZ=${TZ:-UTC}
- LOCAL_MOUNT_POINTS=${LOCAL_MOUNT_POINTS:-/local}
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_DB=0
depends_on:
redis:
condition: service_healthy
redis:
image: redis:7-alpine
container_name: borg-redis
restart: unless-stopped
command: >
redis-server
--maxmemory 2gb
--maxmemory-policy allkeys-lru
--save ""
--appendonly no
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
volumes:
borg_data:
borg_cache:Option 3: External Redis
Use this when Redis already runs somewhere else.
Create docker-compose.yml on the Borg UI host:
services:
app:
image: ainullcode/borg-ui:latest
container_name: borg-web-ui
restart: unless-stopped
ports:
- "${PORT:-8081}:${PORT:-8081}"
volumes:
- borg_data:/data
- borg_cache:/home/borg/.cache/borg
- /etc/localtime:/etc/localtime:ro
- ${LOCAL_STORAGE_PATH:-/home/youruser}:/local:rw
environment:
- PORT=${PORT:-8081}
- PUID=${PUID:-1001}
- PGID=${PGID:-1001}
- TZ=${TZ:-UTC}
- LOCAL_MOUNT_POINTS=${LOCAL_MOUNT_POINTS:-/local}
- REDIS_URL=redis://redis.example.com:6379/0
- REDIS_HOST=disabled
volumes:
borg_data:
borg_cache:Replace redis.example.com with your Redis host.
Examples:
redis://redis.example.com:6379/0
redis://:password@redis.example.com:6379/0
rediss://:password@redis.example.com:6379/0
unix:///run/redis/redis.sock?db=0
unix:///run/redis/redis.sock?db=0&password=passwordREDIS_URL takes precedence over REDIS_HOST. Keeping REDIS_HOST=disabled prevents Borg UI from trying localhost:6379 if the external URL is unavailable.
For unix:// Redis URLs, mount the Redis socket into the Borg UI container at the same path used in REDIS_URL.
If you need to create the external Redis instance too, run Redis on that host with a small Compose file:
services:
redis:
image: redis:7-alpine
container_name: borg-redis
restart: unless-stopped
ports:
- "6379:6379"
command: >
redis-server
--maxmemory 2gb
--maxmemory-policy allkeys-lru
--save ""
--appendonly no
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3Only expose Redis to trusted networks. If Redis is reachable across a network, use firewall rules, a private network, or Redis authentication.
Start Borg UI
From the directory containing docker-compose.yml:
docker compose up -dOpen:
http://localhost:8081Default login:
username: admin
password: admin123Change the password immediately after first login. You can set a different first password with INITIAL_ADMIN_PASSWORD.
Portainer
In Portainer, create a Stack and paste one of the Compose files above.
Use real host paths for volumes. Paths are evaluated on the Docker host, not on your laptop or browser session.
Recommended checks before deploying:
- keep
/datapersistent withborg_dataor a host bind mount - set
PUIDandPGIDfor the host user that should own restored files - set
LOCAL_STORAGE_PATHto the host path that contains repositories or backup sources - keep
LOCAL_MOUNT_POINTS=/localunless you change the container mount path - use the Redis Compose option, external Redis, or
REDIS_HOST=disabled
If you mount the Docker socket for hooks, remember that the container may still need the Docker CLI installed. See Docker Hooks.
Unraid
On Unraid, use either the Docker Compose Manager plugin or the Docker web UI.
Recommended defaults:
PUID=99
PGID=100
TZ=<your timezone>Common path mapping:
/mnt/user/appdata/borg-ui -> /data
/mnt/user/appdata/borg-ui/cache -> /home/borg/.cache/borg
/mnt/user/backups -> /localThen use /local/... paths inside Borg UI.
Set LOCAL_MOUNT_POINTS=/local unless you use a different container path.
For Redis, use the Compose Redis option, an existing Redis container, an external Redis URL, or REDIS_HOST=disabled.
If you use the Docker web UI instead of Compose, add the same container paths and environment variables manually. Make sure /data points to persistent appdata storage.
Pick the Right Host Path
The LOCAL_STORAGE_PATH host path is mounted into the container at /local.
Example:
volumes:
- /mnt/usb-drive:/local:rwThen this host path:
/mnt/usb-drive/borg-backups/laptopis this container path in Borg UI:
/local/borg-backups/laptopDo not add the host prefix again inside the UI.
Permissions
Set PUID and PGID to the host user that should own restored files and write backup repositories.
Find them with:
id -u
id -gExample .env:
PORT=8081
PUID=1000
PGID=1000
LOCAL_STORAGE_PATH=/mnt/usb-drive
TZ=America/ChicagoRedis
Redis is used as an archive-browsing cache. It is not required for backups or restores.
The Compose example uses Redis without disk persistence:
--save ""
--appendonly noThat means cached archive listings survive app container restarts while Redis keeps running, but they do not survive a Redis container restart. This is fine because Borg UI can rebuild the cache.
Set REDIS_HOST=disabled when you intentionally run without Redis. Otherwise the app tries the configured Redis host first and falls back to in-memory cache if it cannot connect.
Optional Docker Socket
Only mount the Docker socket if you use script hooks to stop or start containers during backups:
volumes:
- /var/run/docker.sock:/var/run/docker.sock:rwThis grants powerful host access to the Borg UI container. See Docker Hooks.
Optional FUSE Access
Archive mounting uses borg mount and requires FUSE access.
Remote source backups and SSH restore destinations use SSHFS and also require FUSE access.
cap_add:
- SYS_ADMIN
devices:
- /dev/fuse:/dev/fuse
security_opt:
- apparmor:unconfined
environment:
- BORG_FUSE_IMPL=pyfuse3Add this only if you need archive mounts, SSHFS remote source backups, or SSH restore destinations. See Mounting Archives.
Docker Run
For a quick test:
docker run -d \
--name borg-web-ui \
-p 8081:8081 \
-e REDIS_HOST=disabled \
-v borg_data:/data \
-v borg_cache:/home/borg/.cache/borg \
-v /home/youruser:/local:rw \
ainullcode/borg-ui:latestThis does not include Redis. Use Compose for normal deployments.
Upgrade
docker compose pull
docker compose up -dDo not delete borg_data or borg_cache unless you intentionally want to remove application state or Borg cache data.

