Contact us
Servers 9 min read · 12/2025

Bacula: Backing Up Linux Servers

A complete guide to deploying the Bacula backup system for automated Linux server backups, with support for Virtual Full backups and efficient disk space management.

Bacula is an open-source, enterprise-grade backup system for managing backup, recovery and verification of data across a network. It is modular and consists of several components:

  • Director - the central control component that coordinates all backup operations
  • Storage Daemon - manages the backup storage (disks, tapes)
  • File Daemon - runs on the clients being backed up and sends the data
  • Console - the management and monitoring interface
  • Catalog - a database (MySQL/PostgreSQL) that stores backup metadata

Installation on Debian/Ubuntu

Installing the Director and Storage Daemon (backup server)

bash
apt update
apt install bacula-director-mysql bacula-sd bacula-console

During installation you will be asked about the database configuration. Choose automatic configuration with dbconfig-common.

Installing the File Daemon (backed-up client)

bash
apt install bacula-fd

Verifying the installation

bash
# Check the configuration syntax
bacula-dir -t -c /etc/bacula/bacula-dir.conf

# Start the services
systemctl enable --now bacula-director bacula-sd bacula-fd

Sample configuration

The following configuration backs up a single server with these properties:

  • Daily incremental backups at 01:00
  • Virtual Full backups twice a week (Wednesday and Sunday) at 02:00
  • Automatic freeing of disk space
  • 7-day data retention on disk
  • 14-day metadata retention in the catalog

File Daemon configuration (client)

This configuration runs on every server being backed up and is stored in /etc/bacula/bacula-fd.conf.

bash
#
# Bacula File Daemon Configuration
#

FileDaemon {
  Name = server-fd
  FDport = 9102
  WorkingDirectory = /var/lib/bacula
  Pid Directory = /run/bacula
  Maximum Concurrent Jobs = 10
  # Address the FD listens on (0.0.0.0 = all)
  FDAddress = 0.0.0.0
}

# Connection from the Director
Director {
  Name = backup-dir
  Password = "heslo-pro-file-daemon"
}

# Local monitoring (optional)
Director {
  Name = backup-mon
  Password = "heslo-pro-monitoring"
  Monitor = yes
}

Messages {
  Name = Standard
  director = backup-dir = all, !skipped, !restored
}

Storage Daemon configuration

This configuration runs on the backup server where the data is stored, and it lives in /etc/bacula/bacula-sd.conf.

bash
#
# Bacula Storage Daemon Configuration
#

Storage {
  Name = backup-sd
  SDPort = 9103
  WorkingDirectory = /var/lib/bacula
  Pid Directory = /run/bacula
  Maximum Concurrent Jobs = 20
  SDAddress = 0.0.0.0
}

# Connection from the Director
Director {
  Name = backup-dir
  Password = "heslo-pro-storage-daemon"
}

# Local monitoring (optional)
Director {
  Name = backup-mon
  Password = "heslo-pro-monitoring"
  Monitor = yes
}

# Autochanger - a virtual "tape changer" for disk storage
Autochanger {
  Name = FileChgr1
  Device = FileChgr1-Dev1
  Device = FileChgr1-Dev2
  Device = FileChgr1-Dev3
  Changer Command = ""
  Changer Device = /dev/null
}

# Device for storing backups
Device {
  Name = FileChgr1-Dev1
  Media Type = File
  Archive Device = /home/backupdb/bacula/backup
  LabelMedia = yes
  Random Access = yes
  AutomaticMount = yes
  RemovableMedia = no
  AlwaysOpen = no
  Maximum Concurrent Jobs = 5
}

Device {
  Name = FileChgr1-Dev2
  Media Type = File
  Archive Device = /home/backupdb/bacula/backup
  LabelMedia = yes
  Random Access = yes
  AutomaticMount = yes
  RemovableMedia = no
  AlwaysOpen = no
  Maximum Concurrent Jobs = 5
}

Device {
  Name = FileChgr1-Dev3
  Media Type = File
  Archive Device = /home/backupdb/bacula/backup
  LabelMedia = yes
  Random Access = yes
  AutomaticMount = yes
  RemovableMedia = no
  AlwaysOpen = no
  Maximum Concurrent Jobs = 5
}

Messages {
  Name = Standard
  director = backup-dir = all
}

Why 3 devices (Device)? An autochanger with multiple devices allows parallel operations. Each device can serve one volume at a time, which is essential for:

  1. Virtual Full backups - during consolidation Bacula reads from the source volumes (Dev2) and writes to the target volume (Dev1) at the same time. With a single device this would not be possible.
  2. Backing up multiple clients in parallel - if you back up several servers at once, each one can write to its own volume through a separate device.
  3. Restoring during a backup - you can restore data (read) while another client is being backed up (write).

All devices point to the same directory (Archive Device), but Bacula uses them as independent "drives" for accessing different volumes. For a typical deployment we recommend 2-3 devices; larger infrastructures with many clients can use more.

Note: The directory /home/backupdb/bacula/backup must exist and be writable by the bacula user:

Main configuration file

It is stored in /etc/bacula/bacula-dir.conf.

bash
Director {
  Name = backup-dir
  QueryFile = "/etc/bacula/scripts/query.sql"
  WorkingDirectory = "/var/lib/bacula"
  PidDirectory = "/run/bacula"
  Maximum Concurrent Jobs = 10
  Password = "silne-heslo-pro-console"
  Messages = Daemon
}

# Defaults for all backup jobs
JobDefs {
  Name = "DefaultJob"
  Type = Backup
  Level = Incremental
  Schedule = "WeeklyCycle"
  Messages = Standard
  SpoolAttributes = yes
  Priority = 10
  Write Bootstrap = "/var/lib/bacula/%c.bsr"
  # Automatic upgrade to VirtualFull after 30 days without a Full
  Max Virtual Full Interval = 30 days
  Allow Mixed Priority = yes
  Cancel Lower Level Duplicates = yes
  Cancel Queued Duplicates = yes
}

# Backup job for the server
Job {
  Name = "BackupServer"
  JobDefs = "DefaultJob"
  FileSet = "ServerFileSet"
  Pool = ServerPool
  Client = "server-fd"
  Storage = BackupStorage
  # Keep only the latest unconsolidated incremental
  Backups To Keep = 1
  Delete Consolidated Jobs = Yes
  Accurate = Yes
  # Maximum wait for a job to start
  Max Start Delay = 10h
  # Retry on error
  Reschedule On Error = yes
  Reschedule Times = 3
  Reschedule Interval = 30 min
}

# Restore job
Job {
  Name = "RestoreFiles"
  Type = Restore
  Client = server-fd
  Storage = BackupStorage
  FileSet = "ServerFileSet"
  Pool = ServerPool
  Messages = Standard
  Where = /tmp/bacula-restore
}

# Definition of the files to back up
FileSet {
  Name = "ServerFileSet"
  Include {
    Options {
      signature = MD5
      compression = GZIP
    }
    # Exclude unnecessary directories
    Options {
      exclude = yes
      wild = "*/logs/*"
      wild = "*/var/log/*"
      wild = "*/var/cache/*"
      wild = "*/tmp/*"
      wild = "*/node_modules/*"
      wild = "*/.cache/*"
      wild = "*/media/cache/*"
    }
    File = /root
    File = /home
    File = /etc
  }
}

# Backup schedule
Schedule {
  Name = "WeeklyCycle"
  # Daily incremental backups
  Run = Level=Incremental Priority=10 mon-sun at 01:00
  # Virtual Full twice a week (Wednesday and Sunday)
  Run = Level=VirtualFull Priority=5 wed,sun at 02:00
}

# Client
Client {
  Name = server-fd
  Address = server.example.com
  FDPort = 9102
  Catalog = MyCatalog
  Password = "heslo-pro-file-daemon"
  File Retention = 14 days
  Job Retention = 14 days
  AutoPrune = yes
  Maximum Concurrent Jobs = 10
}

# Storage with an Autochanger
Autochanger {
  Name = BackupStorage
  Address = backup.example.com
  SDPort = 9103
  Password = "heslo-pro-storage-daemon"
  Device = FileChgr1
  Media Type = File
  Maximum Concurrent Jobs = 10
  Autochanger = BackupStorage
}

# Catalog (database)
Catalog {
  Name = MyCatalog
  dbname = "bacula"
  DB Address = "127.0.0.1"
  DB Port = "3306"
  dbuser = "bacula"
  dbpassword = "heslo-databaze"
}

# Pool for regular backups
Pool {
  Name = ServerPool
  Pool Type = Backup
  Recycle = yes
  AutoPrune = yes
  # Data retention on disk
  Volume Retention = 7 days
  # Free up space automatically after purge
  Action On Purge = Truncate
  Recycle Oldest Volume = yes
  Maximum Volume Bytes = 20G
  Maximum Volumes = 50
  Label Format = "ServerVol-"
  # Target pool for Virtual Full
  Next Pool = VFServerPool
}

# Pool for Virtual Full backups
Pool {
  Name = VFServerPool
  Pool Type = Backup
  Recycle = yes
  AutoPrune = yes
  Volume Retention = 7 days
  Maximum Volume Bytes = 20G
  Maximum Volumes = 70
  Action On Purge = Truncate
  Recycle Oldest Volume = yes
  Label Format = "VFServerVol-"
  Storage = BackupStorage
}

# Messages
Messages {
  Name = Standard
  mailcommand = "/usr/sbin/bsmtp -h localhost -f \"Bacula\" -s \"Bacula: %t %e of %c %l\" %r"
  mail = admin@example.com = all, !skipped
  console = all, !skipped, !saved
  append = "/var/log/bacula/bacula.log" = all, !skipped
  catalog = all
}

Messages {
  Name = Daemon
  mailcommand = "/usr/sbin/bsmtp -h localhost -f \"Bacula\" -s \"Bacula daemon message\" %r"
  mail = admin@example.com = all, !skipped
  console = all, !skipped, !saved
  append = "/var/log/bacula/bacula.log" = all, !skipped
}

# Maintenance job for automatic cleanup
Schedule {
  Name = "MaintenanceNightly"
  Run = mon-sun at 09:00
}

Job {
  Name = "MaintenancePrune"
  Type = Admin
  Client = server-fd
  FileSet = "ServerFileSet"
  Schedule = "MaintenanceNightly"
  Priority = 50
  Messages = Standard
  Pool = ServerPool
  Storage = BackupStorage

  # Prune expired jobs
  RunScript {
    Console = "prune jobs client=server-fd yes"
    RunsOnClient = no
    RunsWhen = Before
  }

  # Prune expired volumes
  RunScript {
    Console = "prune expired volume pool=ServerPool yes"
    RunsOnClient = no
    RunsWhen = Before
  }
  RunScript {
    Console = "prune expired volume pool=VFServerPool yes"
    RunsOnClient = no
    RunsWhen = Before
  }

  # Truncate - free up disk space
  RunScript {
    Console = "truncate volume pool=ServerPool allpools storage=BackupStorage yes"
    RunsOnClient = no
    RunsWhen = Before
  }
  RunScript {
    Console = "truncate volume pool=VFServerPool allpools storage=BackupStorage yes"
    RunsOnClient = no
    RunsWhen = Before
  }
}

Console {
  Name = backup-mon
  Password = "heslo-pro-monitoring"
  CommandACL = status, .status
}

Key parameters explained

Data retention

  • Volume Retention - how long to keep data on disk. We recommend at least the duration of the longest backup plus a margin.
  • Job Retention - how long to keep job records in the DB. The recommendation is ≥ Volume Retention.
  • File Retention - how long to keep file lists in the DB. The recommendation is ≤ Job Retention.

Why keep File/Job Retention longer than Volume Retention?

File Retention and Job Retention only affect the metadata in the database (catalog), not the backup files on disk themselves. Longer metadata retention brings several benefits:

1. Protection against losing the baseline for incremental backups

Bacula needs a record of the last Full or Virtual Full backup in the catalog to run an Incremental correctly. If Job Retention expires and the Full backup record disappears, Bacula automatically upgrades the next Incremental to a Full - which means transferring all the data instead of just the changes.

An example of the problem with short retention:

  • Monday: Full backup (1 TB, takes 4 days)
  • Friday: Full completed, Job Retention = 4 days
  • Tuesday next week: the Full backup record expires
  • Wednesday: the scheduled Incremental is upgraded to a Full (another 4 days, 1 TB)

2. Faster and more accurate file restores

File Retention determines how long the database keeps a list of all backed-up files with their paths, sizes and checksums. With longer retention:

  • You can search for specific files across the backup history
  • The restore wizard offers a complete list of files to restore
  • You don't have to browse volumes manually and guess what they contain

Without metadata in the catalog, a restore is still possible, but you need to know the exact JobId or browse the volumes manually with the bls utility.

3. Better overview and debugging

Longer Job Retention lets you:

  • Analyze backup size trends over time
  • Identify problematic jobs and failure patterns
  • Plan storage capacity based on historical data

Impact on disk space

  • Volume Retention - directly affects the disk, as it determines how long backup files are kept. It has no effect on the database.
  • Job Retention - no effect on the disk. It takes up minimal space in the database (a few KB per job).
  • File Retention - no effect on the disk. It has a significant impact on the database, because it stores the complete list of all backed-up files with their metadata.

File Retention and database size

For a server with 4 million files, File Retention can amount to:

  • 4 days of retention: ~200 MB in the database
  • 14 days of retention: ~700 MB in the database
  • 30 days of retention: ~1.5 GB in the database

This is negligible compared to the size of the backups themselves (hundreds of GB to TB), but it can affect database query performance. For very large installations, consider tuning MySQL/PostgreSQL.

Recommended settings

bash
# In the client configuration
File Retention = 14 days   # Lets you find files up to 2 weeks back
Job Retention  = 14 days   # Protects the baseline for incrementals
Volume Retention = 7 days  # Actual data on disk

These settings ensure that:

  • Backup files are recycled after 7 days (saving disk space)
  • Metadata is kept for 14 days (protection against an upgrade to Full)
  • You have a two-week history for file searches

Virtual Full backups

A Virtual Full is a synthesis of the previous Full backup and all subsequent Incrementals into a new "virtual" Full backup. Benefits:

  • No data transfer from the client
  • Faster restores (no need to apply increments)
  • More efficient use of storage

Parameters:

  • Backups To Keep = 1 - deletes old increments after consolidation
  • Delete Consolidated Jobs = Yes - deletes the records of consolidated jobs
  • Next Pool - the target pool for Virtual Full backups

Maintenance job

The MaintenanceNightly maintenance job performs automatic cleanup - it prunes expired records from the catalog and frees disk space by truncating empty volumes.

When to run maintenance: The maintenance job should run only after all backup jobs have finished. If it ran before or during the backups, it could delete metadata needed for Virtual Full consolidation or release volumes that are still in use. For nightly backups starting at 01:00-02:00, we recommend running maintenance in the morning, e.g. at 09:00.

bash
Schedule {
  Name = "MaintenanceNightly"
  Run = mon-sun at 09:00
}

Disk space management

  • Action On Purge = Truncate - after a volume is purged, truncates it to zero, freeing disk space immediately
  • Recycle Oldest Volume = yes - when a new volume is needed, automatically recycles the oldest available one
  • Maximum Volume Bytes - the maximum size of a single volume file (e.g. 20G)
  • Maximum Volumes - the maximum number of volumes in the pool, which limits the total capacity

Job order and priorities

Bacula runs jobs according to their priority - lower number = higher priority. This matters for Virtual Full backups to work correctly.

Why the order matters:

A Virtual Full consolidates the previous Full/VirtualFull and all subsequent Incrementals into a new whole. If the VirtualFull started before an Incremental scheduled for the same day, it would not include that Incremental in the consolidation.

Recommended priority settings:

  • Priority = 5 - Virtual Full backups (higher priority)
  • Priority = 10 - Incremental and Full backups (standard priority)
  • Priority = 50 - maintenance jobs (lowest priority)
bash
Schedule {
  Name = "WeeklyCycle"
  Run = Level=Incremental Priority=10 mon-sun at 01:00
  Run = Level=VirtualFull Priority=5 wed,sun at 02:00
}

How does it work? The correct order is ensured by the time offset in the Schedule - Incrementals start at 01:00, VirtualFull only at 02:00. The key point is that the Incrementals start first.

A VirtualFull consolidates all completed jobs in the catalog. If an Incremental is still running when the VirtualFull starts, it won't be included in this consolidation - it will be included in the next VirtualFull instead. This is expected behavior and not a problem, because the data from the running Incremental remains available on its own.

Virtual Full has a higher priority (lower number) so that it can get ahead of less important jobs when waiting in the queue. Priority only matters for jobs waiting in the queue at the same time.

The Allow Mixed Priority parameter in JobDefs allows jobs with different priorities to run at the same time. Without it, Bacula would wait for all higher-priority jobs to finish before starting lower-priority ones.

bash
Allow Mixed Priority = yes

Working with large backups

If a server backup takes several days (e.g. 1 TB of data over a slow network or HDD), you need to adjust the configuration:

1. Increase Volume Retention

bash
Volume Retention = 14 days  # At least 2× the backup period

2. Increase Job/File Retention

bash
Job Retention = 21 days
File Retention = 21 days

3. Temporarily disable the job while it is running

bash
# Disable scheduled runs
bconsole <<< "disable job=BackupServer"

# Re-enable when finished
bconsole <<< "enable job=BackupServer"

4. Optimize the FileSet

Excluding large unnecessary directories speeds up the backup considerably:

bash
Options {
  exclude = yes
  wild = "*/vendor/*"
  wild = "*/node_modules/*"
  wild = "*.log"
  wild = "*.tmp"
  wild = "/var/lib/docker/*"
}

BAT graphical interface

For users who prefer a graphical environment to the command line, Bacula offers BAT (Bacula Administration Tool). It is a Qt application that provides a clear GUI for managing and monitoring backups.

The main advantage of BAT is that you can run it on your local computer and monitor the state of server backups remotely - there's no need to log in to the backup server over SSH.

Installation

bash
# Debian/Ubuntu - on your computer
apt install bacula-console-qt

Connection configuration

BAT requires a configuration file with the details for connecting to the Director. Create a file such as ~/.bacula/bat.conf:

bash
Director {
  Name = backup-dir
  DIRport = 9101
  address = backup.example.com
  Password = "silne-heslo-pro-console"
}

Important: The password must match the password defined in the Console resource on the Director side.

Running with a custom configuration

bash
# Run with an explicit path to the configuration
bat -c ~/.bacula/bat.conf

For more convenient startup, you can create an alias in ~/.bashrc:

bash
alias bat='bat -c ~/.bacula/bat.conf'

Firewall and network access

For remote connections, access to the Director's port must be allowed on the backup server:

bash
# On the backup server
ufw allow from 192.168.1.0/24 to any port 9101 proto tcp

Alternatively, restrict which addresses it listens on in the Director's configuration:

bash
Director {
  Name = backup-dir
  DirAddresses = {
    ip = { addr = 127.0.0.1 }
    ip = { addr = 192.168.1.10 }  # LAN address
  }
  ...
}

Basic bconsole commands

Connecting to the console

bash
bconsole

Monitoring status

bash
# Director status - overview of scheduled and running jobs
status dir

# Detailed status of a specific job
status jobid=1234

# Client status
status client=server-fd

# Storage status
status storage=BackupStorage

Managing jobs

bash
# Run a backup manually
run job=BackupServer level=Incremental yes

# Cancel a running job
cancel jobid=1234

# List recent jobs
list jobs client=server-fd

# Job details
list jobid=1234

Managing volumes

bash
# List volumes in the pool
list volumes pool=ServerPool

# Volume details
list volume=ServerVol-0001

# Purge a volume manually
purge volume=ServerVol-0001 yes

# Truncate - free up space
truncate volume pool=ServerPool allpools storage=BackupStorage yes

Verifying the configuration

bash
# Check the syntax (without starting)
bacula-dir -t -c /etc/bacula/bacula-dir.conf

# Reload the configuration at runtime
bconsole <<< "reload"

# Show the configuration
show job=BackupServer
show pool=ServerPool
show schedule=WeeklyCycle

Troubleshooting

bash
# Show messages
bconsole <<< "messages"

# Log file
tail -f /var/log/bacula/bacula.log

# Check the connection to the client
bconsole <<< "status client=server-fd"

# Check the storage
bconsole <<< "status storage=BackupStorage"

Restoring files from a backup

Interactive restore

bash
bconsole
bash
# Start the restore wizard
restore

# Select the client
> 5 (Select the Client)
> server-fd

# Choose how to select files
> 5 (Select the most recent backup for a client)

# Navigate and select files
cd /home/user/documents
mark important-file.txt
mark another-file.pdf
cd /etc
mark nginx

# Finish the selection
done

# Confirm the restore
yes

Quick restore of the last job

bash
bconsole << 'EOF'
restore client=server-fd where=/tmp/bacula-restore all
5
server-fd
done
yes
EOF

Restoring a specific directory

bash
bconsole << 'EOF'
restore client=server-fd where=/tmp/bacula-restore
5
server-fd
cd /home/user
mark documents
done
yes
EOF

Restoring from a specific job/date

bash
bconsole << 'EOF'
restore client=server-fd where=/tmp/bacula-restore
# Option 3 - Enter list of comma separated JobIds to select
3
6789,6790
cd /etc
mark nginx
done
yes
EOF

Restore parameters

  • where= Target directory for the restore
  • replace= What to do with existing files: always, ifnewer, ifolder, never
  • client= Target client (can differ from the original)

Restoring to a different server

bash
restore client=original-fd restoreclient=new-fd where=/tmp/restore

Solving common problems

"Cannot find any appendable volumes"

The pool has reached its volume limit. Solution:

bash
# Raise the limit
bconsole <<< "update pool=ServerPool MaxVols=100"

# Or create a new volume manually
bconsole <<< "label storage=BackupStorage pool=ServerPool"

"Only one Job found. Consolidation not needed"

The Virtual Full has nothing to consolidate - a previous Full or Incrementals are missing. Make sure your schedule is set up correctly and that at least one Full + Incremental exists.

Disk filling up

bash
# Manual truncate
bconsole << 'EOF'
truncate volume pool=ServerPool allpools storage=BackupStorage yes
truncate volume pool=VFServerPool allpools storage=BackupStorage yes
EOF

# Check the status of volumes
bconsole <<< "list volumes pool=ServerPool"

Job waiting in the queue

bash
# Show waiting jobs
bconsole <<< "status dir"

# Cancel all waiting jobs
bconsole <<< "cancel all yes"

Documentation

For more information, I recommend the official Bacula documentation.

Share
LinkedIn Facebook

Need more than an article?

Get in touch with no obligation – a few sentences about what you are dealing with are enough.

Read on

Similar articles

More notes from operations and development.

All advice