Create daily database backups with Borg

6 minute read

In this post, I want to describe a creating backup of PostgreSQL database and transferring it to a remote server. I will show you how to automate it to get regular daily backup and use a deduplication tool as Borgbackup for effective using storage. Also, the tool has methods for encryption your data and I will show how to use it. Backup database to borg repository is the efficient and secure way to store your critically important data.

Prerequisites

Before following this tutorial, you should understand conditions that I have:

  • Has two Linux servers:
    • one is a backup remote storage
    • second is a database server

I will use Centos 7 on the remote server, and Ubuntu 16.04 on another server with installed PostgreSQL 9.6. They both have connections to one network, an IP of Centos is 192.168.0.1 and Ubuntu has 192.168.0.2. Backup server has a special backup user with name backup_user

Instructions

Install borg on both servers

Is a simple action just use apt or yum install:

apt install borgbackup

And checking your version after that borg --version.

Centos: borg 1.1.4
Ubuntu: borg 1.0.11

Ok, some differences can’t a problem with using it. If you want to use the last version follow this installing instruction. But didn’t use the version before 1.0.9 because that has a vulnerability.

Official docs have a good description of basic usage a borg commands and options, see them for best understanding next commands.

Configure access to the backup server

Generate ssh key to remote access to the backup server if you don’t get it.

ssh-keygen -b 2048 -t rsa -q -N '' -f ~/.ssh/id_rsa

Create a config file if you don’t want to remember an IP of the remote server like me.

cat << EOT >> ~/.ssh/config
Host backup_server
User backup_user
HostName 192.168.0.1
Port 22
EOT

And run command to copy generated public key to the remote server.

ssh-copy-id backup_server

Test it.

# ssh backup_server
Last login: Sun Apr 15 10:18:16 2018
[backup_user@backup_server ~]$

All is OK.

Create a backup script

Ok, let’s begin to automate the process of creating a backup of the database. I am using a pg_dumpall to create a backup of a full database. A result of this command a SQL-file that can create all existing databases and users in a new place. This result before coping will been stored on local path /root/db_dumps. Also I make backup of database configs.

#!/bin/bash
BACKUP_DIR=/root/db_dumps
BACKUP_DUMP_NAME="dump_$(date '+%Y-%m-%d_%H%M%S').sql"
BACKUP_CONFIG_NAME="config_$(date '+%Y-%m-%d_%H%M%S').tar.gz"
USERNAME=postgres
BORG_CMD="borgmatic"
DB_CONF_PATH="/etc/postgresql/9.6/main"

error_exit()
{
  echo "$1" 1>&2
  exit 1
}

debug_message()
{
    echo "$1" 1>&2
}

debug_message "[backup][START] Start creating backup"

#remove previous backup
[[ -d ${BACKUP_DIR} ]] &&  rm -rf ${BACKUP_DIR}
[[ ! -d ${BACKUP_DIR} ]] && mkdir ${BACKUP_DIR} && chmod 700 ${BACKUP_DIR}

#backup database data
cd ${BACKUP_DIR}
if ! pg_dumpall -U "$USERNAME" --file=${BACKUP_DUMP_NAME}; then
  error_exit "[backup][ERROR] Failed to produce data backup"
else
  debug_message "[backup][SUCCESS] Data backup success created"
fi

#backup database configures
cd ${DB_CONF_PATH}
if ! tar -cvzf ${BACKUP_CONFIG_NAME} *.conf > /dev/null 2>&1; then
  error_exit "[backup][ERROR] Cannot create archive with PG conf files"
else
  [[ -f ${BACKUP_CONFIG_NAME} ]] &&  mv ${BACKUP_CONFIG_NAME} ${BACKUP_DIR}
  debug_message "[backup][SUCCESS] Config files success created"
fi

#copy to backup server
if ! ${BORG_CMD} ; then
  error_exit "[backup][ERROR] Failed copy to backup server"
else
  debug_message "[backup][SUCCESS] Data backup success copied"
fi


#remove local backup files
#none

debug_message "[backup][COMPLETE] Backup success created"

I saved this script in the database server by path /opt/postgres_backup.sh. And set execute rights to file (chmod 700 /opt/postgres_backup.sh).

BorgBackup command

In my script has been a variable BORG_CMD is a wrapper of Borgbackup - borgmatic. It initiates a backup, prunes any old backups according to a retention policy, and validates backups for consistency. The script supports specifying your settings in a declarative configuration file rather than having to put them all on the command-line and handles common errors.

To install borgmatic use pip3:

pip3 install --upgrade borgmatic

Borgmatic settings

The settings of borgmatic is stored on /etc/borgmatic/config.yaml. And my ones to our task are described below.

location:
    source_directories:
        - /root/db_dumps

    repositories:
        - backup_server:/backups/db

storage:
    encryption_passcommand: "cat /root/.borg-passphrase"

retention:
    keep_daily: 30
    keep_weekly: 12
    keep_monthly: 6

consistency:
    checks:
        - repository
        - archives
    check_last: 7

Create a passphrase on the database server

Next command creating a key file to use as a passphrase of an encrypted repository. Borg are using system environments to get some parameters. This one show borg how to get a passphrase to either new or existing repository.

head -c 1024 /dev/urandom | base64 > /root/.borg-passphrase
chmod 400 /root/.borg-passphrase
export BORG_PASSCOMMAND="cat /root/.borg-passphrase"

Create a repository on the backup server

Using a repokey modes to “passphrase-only” security. The key will be stored inside the repository (in its “config” file). In above mentioned attack scenario, the attacker will have the key (but not the passphrase).

# borg init --encryption=repokey backup_server:/backups/db

By default repositories initialized with this version will produce security
errors if written to with an older version (up to and including Borg 1.0.8).

If you want to use these older versions, you can disable the check by running:
borg upgrade --disable-tam db_test

See https://borgbackup.readthedocs.io/en/stable/changes.html#pre-1-0-9-manifest-spoofing-vulnerability for details about the security implications.

The output has some warnings about a using old version.

Create a deamon to automate running a backup script

Ubuntu has a nice mechanism to auto-running scripts or applications in the background. His name - SystemD. And if exists a necessity to run a script by a scheduler (i.e. every night) you should use timers that is part of systemd.

I write my own daemon to run an above script. It has stored on /etc/systemd/system/backup.service.

[Unit]
Description=Backup PostgreSQL database

[Service]
Type=oneshot
ExecStart=/bin/bash /opt/postgres_backup.sh

And I has a timer unit that runs a backup.service every day at midnight

[Unit]
Description=Run backup script every day at midnight

[Timer]
OnCalendar=daily

[Install]
WantedBy=timers.target

After that you should reload daemon-service.

systemctl daemon-reload

Manual run script and view logs

To run our systemd unit (or daemon) just run next command on a terminal:

systemctl start backup

And anfter this command completed view logs by journalctl -u backup

Apr 14 11:07:48 ubuntu systemd[1]: Starting Backup PostgreSQL database...
Apr 14 11:07:48 ubuntu bash[234]: [backup][START] Start creating backup
Apr 14 11:08:48 ubuntu bash[234]: [backup][SUCCESS] Data backup success created
Apr 14 11:08:48 ubuntu bash[234]: [backup][SUCCESS] Config files success created
Apr 14 11:10:27 ubuntu bash[234]: [backup][SUCCESS] Data backup success copied
Apr 14 11:10:27 ubuntu bash[234]: [backup][COMPLETE] Backup success created
Apr 14 11:10:27 ubuntu systemd[1]: Started Backup PostgreSQL database.

Finish preparations and check the results

Don’t forget setup enable flag on the timer for working them after reboot system.

systemctl enable backup.timer
systemctl start backup.timer

And check active system timers with systemctl list-timers.

NEXT                         LEFT     LAST                         PASSED       UNIT                         ACTIVATES
Mon 2018-04-16 00:00:00 MSK  9h left  Sun 2018-04-15 00:00:52 MSK  14h ago      backup.timer                 backup.service
...

5 timers listed.

Ok, I just run a backup script two times on my database and have got two backups.

# borg list backup_server:/backups/db
ubuntu-2018-04-15T00:05:46.344337 Sun, 2018-04-15 00:05:28
ubuntu-2018-04-15T00:08:46.344337 Sun, 2018-04-15 00:08:47

Let’s see the information about second one.

# borg info backup_server:/backups/db::ubuntu-2018-04-15T00:08:46.344337
Name: ubuntu-2018-04-15T00:08:46.344337
Fingerprint: 19314fb992f83d9e746eda534d22fc8375a8d20c0cf8828a4353463574asdw
Hostname: ubuntu
Username: root
Time (start): Sun, 2018-04-15 00:08:47
Time (end):   Sun, 2018-04-15 00:08:47
Number of files: 2

                       Original size      Compressed size    Deduplicated size
This archive:                8.50 GB              8.50 GB                624 B
All archives:               17.00 GB             17.00 GB              6.64 GB

                       Unique chunks         Total chunks
Chunk index:                    2548                 6750

One dump file has a size of 8G, but repository using deduplication stores two ones with less disk usage space. All archives has reduce about 6,6G. Next command will agree this words

backup_server ># du -hd0 /backups/db
6,2G    db

And at the end, I want to show the command that extracts (“restore”) backup from an archive repository to a local path.

mkdir old_data
cd old_data
borg extract backup_server:/backups/db::ubuntu-2018-04-15T00:08:46.344337

In conclucion I want to say that the Borg is a simple, reliable and secure tool to store your backup data. The data deduplication technique used makes Borg suitable for daily backups since only changes are stored. The authenticated encryption technique makes it suitable for backups to not fully trusted targets. And it has a simple interface to run with many parameters with borgmatic.

Additional information:

This links might useful to get any additional information: