Skip to main content
info@drupalodyssey.com
Wednesday, July 30, 2025
Contact

Main navigation

  • Home
  • Blog
    • all
    • Development
    • Community
    • Management
    • DevOps
    Photo by  Anastasia  Shuraeva: https://www.pexels.com/photo/a-bearded-man-reading-a-burning-newspaper-7539726
    Bulletproof Your Drupal Data With Automated Nightly Backups
    Jul 30, 2025
    Contractor happy with blueprints.
    Streamlining Drupal Deployments Using Drush and GitHub Actions CI/CD
    Jul 22, 2025
    Reduce, Reuse, Recycle
    Streamlined Content: Effortless PDF Display & Management in Drupal
    Jul 21, 2025
    Aluminum Cans Passing Through the Assembly Line by cottonbro studio on Pexels
    Automate and Simplify Your Drupal Workflow with Bash Scripts for Shared Hosting
    Jul 19, 2024
    Binoculars resting on newspapers.
    Evaluating Search and Replace Scanner: The Ultimate Tool for Drupal Bulk Content Edits?
    Jun 29, 2024
    People looking at a computer screen
    S3 File System Module Not Working with Media Entity Download Module? Here's the Fix
    Jun 18, 2024
    Mechanic hands working on an engine.
    Setting Up the Etsy OAuth2 Client For Use With The Etsy Shop Integration Module
    May 10, 2024
    Fashion designer sketching new garments.
    Crafting Your Online Store: Drupal's Role in Your Etsy Success
    May 09, 2024
    Socket toolbox
    Beginner's Guide: Getting Started With Drush for Efficient Drupal Development
    May 08, 2024
    Stargazing over mountians.
    Drupal-Powered Stargazing: A Module for NASA's Astronomy Picture of the Day
    Sep 15, 2023
    Computer screen with code.
    Learn How To Script Drupal Installations Using Drush
    Dec 08, 2014
    Scuba diver with Drupal mask.
    Scuba: Drupal Style
    Oct 16, 2014
    Woman frustrated with laptop.
    5 Reasons Your CMS Sucks
    Jul 24, 2013
    Two young men having a discussion in front of a computer.
    Deployment Module XSRF Patch Committed
    Jul 05, 2013
    Two young men having a discussion in front of a computer.
    Deployment Module XSRF Patch Committed
    Jul 05, 2013
    Application settings.
    Using PHP To Disable Internet Explorer Compatibility Mode
    Jun 04, 2013
  • Resources
  • About
  • SPACER
  • SPACER
  • SPACER
  • SPACER
  • SPACER
Search
DevOps

Bulletproof Your Drupal Data With Automated Nightly Backups

July 30, 2025

Drupal Odyssey is supported by it's readers. When you purchase products or services using the links on this site, we may earn a small commission at no additional cost to you. Learn more

As a seasoned Drupal developer and user, one of the most critical aspects of site management, often overlooked until it's too late, is robust and reliable backups. Whether you're a content manager, site builder, or developer, knowing your data is safe is paramount. I've been working with Drupal for a long time …. since Drupal 5, and the one constant truth across all versions has been: backups are non-negotiable. Period.

Today, I want to share a bash script I use for nightly Drupal database backups. While the core concept of a database backup isn't Drupal-specific, the way I've optimized this script is particularly useful for Drupal sites. Plus, having a fresh backup available is the first step in a common local development workflow, which I'll touch upon in a future post about Composer scripts (stay tuned!)

Why a Custom Bash Script?

You might be thinking, "Aren't there modules for this?" Yes, there are, but a simple bash script executed via a cron job offers several advantages:

  • Reliability: It runs outside of the Drupal environment, meaning if your Drupal site itself is having issues, your backup process isn't affected.
  • Performance: It directly interacts with MySQL/MariaDB, often more efficiently for large databases.
  • Control: You have granular control over what gets backed up and how, including where sensitive data is stored and ensuring it adheres to your privacy and security policies.

My script is a modification of a solid foundation I found on TecAdmin.net by Rahul Kumar. I tailored it specifically for my needs on my Drupal site, focusing on optimizing the backup size without losing critical information as well as some minor performance enhancements.

The Drupal-Specific Optimization: Handling Cache Tables

Drupal's caching system is fantastic for performance, but it generates a lot of data in tables that start with "cache_" (e.g., cache, cache_render, cache_container). The data within these tables is expendable – it can be regenerated by simply clearing Drupal's caches. Including all this dynamic data in every nightly backup significantly increases the backup file size and the time it takes to create.

My modification ensures that for these cache tables, only their structure (schema) is backed up, not their data. This keeps your backup files lean and fast, while still preserving the necessary table definitions. When you restore, you just clear caches, and Drupal rebuilds what it needs.

My Nightly Drupal Database Backup Script

Here's the script I use. Remember to replace the placeholder values ([path_to_backup_folder], [mysql_host], etc.) with your actual credentials and paths.

#!/bin/bash
 
################################################################
##
##   MySQL Database Backup Script 
##   Written By: Rahul Kumar
##   URL: https://tecadmin.net/bash-script-mysql-database-backup/
##   Last Update: Jan 05, 2019
##
##   Modified by: Ron Ferguson
##   Change log: Modified to exclude cache tables and minor
##   performance enhancements.
##   Last Updated: Nov 01, 2024
##
################################################################
 
export PATH=/bin:/usr/bin:/usr/local/bin
 
################################################################
################## Update below values  ########################
 
DB_BACKUP_PATH='[path_to_backup_folder]'
MYSQL_HOST='[mysql_host]'
MYSQL_PORT='[mysql_port]'
MYSQL_USER='[mysql_user]'
MYSQL_PASSWORD='[mysql_password]'
DATABASE_NAME='[mysql_database_name]'
BACKUP_RETAIN_DAYS=4   ## Number of days to keep local backup copy
 
#################################################################


# Validate required variables
if [ -z "${DATABASE_NAME}" ] || [ -z "${MYSQL_HOST}" ] || [ -z "${MYSQL_USER}" ] || \
   [ -z "${MYSQL_PASSWORD}" ] || [ -z "${DB_BACKUP_PATH}" ]; then
    echo "Error: One or more required environment variables are not set."
    exit 1
fi

# Set the backup date and start message
TODAY=$(date +"%Y%m%d-%H%M")
echo "Backup started for database - ${DATABASE_NAME}"

# Get a list of tables that begin with "cache"
CACHE_TABLES=$(mysql -h "${MYSQL_HOST}" -P "${MYSQL_PORT}" -u "${MYSQL_USER}" \
    -p"${MYSQL_PASSWORD}" -N -e "SHOW TABLES LIKE 'cache%'" "${DATABASE_NAME}")

# Generate the `--ignore-table` options for each cache table
IGNORE_TABLES=""
for table in ${CACHE_TABLES}; do
    IGNORE_TABLES+="--ignore-table=${DATABASE_NAME}.${table} "
done

# Perform the backup
BACKUP_FILE="${DB_BACKUP_PATH}/${DATABASE_NAME}-live-${TODAY}.sql.gz"
mysqldump --column-statistics=0 \
    ${IGNORE_TABLES} \
    -h "${MYSQL_HOST}" \
    -P "${MYSQL_PORT}" \
    -u "${MYSQL_USER}" \
    -p"${MYSQL_PASSWORD}" \
    "${DATABASE_NAME}" | gzip > "${BACKUP_FILE}" \
    && echo "Database backup successfully completed: ${BACKUP_FILE}" \
    || { echo "Error found during backup"; exit 1; }

# Remove old backups
DBDELDATE=$(date +"%Y%m%d" --date="${BACKUP_RETAIN_DAYS} days ago")
find "${DB_BACKUP_PATH}" -type f -name "*.sql.gz" -mtime +"${BACKUP_RETAIN_DAYS}" -exec rm -f {} \;

 
### End of script ####

How it Works: A Breakdown

Let 's walk through the key parts of the script:

Configuration Variables

At the top, you'll define your database connection details, the backup path, and how many days you want to retain backups. Ensure these are accurate and secure!

Variable Validation

The script includes a simple check to ensure all necessary variables are set. This prevents the script from failing silently if you forget to fill in a placeholder.

Identifying Cache Tables

This is the core Drupal optimization that I made.

mysql -h ... -N -e "SHOW TABLES LIKE 'cache% '" "${DATABASE_NAME}"

This command connects to your MySQL database and queries for all table names that start with cache. The -N option suppresses column names, and -e allows execution of a SQL query.

The output (a list of cache table names) is then stored in the CACHE_TABLES variable.

Building mysqldump Ignore List

The script iterates through each identified cache_ table.

For each table, it constructs an --ignore-table=your_database_name.table_name option.

These options are concatenated into the IGNORE_TABLES variable. This tells mysqldump to skip the data for these tables.

Performing the Backup

mysqldump --column-statistics=0 ${IGNORE_TABLES} ... | gzip > "${BACKUP_FILE}"

mysqldump: The command-line utility for dumping MySQL databases.

--column-statistics=0: This is important for mysqldump versions 8.0+, as it can otherwise cause issues with permissions if the user doesn 't have SELECT privilege on mysql.column_stats.

${IGNORE_TABLES}: This is where our generated list of cache tables (to ignore data for) comes in.

The output of mysqldump is piped (|) to gzip, which compresses the SQL dump.

> "${BACKUP_FILE}": The compressed output is then redirected into your specified backup file path with a timestamp.

&& echo ... || { echo ...; exit 1; }: This is a bash construct that prints a success message if mysqldump is successful (&&) or an error message if it fails (||), exiting with an error code.

Removing Old Backups

find "${DB_BACKUP_PATH}" -type f -name "*.sql.gz" -mtime +"${BACKUP_RETAIN_DAYS}" -exec rm -f {} \;

This command finds all .sql.gz files in your backup path.

-mtime +"${BACKUP_RETAIN_DAYS}": Filters for files that were last modified more than the specified number of days ago.

-exec rm -f {} \;: Executes the rm -f command to forcefully remove each found file. This keeps your backup directory from overflowing.

Beyond the Basics: Further Enhancements

While this script provides a robust solution for daily production backups, its flexibility allows for further powerful modifications. As your development workflow evolves, you might consider enhancing this script to accept command-line input. For instance, you could pass arguments to switch between different database environments (development, testing, or production), allowing you to use the same script for various environment backup needs. Moreover, to prevent errors in automated cron jobs, you could implement logic within the script to set default environment values if no input is provided. This ensures the script always has the necessary parameters, even when running non-interactively, making it even more resilient and versatile for multi-environment Drupal setups. These types of enhancements are on my own to-do list, and they demonstrate how a simple bash script can become a powerful tool in your DevOps toolkit.

Automation with Cron

To automate this, you'll need to set up a cron job on your server or workstation.

Save the script: Save the script to a file (e.g., drupal_db_backup.sh) in a secure location on your server or workstation (e.g., /usr/local/bin/).

Make it executable: chmod +x /usr/local/bin/drupal_db_backup.sh

Edit your crontab by running the command crontab -e

Add a line like this to run it every night at 2:00 AM:

0 2 * * * /usr/local/bin/drupal_db_backup.sh > /dev/null 2>&1

0 2 * * *: Runs at 2:00 AM daily.

> /dev/null 2>&1: Redirects all output (standard output and errors) to /dev/null, preventing unnecessary emails from cron. (You might want to remove this initially for testing to see output.)

Conclusion

Having an automated, optimized database backup system is a cornerstone of responsible Drupal site management. This script provides a lean and reliable way to ensure your most critical data is safe and easily restorable, without bloating your backups with expendable cache data.

What are your go-to backup strategies for Drupal? Share your tips in the comments below!

Author

Ron Ferguson

Previous Blog

Next Blog

0 Comments

Login or Register to post comments.

Categories

Categories

  • Development
    (6)
  • Community
    (9)
  • Management
    (6)
  • DevOps
    (5)

Trending Blog

Trending Blog

Contractor happy with blueprints.
Streamlining Drupal Deployments Using Drush and GitHub Actions CI/CD
22 Jul, 2025
Reduce, Reuse, Recycle
Streamlined Content: Effortless PDF Display & Management in Drupal
21 Jul, 2025
Photo by  Anastasia  Shuraeva: https://www.pexels.com/photo/a-bearded-man-reading-a-burning-newspaper-7539726
Bulletproof Your Drupal Data With Automated Nightly Backups
30 Jul, 2025
Woman frustrated with laptop.
5 Reasons Your CMS Sucks
24 Jul, 2013
Mechanic hands working on an engine.
Setting Up the Etsy OAuth2 Client For Use With The Etsy Shop Integration Module
10 May, 2024

Tags

Tags

  • Drupal 10
  • Drupal 9
  • Drupal 8
  • Drupal 11
  • Drupal 7
  • Drush
  • Drupal
  • MySQL

Ad - Sidebar (300 x 250 AD)

Ad - Sidebar (300 x 600 AD)

Newsletter

Subscribe my Newsletter for new blog & tips Let’s stay updated!

Categories

  • Development
  • Community
  • Management

Useful Links

  • About
  • Contact
  • Privacy Policy
  • Terms & Conditions
  • Disclaimer
  • Cookies

Must Read

Aluminum Cans Passing Through the Assembly Line by cottonbro studio on Pexels
Automate and Simplify Your Drupal Workflow with Bash Scripts for Shared Hosting
19 Jul, 2024
Binoculars resting on newspapers.
Evaluating Search and Replace Scanner: The Ultimate Tool for Drupal Bulk Content Edits?
29 Jun, 2024
Mechanic hands working on an engine.
Setting Up the Etsy OAuth2 Client For Use With The Etsy Shop Integration Module
10 May, 2024
Socket toolbox
Beginner's Guide: Getting Started With Drush for Efficient Drupal Development
08 May, 2024

© 2024 All Rights Reserved.

Proud supporter of active military, veterans and first responders.