Tools: Bacula

Tools: Bacula

What Is Bacula?

Prerequisites

Docker Compose Configuration

Initial Setup

Add a Client Machine

Configuration

Job Definitions

Retention Policies

Backup Levels

Reverse Proxy

Backup

Troubleshooting

"No Prior Full Backup" Error

Client Connection Refused

Catalog Database Corruption

Storage Daemon Full

Verdict

Frequently Asked Questions

Is Bacula overkill for a homelab?

What's the difference between Bacula and Bareos?

Can Bacula back up to cloud storage?

How large does the Bacula catalog database get?

Does Bacula support incremental and differential backups?

Can Bacula back up databases directly?

Related 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. Bacula's architecture requires multiple communicating services. Create a docker-compose.yml file: 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. Each machine you want to back up needs the Bacula File Daemon installed: On the client (Debian/Ubuntu): On the client (RHEL/Rocky): Edit the client's /etc/bacula/bacula-fd.conf: Then register the client in the Director's configuration via the web UI or by editing the Director config. Bacula uses jobs to define what to back up, when, and where. A minimal job includes: A common schedule: Full weekly, Differential daily, Incremental hourly. To expose the Bacularis web UI securely: See Reverse Proxy Setup for full configuration. The Bacula catalog database is the most critical piece to protect — without it, you can't restore from backup volumes. Back up: Use a separate tool (Restic, BorgBackup) to back up the catalog to a different location than your Bacula storage. 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. Symptom: Director reports "Connection refused" when contacting a client Fix: Verify the File Daemon is running on the client: 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. 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: 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: 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. Usually, yes. Bacula's three-daemon architecture (Director, Storage, File) and its job/pool/schedule configuration model are designed for enterprise environments with dozens to hundreds of machines. For a homelab with 1-5 machines, Restic or BorgBackup are far simpler. Use Bacula only if you need enterprise features like tape drive support, accurate scheduling, or detailed reporting across many machines. Bareos (Backup Archiving Recovery Open Sourced) is a fork of Bacula that diverged in 2010. Bareos adds features like native NDMP support, a modern web UI, and improved plugin infrastructure. Both share the same three-daemon architecture and are largely config-compatible. Bareos tends to be more actively developed for community features, while Bacula's enterprise edition has commercial backing. The community edition supports local disk and tape storage natively. Cloud storage (S3, Azure Blob, GCS) requires the commercial Bacula Enterprise edition or a workaround: back up to local disk first, then sync to cloud using Restic or rclone. The community edition focuses on local and LAN-based backup targets. The catalog (PostgreSQL or MySQL) stores metadata for every file in every backup — file paths, sizes, checksums, and attributes. For a typical server with 500,000 files backed up daily with 30-day retention, expect the catalog to reach 1-5 GB. Prune old records regularly with Bacula's built-in retention policies to keep it manageable. Yes. Bacula supports full, incremental, and differential backup levels with accurate scheduling. Accurate mode compares each file's metadata against the catalog to determine what changed — more precise than timestamp-based detection. This prevents missed files when clocks are skewed or files are restored from other backups. Yes, through client-side scripts. Configure a pre-backup script (ClientRunBeforeJob) that runs pg_dump or mysqldump before the file backup starts. The dump file gets included in the regular backup job. For application-consistent backups of running databases, this is the recommended approach. Bacula Enterprise adds native database plugins, but the community edition relies on scripting. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ services: bacula-director: image: bacularis/bacularis-standalone:6.0.0-trixie container_name: bacula-director -weight: 500;">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:6.0.0-trixie container_name: bacula-storage -weight: 500;">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: services: bacula-director: image: bacularis/bacularis-standalone:6.0.0-trixie container_name: bacula-director -weight: 500;">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:6.0.0-trixie container_name: bacula-storage -weight: 500;">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: services: bacula-director: image: bacularis/bacularis-standalone:6.0.0-trixie container_name: bacula-director -weight: 500;">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:6.0.0-trixie container_name: bacula-storage -weight: 500;">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: -weight: 500;">docker compose up -d -weight: 500;">docker compose up -d -weight: 500;">docker compose up -d -weight: 500;">apt -weight: 500;">install bacula-fd -weight: 500;">apt -weight: 500;">install bacula-fd -weight: 500;">apt -weight: 500;">install bacula-fd -weight: 500;">dnf -weight: 500;">install bacula-client -weight: 500;">dnf -weight: 500;">install bacula-client -weight: 500;">dnf -weight: 500;">install bacula-client Director { Name = bacula-dir Password = "shared-secret-password" # Must match Director's client config } FileDaemon { Name = client-hostname-fd Maximum Concurrent Jobs = 5 } Director { Name = bacula-dir Password = "shared-secret-password" # Must match Director's client config } FileDaemon { Name = client-hostname-fd Maximum Concurrent Jobs = 5 } Director { Name = bacula-dir Password = "shared-secret-password" # Must match Director's client config } FileDaemon { Name = client-hostname-fd Maximum Concurrent Jobs = 5 } 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; } } 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; } } 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; } } -weight: 500;">systemctl -weight: 500;">status bacula-fd -weight: 500;">systemctl -weight: 500;">status bacula-fd -weight: 500;">systemctl -weight: 500;">status bacula-fd -weight: 500;">docker exec -it bacula-director psql -U bacula -d bacula < catalog-backup.sql -weight: 500;">docker exec -it bacula-director psql -U bacula -d bacula < catalog-backup.sql -weight: 500;">docker exec -it bacula-director psql -U bacula -d bacula < catalog-backup.sql # From bconsole inside the Director container -weight: 500;">docker exec -it bacula-director /opt/bacula/bin/bconsole *prune expired volumes # From bconsole inside the Director container -weight: 500;">docker exec -it bacula-director /opt/bacula/bin/bconsole *prune expired volumes # From bconsole inside the Director container -weight: 500;">docker exec -it bacula-director /opt/bacula/bin/bconsole *prune expired volumes - 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) - Open the Bacularis web UI at http://your-server-ip:9097 - Log in with the credentials from your environment variables - Navigate to Configuration → Director to review default job definitions - The default configuration includes a self-backup job — verify it runs successfully - 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 - /opt/bacula/etc/ — configuration files - The PostgreSQL catalog database (dump regularly) - SSH keys and certificates - 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. - Best Self-Hosted Backup Solutions - How to Self-Host BackupPC - How to Self-Host Restic - How to Self-Host BorgBackup - UrBackup vs BackupPC - Restic vs BorgBackup - Replace Backblaze - Backup Strategy - Docker Compose Basics