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 rsyncCreate 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-backupGPG 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.shCreate 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.shAutomated 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=EPERMCreate 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.targetEnable 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.timerTest 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.logBackup 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 latestCreate 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.shConfigure 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/chownMonitoring 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.shVerify 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 pingCommon issues
| Symptom | Cause | Fix |
|---|---|---|
| GPG decryption fails | Incorrect passphrase or corrupted key | Verify passphrase file and re-import GPG keys |
| Backup service fails to start | Permission issues or missing directories | Check file ownership with ls -la /var/lib/redis-backup |
| Redis connection refused during backup | Redis service down or wrong connection parameters | Verify Redis status with sudo systemctl status redis |
| Timer not executing backups | Systemd timer not enabled or service misconfigured | Check timer status with systemctl list-timers |
| Large backup files | Redis database size or insufficient compression | Implement backup rotation and check Redis memory usage |
| Restoration fails with permission denied | Incorrect file ownership on Redis data directory | Ensure 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
#!/usr/bin/env bash
set -euo pipefail
# Redis GPG Backup Configuration Script
# Configures automated encrypted Redis backups using GPG
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Configuration
BACKUP_USER="redis-backup"
BACKUP_HOME="/var/lib/redis-backup"
REDIS_HOST="${1:-127.0.0.1}"
REDIS_PORT="${2:-6379}"
GPG_KEY_EMAIL="redis-backup@$(hostname)"
RETENTION_DAYS="${3:-30}"
# Logging function
log() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a /var/log/redis-backup-install.log
}
error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
exit 1
}
warn() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
# Usage information
usage() {
echo "Usage: $0 [redis_host] [redis_port] [retention_days]"
echo " redis_host: Redis server IP (default: 127.0.0.1)"
echo " redis_port: Redis server port (default: 6379)"
echo " retention_days: Backup retention in days (default: 30)"
exit 1
}
# Cleanup on error
cleanup() {
if [[ $? -ne 0 ]]; then
warn "Installation failed. Cleaning up..."
userdel -r "$BACKUP_USER" 2>/dev/null || true
rm -rf "$BACKUP_HOME" 2>/dev/null || true
rm -f /etc/cron.d/redis-backup 2>/dev/null || true
fi
}
trap cleanup ERR
# Check if running as root
check_root() {
if [[ $EUID -ne 0 ]]; then
error "This script must be run as root"
fi
}
# Detect distribution and set package manager
detect_distro() {
if [ ! -f /etc/os-release ]; then
error "Cannot detect distribution. /etc/os-release not found."
fi
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
;;
fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
;;
*)
error "Unsupported distribution: $ID"
;;
esac
log "Detected distribution: $ID ($VERSION_ID)"
}
# Install required packages
install_packages() {
log "[2/8] Installing required packages..."
$PKG_UPDATE
case "$PKG_MGR" in
apt)
$PKG_INSTALL gnupg2 redis-tools gzip rsync cron
;;
dnf|yum)
$PKG_INSTALL gnupg2 redis gzip rsync cronie
systemctl enable crond
systemctl start crond
;;
esac
log "Required packages installed successfully"
}
# Create backup user and directories
create_backup_user() {
log "[3/8] Creating backup user and directories..."
if id "$BACKUP_USER" &>/dev/null; then
warn "User $BACKUP_USER already exists"
else
useradd -r -s /bin/bash -d "$BACKUP_HOME" -m "$BACKUP_USER"
log "Created user: $BACKUP_USER"
fi
mkdir -p "$BACKUP_HOME"/{scripts,backups,keys,logs}
chown -R "$BACKUP_USER":"$BACKUP_USER" "$BACKUP_HOME"
chmod 750 "$BACKUP_HOME"
chmod 700 "$BACKUP_HOME"/keys
log "Backup directories created with proper permissions"
}
# Generate GPG key
generate_gpg_key() {
log "[4/8] Generating GPG key for encryption..."
sudo -u "$BACKUP_USER" bash -c "
export GNUPGHOME='$BACKUP_HOME/keys'
if gpg --list-keys '$GPG_KEY_EMAIL' &>/dev/null; then
echo 'GPG key already exists'
else
cat > /tmp/gpg_batch <<EOF
%echo Generating Redis backup GPG key
Key-Type: RSA
Key-Length: 4096
Name-Real: Redis Backup
Name-Email: $GPG_KEY_EMAIL
Expire-Date: 0
Passphrase: \$(openssl rand -base64 32)
%commit
%echo Done
EOF
gpg --batch --generate-key /tmp/gpg_batch
rm /tmp/gpg_batch
fi
"
log "GPG key generated successfully"
}
# Create backup script
create_backup_script() {
log "[5/8] Creating backup script..."
cat > "$BACKUP_HOME/scripts/redis-backup.sh" <<'EOF'
#!/bin/bash
set -euo pipefail
# Configuration
BACKUP_DIR="/var/lib/redis-backup/backups"
LOG_FILE="/var/lib/redis-backup/logs/backup.log"
GPG_HOME="/var/lib/redis-backup/keys"
REDIS_HOST="127.0.0.1"
REDIS_PORT="6379"
RETENTION_DAYS="30"
# Export GPG home
export GNUPGHOME="$GPG_HOME"
# Logging function
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
# Create RDB backup
create_rdb_backup() {
local backup_file="$1"
if redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" ping >/dev/null 2>&1; then
redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" --rdb "$backup_file"
return $?
else
log_message "ERROR: Cannot connect to Redis server"
return 1
fi
}
# 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"
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 --trust-model always --symmetric --cipher-algo AES256 --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
# Cleanup old backups
find "$BACKUP_DIR" -name "redis_backup_*.rdb.gz.gpg" -mtime +${RETENTION_DAYS} -delete
log_message "Cleaned up backups older than ${RETENTION_DAYS} days"
log_message "Backup completed successfully: $encrypted_backup"
}
# Execute backup
perform_backup
EOF
chown "$BACKUP_USER":"$BACKUP_USER" "$BACKUP_HOME/scripts/redis-backup.sh"
chmod 750 "$BACKUP_HOME/scripts/redis-backup.sh"
log "Backup script created successfully"
}
# Setup cron job
setup_cron() {
log "[6/8] Setting up automated backup schedule..."
cat > /etc/cron.d/redis-backup <<EOF
# Redis encrypted backup - runs daily at 2 AM
0 2 * * * $BACKUP_USER $BACKUP_HOME/scripts/redis-backup.sh
EOF
chmod 644 /etc/cron.d/redis-backup
log "Cron job configured for daily backups at 2 AM"
}
# Test backup functionality
test_backup() {
log "[7/8] Testing backup functionality..."
if redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" ping >/dev/null 2>&1; then
log "Redis server connection successful"
# Run test backup
sudo -u "$BACKUP_USER" "$BACKUP_HOME/scripts/redis-backup.sh"
if [ -n "$(find "$BACKUP_HOME/backups" -name "*.gpg" -mmin -2 2>/dev/null)" ]; then
log "Test backup created successfully"
else
error "Test backup failed"
fi
else
warn "Redis server not accessible at $REDIS_HOST:$REDIS_PORT - backup script created but not tested"
fi
}
# Display summary
show_summary() {
log "[8/8] Installation completed successfully!"
echo
echo -e "${BLUE}=== Redis Backup Configuration Summary ===${NC}"
echo -e "Backup User: ${GREEN}$BACKUP_USER${NC}"
echo -e "Backup Directory: ${GREEN}$BACKUP_HOME/backups${NC}"
echo -e "Script Location: ${GREEN}$BACKUP_HOME/scripts/redis-backup.sh${NC}"
echo -e "GPG Keys: ${GREEN}$BACKUP_HOME/keys${NC}"
echo -e "Logs: ${GREEN}$BACKUP_HOME/logs/backup.log${NC}"
echo -e "Schedule: ${GREEN}Daily at 2 AM${NC}"
echo -e "Retention: ${GREEN}$RETENTION_DAYS days${NC}"
echo
echo -e "${YELLOW}Manual backup command:${NC}"
echo -e "sudo -u $BACKUP_USER $BACKUP_HOME/scripts/redis-backup.sh"
echo
echo -e "${YELLOW}View logs:${NC}"
echo -e "tail -f $BACKUP_HOME/logs/backup.log"
}
# Main execution
main() {
log "[1/8] Starting Redis backup configuration..."
check_root
detect_distro
install_packages
create_backup_user
generate_gpg_key
create_backup_script
setup_cron
test_backup
show_summary
}
# Validate arguments
if [[ $# -gt 3 ]]; then
usage
fi
main "$@"
Review the script before running. Execute with: bash install.sh