Skip to main content

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.

Security Best Practices

Secure your Borg UI installation with authentication, SSH key management, and encryption.

Authentication Methods

Borg UI supports two authentication modes: JWT-based authentication (default) and Reverse Proxy authentication (for SSO/enterprise setups).

JWT Authentication (Default)

How it works:
  • Users authenticate with username/password
  • JWT tokens are issued with configurable expiration (default: 24 hours)
  • Tokens use HS256 algorithm with secret key encryption
  • Passwords are hashed using bcrypt (salt rounds: 12)
Configuration:
# docker-compose.yml
environment:
  - SECRET_KEY=your-custom-secret-key-here  # Optional: auto-generated if not set
  - ACCESS_TOKEN_EXPIRE_MINUTES=1440        # 24 hours (default)
Implementation details (from app/core/security.py:18-39):
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    """Create a JWT access token"""
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=settings.access_token_expire_minutes)
    
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, settings.secret_key, algorithm=settings.algorithm)
    return encoded_jwt
First User Setup:
  • Default admin account is created automatically on first run
  • Username: admin
  • Password: admin123 (or set via INITIAL_ADMIN_PASSWORD environment variable)
  • Must change password on first login (enforced by must_change_password flag)
The default admin password admin123 is insecure. Change it immediately after first login or set INITIAL_ADMIN_PASSWORD before deployment.

Reverse Proxy Authentication

Use case: Integrate with existing SSO systems (Authelia, Authentik, Keycloak, etc.) Configuration:
# docker-compose.yml
environment:
  - DISABLE_AUTHENTICATION=true
  - PROXY_AUTH_HEADER=X-Forwarded-User  # Header containing authenticated username
How it works (from app/core/security.py:97-169):
  1. Borg UI trusts the X-Forwarded-User header from reverse proxy
  2. Users are auto-created on first access (no manual setup required)
  3. Alternative headers are checked: X-Remote-User, Remote-User, X-authentik-username, X-Forwarded-User
  4. If no header is present, defaults to admin user (for direct access)
Supported headers (checked in order):
  • Configured header (PROXY_AUTH_HEADER)
  • X-Remote-User
  • Remote-User
  • X-authentik-username
  • X-Forwarded-User
Nginx example:
location /borg/ {
    proxy_pass http://localhost:8081/;
    proxy_set_header X-Forwarded-User $remote_user;
    # Or with Authelia/Authentik:
    # auth_request /authelia;
    # proxy_set_header X-Forwarded-User $http_remote_user;
}
Security critical: When using proxy auth, Borg UI must only be accessible through the reverse proxy. Bind to 127.0.0.1 or use firewall rules to prevent direct access.
Example with Authelia:
# docker-compose.yml
services:
  borg-ui:
    environment:
      - DISABLE_AUTHENTICATION=true
      - PROXY_AUTH_HEADER=Remote-User
    # Bind only to localhost
    ports:
      - "127.0.0.1:8081:8081"

Secret Key Management

The SECRET_KEY is used for JWT signing and data encryption (SSH keys, repository passwords). Default behavior (from app/config.py:144-162):
  1. On first run, a cryptographically secure key is auto-generated
  2. Saved to /data/.secret_key with 0600 permissions
  3. Reused across container restarts (persisted in volume)
# Auto-generated key format (32 bytes, URL-safe base64)
openssl rand -base64 32
# Example: xQ7vK2_8Rj9L5mP4nW1qY3tZ6uV8wA0bC1dE2fG3hI4=

Manual Configuration

Option 1: Environment variable
environment:
  - SECRET_KEY=your-custom-secret-key-minimum-32-characters
Option 2: Docker secret
secrets:
  - secret_key

secrets:
  secret_key:
    file: ./secret_key.txt
Secret key length: Minimum 32 characters recommended. The system validates length in production mode (app/config.py:183-187).

Key Rotation

Warning: Rotating the secret key will:
  • Invalidate all existing JWT tokens (users must re-login)
  • Make encrypted SSH keys unreadable (requires re-importing keys)
If you must rotate:
  1. Export all SSH keys before rotation
  2. Update SECRET_KEY environment variable
  3. Restart container
  4. Re-import SSH keys
  5. Notify users to re-login

SSH Key Security

Storage and Encryption

How SSH keys are stored (from documentation analysis):
  • Private keys are encrypted using Fernet symmetric encryption
  • Encryption key derived from SECRET_KEY (first 32 bytes)
  • Stored in SQLite database (/data/borg.db)
  • Decrypted only during backup/mount operations (temporary files)
Encryption implementation (app/core/security.py:283-327):
def encrypt_secret(value: str) -> str:
    """Encrypt using Fernet (AES-128 in CBC mode with HMAC)"""
    encryption_key = settings.secret_key.encode()[:32]
    cipher = Fernet(base64.urlsafe_b64encode(encryption_key))
    encrypted_value = cipher.encrypt(value.encode()).decode()
    return encrypted_value

def decrypt_secret(encrypted_value: str) -> str:
    """Decrypt Fernet-encrypted value"""
    encryption_key = settings.secret_key.encode()[:32]
    cipher = Fernet(base64.urlsafe_b64encode(encryption_key))
    decrypted_value = cipher.decrypt(encrypted_value.encode()).decode()
    return decrypted_value

SSH Key Types

Recommended: ED25519 (modern, smaller, faster)
# Generated via UI: Remote Machines → Generate System Key
# Algorithm: ssh-keygen -t ed25519 -N ""
Alternative: RSA 4096 (maximum compatibility)
# For legacy systems that don't support ED25519
# Algorithm: ssh-keygen -t rsa -b 4096 -N ""

Deployment Security

Best practice: Use UI-based deployment
  1. Go to Remote MachinesDeploy Key to Server
  2. Enter password (used once for deployment)
  3. Password is not stored - only used to install public key
  4. Future connections use passwordless SSH key authentication
Manual deployment with restrictions:
# On remote server: restrict SSH key to borg-only access
vim ~/.ssh/authorized_keys
Add command restriction:
command="borg serve --restrict-to-path /backups/borg-repo",restrict ssh-ed25519 AAAAC3... borg-web-ui
Benefits:
  • Prevents shell access with the backup key
  • Restricts borg operations to specific repository path
  • Mitigates damage if key is compromised

Repository Encryption

Borg repositories should always use encryption to protect backup data.

Encryption Modes

Recommended: repokey-blake2 (fastest, most secure)
# Encryption key stored in repository metadata
# Uses BLAKE2b for authentication (faster than SHA256)
borg init --encryption=repokey-blake2 /path/to/repo
Alternative: keyfile-blake2 (key stored separately)
# Encryption key stored in ~/.config/borg/keys/
# More secure if repository storage is compromised
borg init --encryption=keyfile-blake2 /path/to/repo
For non-sensitive data: none
# No encryption (faster backups, but data readable by anyone)
borg init --encryption=none /path/to/repo
Always use encryption for production data. Unencrypted repositories expose all backup contents to anyone with file access.

Passphrase Management

During repository creation:
  • Borg UI prompts for passphrase
  • Passphrase is encrypted before storage in database
  • Decrypted only during backup/restore operations
Passphrase security:
  • Minimum 20 characters recommended
  • Use password manager to generate strong passphrases
  • Store passphrase separately from repository backups
Keyfile storage: For keyfile mode, export and backup the key:
docker exec borg-web-ui borg key export /path/to/repo /data/backup-keyfile.txt
Store this file separately from your backups (e.g., password manager).

Network Security

Binding and Firewall

Internal network only:
# docker-compose.yml
ports:
  - "127.0.0.1:8081:8081"  # Localhost only
Access via reverse proxy:
location /borg/ {
    proxy_pass http://127.0.0.1:8081/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}
Firewall rules (if exposing directly):
# UFW (Ubuntu/Debian)
sudo ufw allow from 192.168.1.0/24 to any port 8081

# iptables
sudo iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 8081 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 8081 -j DROP

HTTPS/TLS

Use reverse proxy for TLS termination:
server {
    listen 443 ssl http2;
    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;
    
    # Modern TLS configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    
    location / {
        proxy_pass http://127.0.0.1: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;
    }
}
Borg UI does not include built-in TLS. Always use a reverse proxy (Nginx, Caddy, Traefik) for HTTPS.

User Management

Creating Users

Via Web Interface:
  1. Login as admin
  2. Go to SettingsUsers
  3. Click Add User
  4. Set username, password, email
  5. Assign admin privileges if needed
Password requirements:
  • Minimum 8 characters (enforced client-side)
  • Passwords are bcrypt-hashed before storage
  • Salt rounds: 12 (from app/core/security.py:21-27)

Admin Privileges

Admin users can:
  • Create/delete users
  • Modify system settings
  • Access all repositories
  • View system logs
  • Manage SSH connections
Regular users can:
  • Create their own repositories
  • Run backups on owned repositories
  • Browse and restore from owned repositories
  • Manage their own schedules
Admin accounts have full system access. Limit admin privileges to trusted users only.

User Deactivation

Disable user instead of deleting:
  1. Settings → Users → Edit User
  2. Uncheck “Active”
  3. Save
Effect:
  • User cannot login
  • Existing sessions are invalidated
  • User data (repositories, schedules) is preserved
  • Can be reactivated later

Security Hardening

Docker Security

Run as non-root user:
# docker-compose.yml
environment:
  - PUID=1000  # Your user ID
  - PGID=1000  # Your group ID
Read-only root filesystem (advanced):
services:
  borg-ui:
    read_only: true
    tmpfs:
      - /tmp
      - /var/tmp
    volumes:
      - ./data:/data:rw  # Only /data is writable
Drop unnecessary capabilities:
cap_drop:
  - ALL
cap_add:
  - CHOWN
  - DAC_OVERRIDE
  - FOWNER
  - SETGID
  - SETUID

File Permissions

Secure data directory:
# On host system
chmod 700 /path/to/data
chown 1000:1000 /path/to/data  # Match PUID/PGID
SSH key permissions:
# Inside container (automatic on startup)
chmod 700 /home/borg/.ssh
chmod 600 /home/borg/.ssh/id_ed25519
chmod 644 /home/borg/.ssh/id_ed25519.pub

Database Security

Backup database regularly:
# Backup SQLite database
docker exec borg-web-ui sqlite3 /data/borg.db ".backup /data/borg-backup.db"

# Copy to host
docker cp borg-web-ui:/data/borg-backup.db ./borg-db-$(date +%Y%m%d).db
Database contains encrypted SSH keys and repository passphrases. Store backups securely and encrypt them.

Audit and Logging

Security Event Logging

Logged security events:
  • User authentication attempts (success/failure)
  • User creation/deletion
  • SSH key generation/deployment
  • Repository access
  • System setting changes
View logs:
# Real-time logs
docker logs -f borg-web-ui

# Filter security events
docker logs borg-web-ui | grep -i "auth\|login\|user"

# Persistent logs
cat /path/to/data/logs/borg-ui.log
Log level configuration:
environment:
  - LOG_LEVEL=INFO  # DEBUG, INFO, WARNING, ERROR, CRITICAL

Failed Login Monitoring

Watch for authentication failures:
docker logs borg-web-ui | grep "Could not validate credentials"
Consider implementing fail2ban for automated IP blocking after multiple failures.

Security Checklist

Authentication:
  • Changed default admin password
  • Created individual user accounts (no shared credentials)
  • Disabled unused admin accounts
  • Configured SECRET_KEY (auto-generated or custom)
SSH Keys:
  • Generated ED25519 system key
  • Deployed keys with borg serve --restrict-to-path restrictions
  • Verified SSH key permissions (600 for private, 644 for public)
  • Documented key locations and backup procedures
Repositories:
  • Enabled encryption (repokey-blake2 or keyfile-blake2)
  • Set strong passphrases (20+ characters)
  • Backed up keyfiles separately
  • Tested repository recovery procedure
Network:
  • Configured HTTPS via reverse proxy
  • Bound to localhost or restricted network
  • Configured firewall rules
  • Tested access from expected networks only
System:
  • Running as non-root user (PUID/PGID configured)
  • Data directory permissions secured (700)
  • Database backups scheduled
  • Log monitoring configured
  • Updated to latest version

SSH Keys Guide

Detailed SSH key setup and management

Configuration

Environment variables and system settings

Troubleshooting

Common security-related issues and solutions

Maintenance

Database backups and system maintenance

Build docs developers (and LLMs) love