Documentation Index Fetch the complete documentation index at: https://mintlify.com/karanhudia/borg-ui/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Running Borg UI behind a reverse proxy enables:
SSL/TLS encryption (HTTPS)
Custom domains (e.g., backups.yourdomain.com)
Subfolder deployment (e.g., yourdomain.com/borg)
Additional security features
Integration with existing infrastructure
Nginx
Root Path Configuration
For serving Borg UI at the root of a domain (backups.yourdomain.com):
server {
listen 80 ;
server_name backups.yourdomain.com;
# Redirect HTTP to HTTPS
return 301 https://$ server_name $ request_uri ;
}
server {
listen 443 ssl http2;
server_name backups.yourdomain.com;
# SSL configuration
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
location / {
proxy_pass http://localhost:8081;
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 ;
# WebSocket support (for real-time updates)
proxy_http_version 1.1 ;
proxy_set_header Upgrade $ http_upgrade ;
proxy_set_header Connection "upgrade" ;
# Timeouts for long-running operations
proxy_connect_timeout 600 ;
proxy_send_timeout 600 ;
proxy_read_timeout 600 ;
send_timeout 600 ;
}
# Increase client max body size for file uploads/restores
client_max_body_size 1G ;
}
Subfolder Configuration
For serving Borg UI in a subfolder (yourdomain.com/borg):
Configure BASE_PATH in Borg UI
Set the BASE_PATH environment variable: Docker: docker run -d \
-e BASE_PATH=/borg \
# ... other options
ainullcode/borg-ui:latest
Docker Compose: services :
app :
environment :
- BASE_PATH=/borg
Unraid:
Add environment variable:Key: BASE_PATH
Value: /borg
Must start with / (e.g., /borg not borg)
No trailing slash (e.g., /borg not /borg/)
Rebuild container after setting: docker-compose up -d --build
Configure Nginx
server {
listen 80 ;
server_name yourdomain.com;
return 301 https://$ server_name $ request_uri ;
}
server {
listen 443 ssl http2;
server_name yourdomain.com;
# SSL configuration
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location /borg/ {
proxy_pass http://localhost:8081/;
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 ;
# WebSocket support
proxy_http_version 1.1 ;
proxy_set_header Upgrade $ http_upgrade ;
proxy_set_header Connection "upgrade" ;
# Timeouts
proxy_connect_timeout 600 ;
proxy_send_timeout 600 ;
proxy_read_timeout 600 ;
send_timeout 600 ;
}
client_max_body_size 1G ;
}
Note the trailing slash in proxy_pass http://localhost:8081/; - this is required for subfolder setups.
Test and reload
nginx -t
systemctl reload nginx
Nginx Proxy Manager
Popular with Unraid users and home lab setups:
Add Proxy Host
Open Nginx Proxy Manager interface
Click Proxy Hosts → Add Proxy Host
Configure Details tab
Domain Names: backups.yourdomain.com
Scheme: http
Forward Hostname/IP: [Borg UI container IP or hostname]
Forward Port: 8081
✓ Block Common Exploits
✓ Websockets Support
Configure SSL tab
SSL Certificate: Request a new SSL Certificate
Force SSL: On
HTTP/2 Support: On
HSTS Enabled: On
Or use existing certificate if available.
Configure Advanced tab
proxy_connect_timeout 600 ;
proxy_send_timeout 600 ;
proxy_read_timeout 600 ;
send_timeout 600 ;
client_max_body_size 1G ;
Save and test
Save the proxy host and access https://backups.yourdomain.com
Subfolder with NPM
For subfolder deployment (yourdomain.com/borg):
Set BASE_PATH=/borg in Borg UI container (see above)
In NPM Proxy Host:
Domain Names: yourdomain.com
Forward Hostname/IP: [Borg UI IP]
Forward Port: 8081
In Advanced tab:
location /borg/ {
proxy_pass http://[BORG_UI_IP]:8081/;
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 ;
proxy_http_version 1.1 ;
proxy_set_header Upgrade $ http_upgrade ;
proxy_set_header Connection "upgrade" ;
proxy_connect_timeout 600 ;
proxy_send_timeout 600 ;
proxy_read_timeout 600 ;
}
Traefik
Root Path Configuration
Using Docker labels for Traefik v2/v3:
services :
app :
image : ainullcode/borg-ui:latest
container_name : borg-web-ui
restart : unless-stopped
networks :
- traefik
- borg_network
labels :
- "traefik.enable=true"
# HTTP router
- "traefik.http.routers.borg-ui.rule=Host(`backups.yourdomain.com`)"
- "traefik.http.routers.borg-ui.entrypoints=web"
- "traefik.http.routers.borg-ui.middlewares=borg-ui-https-redirect"
# HTTPS router
- "traefik.http.routers.borg-ui-secure.rule=Host(`backups.yourdomain.com`)"
- "traefik.http.routers.borg-ui-secure.entrypoints=websecure"
- "traefik.http.routers.borg-ui-secure.tls=true"
- "traefik.http.routers.borg-ui-secure.tls.certresolver=letsencrypt"
# Service
- "traefik.http.services.borg-ui.loadbalancer.server.port=8081"
# Middleware - HTTPS redirect
- "traefik.http.middlewares.borg-ui-https-redirect.redirectscheme.scheme=https"
- "traefik.http.middlewares.borg-ui-https-redirect.redirectscheme.permanent=true"
# Security headers
- "traefik.http.middlewares.borg-ui-headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.borg-ui-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.borg-ui-headers.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.borg-ui-headers.headers.browserXssFilter=true"
- "traefik.http.middlewares.borg-ui-headers.headers.frameDeny=true"
- "traefik.http.routers.borg-ui-secure.middlewares=borg-ui-headers"
# ... volumes, environment, etc.
networks :
traefik :
external : true
borg_network :
driver : bridge
Subfolder Configuration
Set BASE_PATH
environment :
- BASE_PATH=/borg
Configure Traefik labels
labels :
- "traefik.enable=true"
# HTTP router
- "traefik.http.routers.borg-ui.rule=Host(`yourdomain.com`) && PathPrefix(`/borg`)"
- "traefik.http.routers.borg-ui.entrypoints=web"
- "traefik.http.routers.borg-ui.middlewares=borg-ui-https-redirect"
# HTTPS router
- "traefik.http.routers.borg-ui-secure.rule=Host(`yourdomain.com`) && PathPrefix(`/borg`)"
- "traefik.http.routers.borg-ui-secure.entrypoints=websecure"
- "traefik.http.routers.borg-ui-secure.tls=true"
- "traefik.http.routers.borg-ui-secure.tls.certresolver=letsencrypt"
# Strip prefix middleware
- "traefik.http.middlewares.borg-ui-stripprefix.stripprefix.prefixes=/borg"
# Service
- "traefik.http.services.borg-ui.loadbalancer.server.port=8081"
# HTTPS redirect
- "traefik.http.middlewares.borg-ui-https-redirect.redirectscheme.scheme=https"
# Combine middlewares
- "traefik.http.routers.borg-ui-secure.middlewares=borg-ui-stripprefix,borg-ui-headers"
Rebuild container
docker-compose up -d --build
Traefik File Configuration
Alternatively, using traefik.yml or dynamic configuration:
http :
routers :
borg-ui :
rule : "Host(`backups.yourdomain.com`)"
entryPoints :
- websecure
service : borg-ui
tls :
certResolver : letsencrypt
middlewares :
- borg-ui-headers
services :
borg-ui :
loadBalancer :
servers :
- url : "http://localhost:8081"
passHostHeader : true
middlewares :
borg-ui-headers :
headers :
stsSeconds : 31536000
stsIncludeSubdomains : true
contentTypeNosniff : true
browserXssFilter : true
frameDeny : true
Caddy
Root Path Configuration
backups.yourdomain.com {
reverse_proxy localhost:8081 {
# WebSocket support
header_up Upgrade {http.request.header.Upgrade}
header_up Connection {http.request.header.Connection}
# Timeouts for long operations
transport http {
dial_timeout 600s
response_header_timeout 600s
read_timeout 600s
write_timeout 600s
}
}
# Security headers
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
X-XSS-Protection "1; mode=block"
}
# Request size limit
request_body {
max_size 1GB
}
}
Subfolder Configuration
yourdomain.com {
# Set BASE_PATH=/borg in Borg UI first
handle_path /borg/* {
reverse_proxy localhost:8081 {
header_up Upgrade {http.request.header.Upgrade}
header_up Connection {http.request.header.Connection}
transport http {
dial_timeout 600s
response_header_timeout 600s
read_timeout 600s
write_timeout 600s
}
}
}
header /borg/* {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
X-XSS-Protection "1; mode=block"
}
}
Apache
Root Path Configuration
< VirtualHost *:80 >
ServerName backups.yourdomain.com
Redirect permanent / https://backups.yourdomain.com/
</ VirtualHost >
< VirtualHost *:443 >
ServerName backups.yourdomain.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/cert.pem
SSLCertificateKeyFile /etc/ssl/private/key.pem
# Security headers
Header always set Strict-Transport-Security "max-age= 31536000 ; includeSubDomains"
Header always set X-Content-Type- Options "nosniff"
Header always set X-Frame- Options "SAMEORIGIN"
Header always set X-XSS-Protection " 1 ; mode=block"
ProxyPreserveHost On
ProxyPass / http://localhost: 8081 /
ProxyPassReverse / http://localhost: 8081 /
# WebSocket support
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:8081/$1 [P,L]
# Timeouts
ProxyTimeout 600
# Request size limit
LimitRequestBody 1073741824
</ VirtualHost >
Enable required modules:
a2enmod proxy proxy_http proxy_wstunnel rewrite headers ssl
systemctl restart apache2
Subfolder Configuration
< VirtualHost *:443 >
ServerName yourdomain.com
# SSL configuration...
# Set BASE_PATH=/borg in Borg UI first
< Location /borg >
ProxyPreserveHost On
ProxyPass http://localhost: 8081 /
ProxyPassReverse http://localhost: 8081 /
# WebSocket support
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /borg/(.*) ws://localhost:8081/$1 [P,L]
</ Location >
ProxyTimeout 600
</ VirtualHost >
Configuration Summary
BASE_PATH Environment Variable
Deployment Example BASE_PATH Value Root domain backups.domain.comNot needed (or /) Subfolder domain.com/borg/borgSubfolder domain.com/backup/backup
Important rules for BASE_PATH:
Must start with / (e.g., /borg not borg)
No trailing slash (e.g., /borg not /borg/)
Requires container rebuild: docker-compose up -d --build
Common Proxy Settings
Essential headers for all reverse proxy configurations:
Proxy Headers:
Host: $host
X-Real-IP: $remote_addr
X-Forwarded-For: $proxy_add_x_forwarded_for
X-Forwarded-Proto: $scheme
WebSocket Support:
Upgrade: $http_upgrade
Connection: "upgrade"
HTTP Version: 1.1
Timeouts:
Connect: 600s
Send: 600s
Read: 600s
Request Limits:
Max Body Size: 1GB
Testing
Verify Configuration
Test reverse proxy config
Nginx: Apache: Caddy: caddy validate --config /etc/caddy/Caddyfile
Check SSL certificate
curl -I https://backups.yourdomain.com
Verify the certificate is valid and served correctly.
Test WebSocket connection
Open browser developer tools (F12) → Network tab → WS filter. Navigate to Borg UI and start a backup. You should see WebSocket connections for real-time updates.
Test large operations
Perform a backup or restore operation. Verify it doesn’t timeout (600s should be sufficient for most operations).
Troubleshooting
502 Bad Gateway
Cause: Reverse proxy can’t reach Borg UI container.
Solutions:
Verify Borg UI is running:
Check if port 8081 is accessible:
curl http://localhost:8081
Verify network connectivity between proxy and container
Check proxy logs for connection errors
404 Not Found (Subfolder Setup)
Cause: BASE_PATH not configured correctly.
Solutions:
Verify BASE_PATH is set in container:
docker exec borg-web-ui env | grep BASE_PATH
Ensure BASE_PATH starts with / and has no trailing slash
Rebuild container after setting BASE_PATH:
docker-compose up -d --build
Check proxy configuration matches BASE_PATH value
Static Assets Not Loading
Cause: Incorrect proxy_pass configuration or missing BASE_PATH.
Solutions:
For subfolder setups, ensure trailing slash in proxy_pass:
# Correct:
location /borg/ {
proxy_pass http://localhost:8081/;
}
# Incorrect:
location /borg/ {
proxy_pass http://localhost:8081; # Missing trailing slash
}
Verify BASE_PATH is set and container rebuilt
Check browser console for 404 errors on assets
WebSocket Connection Failed
Cause: Missing WebSocket proxy headers.
Solutions:
Add WebSocket headers (see examples above)
Verify HTTP version is 1.1:
Check firewall allows WebSocket connections
Operation Timeouts
Cause: Proxy timeout too short for long-running operations.
Solutions:
Increase proxy timeouts to 600s (see examples above)
Increase Borg operation timeouts in container:
environment :
- BORG_EXTRACT_TIMEOUT=7200
- BORG_INFO_TIMEOUT=7200
Check both proxy and container logs for timeout errors
Security Best Practices
Use Strong SSL/TLS Configuration
Use TLS 1.2 or higher
Disable weak ciphers
Enable HSTS
Use valid SSL certificates (Let’s Encrypt recommended)
Enable HTTP/2 for better performance
Use firewall rules to limit access
Consider IP whitelisting for admin interface
Enable fail2ban for brute force protection
Use strong passwords
Consider adding HTTP basic auth at proxy level
Implement rate limiting at reverse proxy level: Nginx: limit_req_zone $ binary_remote_addr zone=borg_ui:10m rate=10r/s;
location / {
limit_req zone=borg_ui burst=20 nodelay;
# ... proxy settings
}
Traefik: labels :
- "traefik.http.middlewares.borg-ui-ratelimit.ratelimit.average=10"
- "traefik.http.middlewares.borg-ui-ratelimit.ratelimit.burst=20"
Next Steps
Docker Simple deployment with docker run
Docker Compose Recommended production setup