How to Self-Host Bacula with Docker Compose

What Is Bacula?

Bacula is an enterprise-grade backup framework that manages backup, recovery, and verification of data across a network. It uses a client-server architecture with three core components: the Director (schedules and coordinates), the Storage Daemon (manages backup media), and the File Daemon (runs on each client machine). Bacula is one of the oldest open-source backup systems (first released 2000) and is used in production at organizations managing petabytes of data.

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 2 GB of free RAM (minimum)
  • Sufficient disk space for backup storage
  • Network access to all machines you want to back up
  • A domain name (optional, for remote web UI access)

Docker Compose Configuration

Bacula’s architecture requires multiple communicating services. Create a docker-compose.yml file:

services:
  bacula-director:
    image: bacularis/bacularis-standalone:18.0.4
    container_name: bacula-director
    restart: unless-stopped
    ports:
      - "9097:9097"     # Bacularis web UI
      - "9101:9101"     # Director daemon
    volumes:
      - bacula-config:/opt/bacula/etc          # Bacula configuration
      - bacula-data:/opt/bacula/archive        # Backup storage
      - bacula-catalog:/var/lib/pgsql/data     # PostgreSQL catalog database
      - bacula-logs:/opt/bacula/log            # Bacula logs
    environment:
      DB_INIT: "true"                          # Initialize database on first run
      DB_HOST: "localhost"
      DB_PORT: "5432"
      DB_NAME: "bacula"
      DB_USER: "bacula"
      DB_PASSWORD: "change-this-password"       # CHANGE: strong database password
      BACULARIS_API_USER: "admin"               # CHANGE: web UI username
      BACULARIS_API_PASSWORD: "change-this-too"  # CHANGE: web UI password
    networks:
      - backup

  bacula-storage:
    image: bacularis/bacularis-standalone:18.0.4
    container_name: bacula-storage
    restart: unless-stopped
    ports:
      - "9103:9103"     # Storage daemon
    volumes:
      - bacula-config:/opt/bacula/etc:ro
      - bacula-data:/opt/bacula/archive
    entrypoint: ["/opt/bacula/bin/bacula-sd", "-f", "-c", "/opt/bacula/etc/bacula-sd.conf"]
    networks:
      - backup

networks:
  backup:
    driver: bridge

volumes:
  bacula-config:
  bacula-data:
  bacula-catalog:
  bacula-logs:

Note: The bacularis/bacularis-standalone image bundles the Bacula Director, Storage Daemon, PostgreSQL catalog, and the Bacularis web UI in a single image. For production multi-server deployments, use separate bacula-dir, bacula-sd, and bacula-fd images.

Start the stack:

docker compose up -d

Initial Setup

  1. Open the Bacularis web UI at http://your-server-ip:9097
  2. Log in with the credentials from your environment variables
  3. Navigate to ConfigurationDirector to review default job definitions
  4. The default configuration includes a self-backup job — verify it runs successfully

Add a Client Machine

Each machine you want to back up needs the Bacula File Daemon installed:

On the client (Debian/Ubuntu):

apt install bacula-fd

On the client (RHEL/Rocky):

dnf install bacula-client

Edit the client’s /etc/bacula/bacula-fd.conf:

Director {
  Name = bacula-dir
  Password = "shared-secret-password"   # Must match Director's client config
}

FileDaemon {
  Name = client-hostname-fd
  Maximum Concurrent Jobs = 5
}

Then register the client in the Director’s configuration via the web UI or by editing the Director config.

Configuration

Job Definitions

Bacula uses jobs to define what to back up, when, and where. A minimal job includes:

  • Client: which machine to back up
  • FileSet: which directories to include/exclude
  • Schedule: when to run (full, incremental, differential)
  • Pool: which storage pool to target
  • Level: Full, Incremental, or Differential

Retention Policies

SettingRecommended ValuePurpose
File Retention60 daysHow long individual file records are kept in the catalog
Job Retention180 daysHow long job records are kept
Volume Retention365 daysHow long backup volumes are retained
Maximum Volume Bytes50 GBMaximum size per backup volume file
Maximum Volumes100Maximum volumes per pool

Backup Levels

LevelBacks UpSpeedStorage
FullEverything in the FileSetSlowestMost
DifferentialChanges since last FullMediumMedium
IncrementalChanges since last backup of any levelFastestLeast

A common schedule: Full weekly, Differential daily, Incremental hourly.

Reverse Proxy

To expose the Bacularis web UI securely:

server {
    listen 443 ssl;
    server_name bacula.example.com;

    location / {
        proxy_pass http://localhost:9097;
        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;
    }
}

See Reverse Proxy Setup for full configuration.

Backup

The Bacula catalog database is the most critical piece to protect — without it, you can’t restore from backup volumes. Back up:

  • /opt/bacula/etc/ — configuration files
  • The PostgreSQL catalog database (dump regularly)
  • SSH keys and certificates

Use a separate tool (Restic, BorgBackup) to back up the catalog to a different location than your Bacula storage.

Troubleshooting

”No Prior Full Backup” Error

Symptom: Incremental or Differential jobs fail with “No prior Full backup found”

Fix: Run a Full backup for the client first. Incremental and Differential jobs reference the last Full. Without one, they can’t calculate deltas.

Client Connection Refused

Symptom: Director reports “Connection refused” when contacting a client

Fix: Verify the File Daemon is running on the client:

systemctl status bacula-fd

Check that port 9102 is open on the client’s firewall and the password in the client’s bacula-fd.conf matches the Director’s client resource configuration.

Catalog Database Corruption

Symptom: Director fails to start with database errors

Fix: Check PostgreSQL logs in the container. If the catalog is corrupted, restore from your catalog backup:

docker exec -it bacula-director psql -U bacula -d bacula < catalog-backup.sql

Storage Daemon Full

Symptom: Jobs fail with “No appendable volumes” or “Catalog error”

Fix: Either add more disk space to the storage volume, prune expired volumes, or adjust retention policies to release old backup data:

# From bconsole inside the Director container
docker exec -it bacula-director /opt/bacula/bin/bconsole
*prune expired volumes

Resource Requirements

  • RAM: 512 MB idle, 1-2 GB during active backup jobs
  • CPU: Low to moderate (depends on compression and encryption settings)
  • Disk: Depends on data volume. The catalog database grows with the number of files backed up — budget 1-5 GB for the catalog, plus backup storage.

Verdict

Bacula is the most powerful open-source backup system available, but it’s also the most complex. The three-daemon architecture, job/pool/schedule/FileSet configuration model, and catalog management create a steep learning curve. If you’re backing up 10+ machines in a structured environment and need enterprise features (media management, accurate scheduling, detailed reporting), Bacula is worth the investment.

For home servers or small setups (1-5 machines), BackupPC is simpler for centralized pull-based backups, and Restic or BorgBackup are better for individual machine backups. Bacula’s complexity is justified only when you need its enterprise capabilities.

Comments