Configure Redis backup encryption with GPG for secure automated backups

Intermediate 45 min Apr 15, 2026 67 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up encrypted Redis backups using GPG keys with automated scheduling through systemd timers. This tutorial covers GPG key management, backup script creation, and secure restoration procedures for production Redis environments.

Prerequisites

  • Redis server installed and running
  • Root or sudo access
  • Basic understanding of Linux systemd services
  • Familiarity with GPG encryption concepts

What this solves

Redis databases contain sensitive application data that needs secure backup strategies in production environments. Standard Redis backup files (RDB and AOF) are stored in plain text, making them vulnerable if backup storage is compromised. This tutorial implements GPG encryption for Redis backups with automated scheduling, ensuring your database snapshots remain secure during storage and transmission.

Prerequisites and preparation

Install required packages

Install GPG, Redis tools, and backup utilities needed for encrypted backup operations.

sudo apt update
sudo apt install -y gnupg2 redis-tools gzip rsync
sudo dnf update -y
sudo dnf install -y gnupg2 redis gzip rsync

Create dedicated backup user

Create a system user specifically for Redis backup operations to maintain security isolation.

sudo useradd -r -s /bin/bash -d /var/lib/redis-backup -m redis-backup
sudo mkdir -p /var/lib/redis-backup/{scripts,backups,keys}
sudo chown -R redis-backup:redis-backup /var/lib/redis-backup
sudo chmod 750 /var/lib/redis-backup

GPG key generation and management

Generate GPG key pair for backups

Create a dedicated GPG key pair for Redis backup encryption using the backup user account.

sudo -u redis-backup gpg --batch --full-generate-key <

Export and secure GPG keys

Export the GPG keys and store them securely for backup recovery scenarios.

sudo -u redis-backup bash -c '
GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format=long | grep sec | awk "{print \$2}" | cut -d"/" -f2)
echo "GPG Key ID: $GPG_KEY_ID" > /var/lib/redis-backup/keys/key-info.txt
gpg --armor --export $GPG_KEY_ID > /var/lib/redis-backup/keys/public-key.asc
gpg --armor --export-secret-keys $GPG_KEY_ID > /var/lib/redis-backup/keys/private-key.asc
chmod 600 /var/lib/redis-backup/keys/private-key.asc
chmod 644 /var/lib/redis-backup/keys/public-key.asc
'

Configure GPG trust and passphrase

Set up GPG trust relationships and configure passphrase handling for automated operations.

sudo -u redis-backup bash -c '
GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format=long | grep sec | awk "{print \$2}" | cut -d"/" -f2)
echo "$GPG_KEY_ID:6:" | gpg --import-ownertrust
echo "your-secure-passphrase-here" > /var/lib/redis-backup/keys/passphrase
chmod 600 /var/lib/redis-backup/keys/passphrase
'

Redis backup script with GPG encryption

Create encrypted backup script

Develop a comprehensive backup script that handles Redis data export, compression, and GPG encryption.

#!/bin/bash

Redis Backup Script with GPG Encryption

Configuration

REDIS_HOST="127.0.0.1" REDIS_PORT="6379" REDIS_PASSWORD="" BACKUP_DIR="/var/lib/redis-backup/backups" LOG_FILE="/var/lib/redis-backup/backup.log" GPG_PASSPHRASE_FILE="/var/lib/redis-backup/keys/passphrase" RETENTION_DAYS="30"

Get GPG key ID

GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format=long | grep sec | awk '{print $2}' | cut -d"/" -f2)

Function to log messages

log_message() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE" }

Function to create RDB backup

create_rdb_backup() { local backup_file="$1" if [ -n "$REDIS_PASSWORD" ]; then redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWORD" --rdb "$backup_file" else redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" --rdb "$backup_file" fi return $? }

Function to backup Redis using BGSAVE

create_bgsave_backup() { local redis_data_dir="$1" local backup_file="$2" # Trigger BGSAVE if [ -n "$REDIS_PASSWORD" ]; then redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWORD" BGSAVE else redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" BGSAVE fi # Wait for BGSAVE to complete while true; do if [ -n "$REDIS_PASSWORD" ]; then BGSAVE_STATUS=$(redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWORD" LASTSAVE) else BGSAVE_STATUS=$(redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" LASTSAVE) fi sleep 2 if [ -n "$REDIS_PASSWORD" ]; then CURRENT_STATUS=$(redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWORD" LASTSAVE) else CURRENT_STATUS=$(redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" LASTSAVE) fi if [ "$BGSAVE_STATUS" != "$CURRENT_STATUS" ]; then break fi done # Copy the RDB file cp "$redis_data_dir/dump.rdb" "$backup_file" return $? }

Main backup function

perform_backup() { local timestamp=$(date +"%Y%m%d_%H%M%S") local backup_name="redis_backup_${timestamp}" local temp_backup="${BACKUP_DIR}/${backup_name}.rdb" local compressed_backup="${BACKUP_DIR}/${backup_name}.rdb.gz" local encrypted_backup="${BACKUP_DIR}/${backup_name}.rdb.gz.gpg" log_message "Starting Redis backup: $backup_name" # Create backup directory if it doesn't exist mkdir -p "$BACKUP_DIR" # Create Redis backup if create_rdb_backup "$temp_backup"; then log_message "Redis RDB backup created successfully" else log_message "ERROR: Failed to create Redis backup" return 1 fi # Compress the backup if gzip -c "$temp_backup" > "$compressed_backup"; then log_message "Backup compressed successfully" rm "$temp_backup" else log_message "ERROR: Failed to compress backup" rm "$temp_backup" return 1 fi # Encrypt the backup if gpg --batch --yes --passphrase-file "$GPG_PASSPHRASE_FILE" --cipher-algo AES256 --compress-algo 1 --symmetric --output "$encrypted_backup" "$compressed_backup"; then log_message "Backup encrypted successfully: $encrypted_backup" rm "$compressed_backup" else log_message "ERROR: Failed to encrypt backup" rm "$compressed_backup" return 1 fi # Verify encrypted backup if gpg --batch --yes --passphrase-file "$GPG_PASSPHRASE_FILE" --list-packets "$encrypted_backup" > /dev/null 2>&1; then log_message "Backup verification successful" else log_message "ERROR: Backup verification failed" return 1 fi # Calculate and log file sizes local file_size=$(du -h "$encrypted_backup" | cut -f1) log_message "Encrypted backup size: $file_size" return 0 }

Function to clean old backups

cleanup_old_backups() { log_message "Starting cleanup of backups older than $RETENTION_DAYS days" find "$BACKUP_DIR" -name "redis_backup_*.rdb.gz.gpg" -mtime +"$RETENTION_DAYS" -delete local deleted_count=$(find "$BACKUP_DIR" -name "redis_backup_*.rdb.gz.gpg" -mtime +"$RETENTION_DAYS" | wc -l) log_message "Cleanup completed. Deleted $deleted_count old backup files" }

Function to check Redis connectivity

check_redis_connection() { if [ -n "$REDIS_PASSWORD" ]; then redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWORD" ping > /dev/null 2>&1 else redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" ping > /dev/null 2>&1 fi return $? }

Main execution

main() { log_message "Redis backup script started" # Check Redis connection if ! check_redis_connection; then log_message "ERROR: Cannot connect to Redis server at $REDIS_HOST:$REDIS_PORT" exit 1 fi # Check GPG key if [ -z "$GPG_KEY_ID" ]; then log_message "ERROR: No GPG key found for encryption" exit 1 fi # Perform backup if perform_backup; then log_message "Backup completed successfully" # Cleanup old backups cleanup_old_backups log_message "Redis backup script completed successfully" exit 0 else log_message "ERROR: Backup failed" exit 1 fi }

Run main function

main "$@"

Set script permissions and ownership

Configure appropriate permissions for the backup script to ensure security and functionality.

sudo chown redis-backup:redis-backup /var/lib/redis-backup/scripts/redis-backup.sh
sudo chmod 750 /var/lib/redis-backup/scripts/redis-backup.sh

Create backup verification script

Develop a script to verify and test encrypted backup integrity and restoration capabilities.

#!/bin/bash

Redis Backup Verification Script

BACKUP_DIR="/var/lib/redis-backup/backups" GPG_PASSPHRASE_FILE="/var/lib/redis-backup/keys/passphrase" TEMP_DIR="/tmp/redis-backup-verify"

Function to verify backup integrity

verify_backup() { local backup_file="$1" echo "Verifying backup: $(basename $backup_file)" # Create temporary directory mkdir -p "$TEMP_DIR" # Decrypt backup if gpg --batch --yes --passphrase-file "$GPG_PASSPHRASE_FILE" --decrypt "$backup_file" > "$TEMP_DIR/backup.rdb.gz" 2>/dev/null; then echo "✓ Decryption successful" else echo "✗ Decryption failed" return 1 fi # Decompress backup if gunzip -c "$TEMP_DIR/backup.rdb.gz" > "$TEMP_DIR/backup.rdb" 2>/dev/null; then echo "✓ Decompression successful" else echo "✗ Decompression failed" rm -rf "$TEMP_DIR" return 1 fi # Verify RDB file format if file "$TEMP_DIR/backup.rdb" | grep -q "Redis RDB"; then echo "✓ RDB format verification successful" else echo "✗ RDB format verification failed" rm -rf "$TEMP_DIR" return 1 fi # Get file size local size=$(du -h "$backup_file" | cut -f1) echo "✓ Backup size: $size" # Cleanup rm -rf "$TEMP_DIR" echo "✓ Backup verification completed successfully" return 0 }

Main function

if [ $# -eq 0 ]; then echo "Usage: $0 or 'latest' for most recent backup" exit 1 fi if [ "$1" = "latest" ]; then LATEST_BACKUP=$(ls -t "$BACKUP_DIR"/redis_backup_*.rdb.gz.gpg 2>/dev/null | head -1) if [ -z "$LATEST_BACKUP" ]; then echo "No backups found in $BACKUP_DIR" exit 1 fi verify_backup "$LATEST_BACKUP" else if [ ! -f "$1" ]; then echo "Backup file not found: $1" exit 1 fi verify_backup "$1" fi

Set verification script permissions

Configure permissions for the backup verification script.

sudo chown redis-backup:redis-backup /var/lib/redis-backup/scripts/verify-backup.sh
sudo chmod 750 /var/lib/redis-backup/scripts/verify-backup.sh

Automated backup scheduling with systemd

Create systemd service unit

Create a systemd service unit for the Redis backup process to ensure proper execution environment.

[Unit]
Description=Redis Encrypted Backup Service
After=redis.service network.target
Requires=redis.service

[Service]
Type=oneshot
User=redis-backup
Group=redis-backup
WorkingDirectory=/var/lib/redis-backup
ExecStart=/var/lib/redis-backup/scripts/redis-backup.sh
Environment=HOME=/var/lib/redis-backup
Environment=GNUPGHOME=/var/lib/redis-backup/.gnupg
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/redis-backup
NoNewPrivileges=true
CapabilityBoundingSet=
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM

Create systemd timer unit

Configure a systemd timer to schedule automated backup execution at regular intervals.

[Unit]
Description=Redis Encrypted Backup Timer
Requires=redis-backup.service

[Timer]

Run daily at 2:30 AM

OnCalendar=--* 02:30:00

Run 5 minutes after boot if missed

OnBootSec=5min

Randomize execution time by up to 10 minutes

RandomizedDelaySec=600 Persistent=true [Install] WantedBy=timers.target

Enable and start systemd timer

Activate the systemd timer and verify its configuration for automated backup scheduling.

sudo systemctl daemon-reload
sudo systemctl enable redis-backup.timer
sudo systemctl start redis-backup.timer
sudo systemctl status redis-backup.timer

Test backup service manually

Execute the backup service manually to verify proper operation before relying on automated scheduling.

sudo systemctl start redis-backup.service
sudo systemctl status redis-backup.service
sudo -u redis-backup cat /var/lib/redis-backup/backup.log

Backup verification and restoration procedures

Verify latest backup

Test the integrity and accessibility of the most recent encrypted backup file.

sudo -u redis-backup /var/lib/redis-backup/scripts/verify-backup.sh latest

Create restoration script

Develop a comprehensive script for restoring Redis data from encrypted backups.

#!/bin/bash

Redis Backup Restoration Script

BACKUP_DIR="/var/lib/redis-backup/backups" GPG_PASSPHRASE_FILE="/var/lib/redis-backup/keys/passphrase" REDIS_DATA_DIR="/var/lib/redis" TEMP_DIR="/tmp/redis-restore" LOG_FILE="/var/lib/redis-backup/restore.log"

Function to log messages

log_message() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE" echo "$1" }

Function to restore backup

restore_backup() { local backup_file="$1" if [ ! -f "$backup_file" ]; then log_message "ERROR: Backup file not found: $backup_file" return 1 fi log_message "Starting restoration from: $(basename $backup_file)" # Create temporary directory mkdir -p "$TEMP_DIR" # Stop Redis service log_message "Stopping Redis service" sudo systemctl stop redis-server 2>/dev/null || sudo systemctl stop redis 2>/dev/null # Wait for Redis to stop sleep 3 # Backup current Redis data if [ -f "$REDIS_DATA_DIR/dump.rdb" ]; then log_message "Backing up current Redis data" sudo cp "$REDIS_DATA_DIR/dump.rdb" "$REDIS_DATA_DIR/dump.rdb.backup.$(date +%Y%m%d_%H%M%S)" fi # Decrypt backup log_message "Decrypting backup file" if gpg --batch --yes --passphrase-file "$GPG_PASSPHRASE_FILE" --decrypt "$backup_file" > "$TEMP_DIR/backup.rdb.gz" 2>/dev/null; then log_message "Decryption successful" else log_message "ERROR: Decryption failed" rm -rf "$TEMP_DIR" sudo systemctl start redis-server 2>/dev/null || sudo systemctl start redis 2>/dev/null return 1 fi # Decompress backup log_message "Decompressing backup file" if gunzip -c "$TEMP_DIR/backup.rdb.gz" > "$TEMP_DIR/dump.rdb" 2>/dev/null; then log_message "Decompression successful" else log_message "ERROR: Decompression failed" rm -rf "$TEMP_DIR" sudo systemctl start redis-server 2>/dev/null || sudo systemctl start redis 2>/dev/null return 1 fi # Copy restored file to Redis data directory log_message "Installing restored Redis data" sudo cp "$TEMP_DIR/dump.rdb" "$REDIS_DATA_DIR/dump.rdb" sudo chown redis:redis "$REDIS_DATA_DIR/dump.rdb" sudo chmod 644 "$REDIS_DATA_DIR/dump.rdb" # Start Redis service log_message "Starting Redis service" sudo systemctl start redis-server 2>/dev/null || sudo systemctl start redis 2>/dev/null # Wait for Redis to start sleep 5 # Verify Redis is running if redis-cli ping > /dev/null 2>&1; then log_message "Redis restoration completed successfully" # Get database info local db_size=$(redis-cli dbsize) log_message "Restored database contains $db_size keys" # Cleanup rm -rf "$TEMP_DIR" return 0 else log_message "ERROR: Redis failed to start after restoration" rm -rf "$TEMP_DIR" return 1 fi }

Main function

if [ $# -eq 0 ]; then echo "Usage: $0 or 'latest' for most recent backup" echo "Available backups:" ls -la "$BACKUP_DIR"/redis_backup_*.rdb.gz.gpg 2>/dev/null | tail -10 exit 1 fi if [ "$1" = "latest" ]; then LATEST_BACKUP=$(ls -t "$BACKUP_DIR"/redis_backup_*.rdb.gz.gpg 2>/dev/null | head -1) if [ -z "$LATEST_BACKUP" ]; then echo "No backups found in $BACKUP_DIR" exit 1 fi echo "Restoring from latest backup: $(basename $LATEST_BACKUP)" read -p "Are you sure you want to restore? This will overwrite current data [y/N]: " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then restore_backup "$LATEST_BACKUP" else echo "Restoration cancelled" exit 1 fi else if [ ! -f "$1" ]; then echo "Backup file not found: $1" exit 1 fi echo "Restoring from: $(basename $1)" read -p "Are you sure you want to restore? This will overwrite current data [y/N]: " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then restore_backup "$1" else echo "Restoration cancelled" exit 1 fi fi

Set restoration script permissions

Configure appropriate permissions for the restoration script with elevated privileges for Redis service management.

sudo chown redis-backup:redis-backup /var/lib/redis-backup/scripts/restore-backup.sh
sudo chmod 750 /var/lib/redis-backup/scripts/restore-backup.sh

Configure sudo permissions for Redis service management

Allow the backup user to manage Redis service for restoration procedures.

# Allow redis-backup user to manage Redis service
redis-backup ALL=(ALL) NOPASSWD: /bin/systemctl start redis-server, /bin/systemctl stop redis-server, /bin/systemctl start redis, /bin/systemctl stop redis, /bin/cp, /bin/chown

Monitoring and maintenance

Create backup monitoring script

Develop a monitoring script to check backup status and send alerts for backup failures.

#!/bin/bash

Redis Backup Monitoring Script

BACKUP_DIR="/var/lib/redis-backup/backups" LOG_FILE="/var/lib/redis-backup/backup.log" ALERT_EMAIL="admin@example.com" MAX_AGE_HOURS="25"

Function to send alert

send_alert() { local message="$1" echo "REDIS BACKUP ALERT: $message" | mail -s "Redis Backup Alert" "$ALERT_EMAIL" 2>/dev/null echo "$(date '+%Y-%m-%d %H:%M:%S') - ALERT: $message" >> "$LOG_FILE" }

Check if recent backup exists

check_recent_backup() { local latest_backup=$(ls -t "$BACKUP_DIR"/redis_backup_*.rdb.gz.gpg 2>/dev/null | head -1) if [ -z "$latest_backup" ]; then send_alert "No backup files found in $BACKUP_DIR" return 1 fi # Check backup age local backup_age=$(find "$latest_backup" -mmin +$((MAX_AGE_HOURS * 60)) 2>/dev/null) if [ -n "$backup_age" ]; then local backup_date=$(stat -c %y "$latest_backup" | cut -d' ' -f1,2 | cut -d'.' -f1) send_alert "Latest backup is older than $MAX_AGE_HOURS hours. Last backup: $backup_date" return 1 fi return 0 }

Check log for errors

check_backup_errors() { local recent_errors=$(grep "ERROR" "$LOG_FILE" | tail -5) if [ -n "$recent_errors" ]; then send_alert "Recent backup errors detected: $recent_errors" return 1 fi return 0 }

Main monitoring function

main() { echo "$(date '+%Y-%m-%d %H:%M:%S') - Running backup monitoring check" >> "$LOG_FILE" local alerts=0 if ! check_recent_backup; then alerts=$((alerts + 1)) fi if ! check_backup_errors; then alerts=$((alerts + 1)) fi if [ $alerts -eq 0 ]; then echo "$(date '+%Y-%m-%d %H:%M:%S') - Backup monitoring: All checks passed" >> "$LOG_FILE" fi return $alerts } main "$@"

Set monitoring script permissions

Configure permissions for the backup monitoring script.

sudo chown redis-backup:redis-backup /var/lib/redis-backup/scripts/monitor-backups.sh
sudo chmod 750 /var/lib/redis-backup/scripts/monitor-backups.sh
Security Warning: Store GPG passphrases securely with restrictive file permissions (600). Never commit passphrase files to version control systems. Consider using hardware security modules or key management services for production environments.

Verify your setup

# Check systemd timer status
sudo systemctl status redis-backup.timer

List scheduled timer executions

sudo systemctl list-timers redis-backup.timer

Check GPG key availability

sudo -u redis-backup gpg --list-keys

Verify latest backup

sudo -u redis-backup /var/lib/redis-backup/scripts/verify-backup.sh latest

Check backup log

sudo -u redis-backup tail -20 /var/lib/redis-backup/backup.log

List available backups

sudo -u redis-backup ls -la /var/lib/redis-backup/backups/

Test Redis connectivity

redis-cli ping

Common issues

SymptomCauseFix
GPG decryption failsIncorrect passphrase or corrupted keyVerify passphrase file and re-import GPG keys
Backup service fails to startPermission issues or missing directoriesCheck file ownership with ls -la /var/lib/redis-backup
Redis connection refused during backupRedis service down or wrong connection parametersVerify Redis status with sudo systemctl status redis
Timer not executing backupsSystemd timer not enabled or service misconfiguredCheck timer status with systemctl list-timers
Large backup filesRedis database size or insufficient compressionImplement backup rotation and check Redis memory usage
Restoration fails with permission deniedIncorrect file ownership on Redis data directoryEnsure Redis user owns data files: sudo chown redis:redis /var/lib/redis/dump.rdb

Next steps

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle infrastructure security hardening for businesses that depend on uptime. From initial setup to ongoing operations.