Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 76

Automating Database Backups with Systemd Timers

Recommended resources

Hello! Welcome to the final lesson in our module on PostgreSQL administration.

In our last few lessons, you've learned how to secure your database with user roles, perform manual backups and restores using pg_dump and pg_restore, and ensure PostgreSQL coexists peacefully with SELinux. These are all essential sysadmin skills. However, a backup you have to remember to run manually is a backup that's easy to forget.

Today, we'll solve that problem. Our goal is to automate periodic database backups using a systemd timer. This will bring together everything you've learned about PostgreSQL into a reliable, automated system. We will use systemd timers, the modern and powerful standard for scheduling tasks on Fedora, to create a "set it and forget it" backup solution for your Django projects.

1. Understanding systemd Timers vs. Cron

For decades, cron was the standard tool for scheduling tasks on Linux. While still functional, modern systems like Fedora use systemd, which offers a more powerful and integrated alternative: systemd timers.

A systemd timer is simply a systemd unit that triggers another unit (usually a service) based on a time event. This approach has several advantages:

  • Integrated Logging: All output and errors from your scheduled task are automatically captured by journalctl, making debugging much easier.
  • Dependency Management: You can specify that your backup should only run after the PostgreSQL service has started.
  • Better Management: Each scheduled task gets its own set of unit files, which is cleaner than managing a single, potentially complex crontab file.
  • Guaranteed Execution: You can ensure that a missed job (e.g., if the server was off) runs as soon as the system boots up.

To get a solid foundation, let's watch a video that explains these concepts and shows how to create the necessary files.

Automate Your Tasks with systemd Timers: A Step-by-Step Guide

This video from Learn Linux TV provides an excellent guide to systemd timers. It explains the core concepts, compares them to cron, and walks through creating the two files required for any scheduled task.

Please watch the following segments: Introduction to Timers (00:40 - 07:21): Focus on the comparison between systemd timers and cron, and the benefits of using timers. The Service File (07:36 - 13:30): Pay attention to how a .service file is created to define what command will be executed. The Timer File and Activation (13:30 - 22:36): Understand the structure of the .timer file, which defines when the service runs, and how the timer and service are linked by name. The 'Persistent' Option (25:34 - 27:23): This part covers a key feature for backups: ensuring a missed job runs on the next boot.

As the video explained, automating a task with systemd requires two files that share the same base name:

  1. A .service file: This defines the task itself—the command or script to execute. For us, this will be a script that runs pg_dump.
  2. A .timer file: This defines the schedule—when the .service file should be triggered.

Let's build these now.


2. Hands-On: Creating the Backup Solution

We will create a complete backup solution in three parts: the backup script, the systemd service to run it, and the systemd timer to schedule it.

Part 1: The Backup Script

First, we need a shell script that performs the actual backup and cleans up old files. This combines the pg_dump command you learned previously with some basic shell scripting.

Your Task: Create the Backup Script

  1. Open a terminal and create a new script file. A good location for custom scripts is /usr/local/bin/.

    sudo vim /usr/local/bin/pg-backup.sh
    
  2. Enter insert mode (press i) and paste the following script. Remember to change your_db_name to the actual name of your Django database.

    
    
    
    #!/bin/bash
    
    
    
    
    # === Configuration ===
    # The database you want to back up
    PGDATABASE="your_db_name"
    
    
    
    
    # The user to connect as (postgres user has passwordless local access)
    PGUSER="postgres"
    
    
    
    
    # The directory to store backups in
    BACKUP_DIR="/var/lib/pgsql/backups"
    
    
    
    
    # The number of days to keep backups
    RETENTION_DAYS=7
    
    
    
    
    # === Script Logic ===
    
    
    
    
    # Ensure backup directory exists
    # -p creates parent directories if needed
    # -m sets the mode (permissions)
    sudo -u postgres mkdir -p -m 700 "${BACKUP_DIR}"
    
    
    
    
    # Create a filename with a timestamp
    TIMESTAMP=$(date +%Y%m%d_%H%M%S)
    BACKUP_FILE="${BACKUP_DIR}/${PGDATABASE}_${TIMESTAMP}.dump"
    
    echo "Starting backup of database '${PGDATABASE}' to ${BACKUP_FILE}"
    
    
    
    
    # Dump the database using pg_dump's custom format (-Fc is compressed)
    # We use 'sudo -u postgres' to run the command as the postgres user
    sudo -u postgres pg_dump -U "${PGUSER}" -d "${PGDATABASE}" -Fc -f "${BACKUP_FILE}"
    
    if [ $? -eq 0 ]; then
      echo "Backup completed successfully."
    else
      echo "ERROR: Backup failed!" >&2
      exit 1
    fi
    
    
    
    
    # Remove backups older than the retention period
    echo "Cleaning up backups older than ${RETENTION_DAYS} days..."
    find "${BACKUP_DIR}" -name "*.dump" -mtime +${RETENTION_DAYS} -delete
    
    echo "Cleanup complete."
    

    Note: We use sudo -u postgres to ensure the backup directory and file are owned by the postgres user, which simplifies permissions.

  3. Save and quit vim (press Esc, then type :wq).

  4. Make the script executable:

    sudo chmod +x /usr/local/bin/pg-backup.sh
    

Part 2: The systemd Service File

Now we need a .service file that tells systemd how to run our script.

Your Task: Create the Service File

  1. Create the service unit file in /etc/systemd/system/.

    sudo vim /etc/systemd/system/pg-backup.service
    

    Notice the name pg-backup.service matches the script's purpose.

  2. Enter insert mode and paste the following content.

    [Unit]
    Description=PostgreSQL Backup Service
    
    
    
    # Ensures this runs only after the database is ready
    Requires=postgresql.service
    After=postgresql.service
    
    [Service]
    
    
    
    # 'oneshot' means it's a short-lived task, not a long-running daemon
    Type=oneshot
    
    
    
    
    # The command to execute
    ExecStart=/usr/local/bin/pg-backup.sh
    
  3. Save and quit (:wq).

This service file is simple but powerful. The Requires and After directives ensure that systemd will never try to back up a database that isn't running yet.

Part 3: The systemd Timer File

Finally, we'll create the .timer file to schedule our service.

Your Task: Create the Timer File

  1. Create the timer unit file. It must have the same name as the service file, but with a .timer extension.

    sudo vim /etc/systemd/system/pg-backup.timer
    
  2. Enter insert mode and paste this content:

    [Unit]
    Description=Daily PostgreSQL Backup Timer
    
    [Timer]
    
    
    
    # Run daily at 2:00 AM
    OnCalendar=daily
    
    
    
    
    # Run the job on boot if the last scheduled run was missed
    Persistent=true
    
    [Install]
    
    
    
    # Ensures the timer is started at boot
    WantedBy=timers.target
    
  3. Save and quit (:wq).

The OnCalendar=daily is a convenient shorthand for *-*-* 00:00:00. You can be more specific, like *-*-* 02:30:00 to run at 2:30 AM every day. The Persistent=true directive is the magic that protects you if your server is powered off at 2:30 AM—the backup will run as soon as it boots up again.


3. Activation and Verification

With all the files in place, let's tell systemd about them and activate our timer.

  1. Reload the systemd daemon to make it aware of your new unit files:

    sudo systemctl daemon-reload
    
  2. Enable and start the timer. You do not need to enable the service file; the timer will trigger it automatically. The --now flag both enables the timer to start on boot and starts it immediately.

    sudo systemctl enable --now pg-backup.timer
    
  3. Verify the timer is active and see when it's scheduled to run next.

    systemctl list-timers
    

    You should see pg-backup.timer in the list.

systemctl list-timers Output
Example output of the `systemctl list-timers` command. Your output should show the `pg-backup.timer`, when it will run next (`NEXT`), and how much time is left (`LEFT`).

Testing Your Timer

Waiting until 2 AM to see if it works isn't practical. To test it now:

  1. Edit the timer file (sudo vim /etc/systemd/system/pg-backup.timer).
  2. Change the OnCalendar line to a time a few minutes in the future. For example, if it's 15:20, set it to *-*-* 15:23:00.
  3. Save the file, then run sudo systemctl daemon-reload and sudo systemctl restart pg-backup.timer to apply the new schedule.
  4. Wait for the scheduled time to pass.
  5. Check if the backup file was created: sudo ls -l /var/lib/pgsql/backups.
  6. Check the logs to see the output from your script:
    journalctl -u pg-backup.service
    
    If successful, you'll see the "Backup completed successfully" messages from your script.
Systemd Backup Service Journal Logs
Example of what the journal log might look like for a successful run of a service. You'll see "Started" and "Deactivated" messages, along with any output from your script.

Once you've confirmed it works, remember to change the OnCalendar setting back to daily or your desired schedule and reload/restart the timer again.


Conclusion

Congratulations! You have successfully built a robust, automated backup system for your PostgreSQL database using modern system administration tools. This is a critical piece of infrastructure for any serious web application.

Key Takeaways:

  • systemd timers are the modern way to schedule tasks on Fedora, offering better integration, logging, and control than cron.
  • Automation requires a .service file (defining what to do) and a .timer file (defining when to do it).
  • The OnCalendar directive sets the schedule, and Persistent=true ensures missed jobs are run at the next opportunity.
  • You manage timers with systemctl (enable, start, list-timers) and review their execution with journalctl.

Preview of the Next Module:
This lesson completes our deep dive into PostgreSQL administration. You are now fully equipped to install, secure, manage, and back up a production-ready database.

Next, we will move on to our final module, Containerized Django Deployment with Podman. You've learned how to set up each piece of your web stack—Nginx, Gunicorn, and PostgreSQL—directly on your Fedora system. Now, you'll learn how to package your entire Django application and its services into containers. This is the standard for modern, portable, and scalable web development, and your skills with Podman on Fedora will be directly applicable.