Backup & Recovery
Complete guide to backing up and recovering FiveM server data and configurations.
A comprehensive backup and recovery strategy is essential for any FiveM server. This guide covers everything from basic backup procedures to advanced disaster recovery planning.
Understanding What to Backup
Critical Data Categories
Server Files
- Server executable and artifacts
- Configuration files (server.cfg)
- Resource files and configurations
- Custom scripts and modifications
Database
- Player data and statistics
- Vehicle ownership records
- Property and housing data
- Economy and banking information
- Logs and transaction history
Runtime Data
- Current server state
- Player sessions
- Dynamic configurations
- Cache files
Backup Priority Levels
Priority 1 - Critical (Daily)
- Database (complete)
- server.cfg
- Custom resource configurations
- Player data
Priority 2 - Important (Weekly)
- All resources
- Server artifacts
- Log files
- Map files and assets
Priority 3 - Nice to Have (Monthly)
- Cache directories
- Temporary files
- Old log archives
Database Backup Strategies
Automated MySQL/MariaDB Backups
Linux Backup Script
#!/bin/bash
# backup_database.sh
# Configuration
DB_HOST="localhost"
DB_NAME="fivem"
DB_USER="fivem_user"
DB_PASS="password"
BACKUP_DIR="/backups/database"
RETENTION_DAYS=30
COMPRESS=true
# Discord notification (optional)
DISCORD_WEBHOOK=""
# Create backup directory
mkdir -p "$BACKUP_DIR"
# Generate backup filename
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/fivem_db_$DATE.sql"
# Log function
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$BACKUP_DIR/backup.log"
}
log "Starting database backup..."
# Create backup
if mysqldump -h"$DB_HOST" -u"$DB_USER" -p"$DB_PASS" \
--single-transaction \
--routines \
--triggers \
--hex-blob \
--default-character-set=utf8mb4 \
"$DB_NAME" > "$BACKUP_FILE"; then
log "Database backup created: $BACKUP_FILE"
# Compress backup
if [ "$COMPRESS" = true ]; then
gzip "$BACKUP_FILE"
BACKUP_FILE="$BACKUP_FILE.gz"
log "Backup compressed: $BACKUP_FILE"
fi
# Verify backup
if [ -f "$BACKUP_FILE" ] && [ -s "$BACKUP_FILE" ]; then
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
log "Backup completed successfully. Size: $BACKUP_SIZE"
# Send Discord notification
if [ -n "$DISCORD_WEBHOOK" ]; then
curl -H "Content-Type: application/json" \
-X POST \
-d "{\"content\": \"✅ Database backup completed: $BACKUP_SIZE\"}" \
"$DISCORD_WEBHOOK"
fi
else
log "ERROR: Backup verification failed"
exit 1
fi
else
log "ERROR: Database backup failed"
exit 1
fi
# Cleanup old backups
find "$BACKUP_DIR" -name "fivem_db_*.sql*" -mtime +$RETENTION_DAYS -delete
DELETED_COUNT=$(find "$BACKUP_DIR" -name "fivem_db_*.sql*" -mtime +$RETENTION_DAYS | wc -l)
if [ $DELETED_COUNT -gt 0 ]; then
log "Deleted $DELETED_COUNT old backup files"
fi
log "Backup process completed"
Windows PowerShell Script
# backup_database.ps1
param(
[string]$DBHost = "localhost",
[string]$DBName = "fivem",
[string]$DBUser = "fivem_user",
[string]$DBPassword = "password",
[string]$BackupDir = "C:\FiveM\backups\database",
[int]$RetentionDays = 30,
[switch]$Compress = $true
)
# Create backup directory
if (!(Test-Path $BackupDir)) {
New-Item -ItemType Directory -Path $BackupDir -Force
}
# Generate backup filename
$Date = Get-Date -Format "yyyyMMdd_HHmmss"
$BackupFile = Join-Path $BackupDir "fivem_db_$Date.sql"
# Log function
function Write-Log {
param([string]$Message)
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$LogMessage = "[$Timestamp] $Message"
Write-Host $LogMessage
$LogMessage | Out-File -FilePath (Join-Path $BackupDir "backup.log") -Append
}
Write-Log "Starting database backup..."
try {
# Create backup using mysqldump
$Arguments = @(
"-h$DBHost",
"-u$DBUser",
"-p$DBPassword",
"--single-transaction",
"--routines",
"--triggers",
"--hex-blob",
"--default-character-set=utf8mb4",
$DBName
)
$Process = Start-Process -FilePath "mysqldump" -ArgumentList $Arguments -RedirectStandardOutput $BackupFile -Wait -PassThru -NoNewWindow
if ($Process.ExitCode -eq 0) {
Write-Log "Database backup created: $BackupFile"
# Compress backup
if ($Compress) {
Compress-Archive -Path $BackupFile -DestinationPath "$BackupFile.zip"
Remove-Item $BackupFile
$BackupFile = "$BackupFile.zip"
Write-Log "Backup compressed: $BackupFile"
}
# Verify backup
if (Test-Path $BackupFile) {
$BackupSize = (Get-Item $BackupFile).Length / 1MB
Write-Log "Backup completed successfully. Size: $([math]::Round($BackupSize, 2)) MB"
} else {
throw "Backup file not found after creation"
}
} else {
throw "mysqldump failed with exit code: $($Process.ExitCode)"
}
# Cleanup old backups
$OldBackups = Get-ChildItem -Path $BackupDir -Name "fivem_db_*" | Where-Object { $_.CreationTime -lt (Get-Date).AddDays(-$RetentionDays) }
foreach ($OldBackup in $OldBackups) {
Remove-Item (Join-Path $BackupDir $OldBackup)
}
if ($OldBackups.Count -gt 0) {
Write-Log "Deleted $($OldBackups.Count) old backup files"
}
Write-Log "Backup process completed successfully"
} catch {
Write-Log "ERROR: $($_.Exception.Message)"
exit 1
}
Advanced Database Backup Features
Point-in-Time Recovery Setup
# Enable binary logging for point-in-time recovery
# Add to my.cnf/my.ini
[mysqld]
log-bin=mysql-bin
binlog_format=ROW
binlog_row_image=FULL
expire_logs_days=7
max_binlog_size=500M
# Backup script with binary log management
#!/bin/bash
# backup_with_binlog.sh
DB_NAME="fivem"
BACKUP_DIR="/backups/database"
BINLOG_DIR="/backups/binlogs"
# Full backup
mysqldump --single-transaction --flush-logs --master-data=2 \
--routines --triggers fivem > "$BACKUP_DIR/full_backup_$(date +%Y%m%d_%H%M%S).sql"
# Archive binary logs
mkdir -p "$BINLOG_DIR"
mysql -e "FLUSH LOGS;"
cp /var/lib/mysql/mysql-bin.* "$BINLOG_DIR/"
echo "Full backup and binary log archive completed"
Incremental Backup Strategy
#!/bin/bash
# incremental_backup.sh
FULL_BACKUP_DAY="Sunday"
CURRENT_DAY=$(date +%A)
BACKUP_DIR="/backups/database"
if [ "$CURRENT_DAY" = "$FULL_BACKUP_DAY" ]; then
# Full backup
echo "Performing full backup..."
mysqldump --single-transaction --flush-logs --master-data=2 \
--routines --triggers fivem > "$BACKUP_DIR/full_$(date +%Y%m%d).sql"
else
# Incremental backup (binary logs)
echo "Performing incremental backup..."
mkdir -p "$BACKUP_DIR/incremental/$(date +%Y%m%d)"
mysqlbinlog --start-datetime="$(date -d '1 day ago' '+%Y-%m-%d 00:00:00')" \
/var/lib/mysql/mysql-bin.* > "$BACKUP_DIR/incremental/$(date +%Y%m%d)/binlog_$(date +%H%M%S).sql"
fi
File System Backups
Complete Server Backup
#!/bin/bash
# backup_server.sh
SERVER_DIR="/opt/fivem"
BACKUP_ROOT="/backups/server"
EXCLUDE_FILE="/tmp/backup_exclude.txt"
# Create exclude list
cat > "$EXCLUDE_FILE" << EOF
*.log
*.tmp
cache/
temp/
node_modules/
.git/
*.pid
EOF
# Create timestamped backup directory
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="$BACKUP_ROOT/$DATE"
mkdir -p "$BACKUP_DIR"
echo "Starting full server backup..."
# Backup server files
tar -czf "$BACKUP_DIR/server_files.tar.gz" \
--exclude-from="$EXCLUDE_FILE" \
-C "$(dirname $SERVER_DIR)" \
"$(basename $SERVER_DIR)"
# Backup configuration separately
cp "$SERVER_DIR/server.cfg" "$BACKUP_DIR/"
cp -r "$SERVER_DIR/resources" "$BACKUP_DIR/resources_config"
# Create backup manifest
cat > "$BACKUP_DIR/backup_manifest.txt" << EOF
Backup Created: $(date)
Server Directory: $SERVER_DIR
Backup Type: Full Server Backup
Files Included:
- Server executables and artifacts
- Configuration files
- Resources and scripts
- Custom modifications
Files Excluded:
$(cat $EXCLUDE_FILE)
EOF
# Calculate backup size
BACKUP_SIZE=$(du -sh "$BACKUP_DIR" | cut -f1)
echo "Backup completed. Size: $BACKUP_SIZE"
# Cleanup
rm "$EXCLUDE_FILE"
# Retention cleanup (keep last 10 backups)
ls -t "$BACKUP_ROOT" | tail -n +11 | xargs -I {} rm -rf "$BACKUP_ROOT/{}"
Resource-Specific Backup
#!/bin/bash
# backup_resources.sh
RESOURCES_DIR="/opt/fivem/resources"
BACKUP_DIR="/backups/resources"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# Custom resources only (exclude default FiveM resources)
CUSTOM_RESOURCES=(
"esx_*"
"qb-*"
"custom_*"
"my_*"
)
mkdir -p "$BACKUP_DIR/$TIMESTAMP"
for pattern in "${CUSTOM_RESOURCES[@]}"; do
for resource in $RESOURCES_DIR/$pattern; do
if [ -d "$resource" ]; then
RESOURCE_NAME=$(basename "$resource")
echo "Backing up resource: $RESOURCE_NAME"
# Create resource backup
tar -czf "$BACKUP_DIR/$TIMESTAMP/${RESOURCE_NAME}.tar.gz" -C "$RESOURCES_DIR" "$RESOURCE_NAME"
# Extract configuration for quick reference
if [ -f "$resource/config.lua" ]; then
cp "$resource/config.lua" "$BACKUP_DIR/$TIMESTAMP/${RESOURCE_NAME}_config.lua"
fi
fi
done
done
echo "Resource backup completed: $BACKUP_DIR/$TIMESTAMP"
Automated Backup Solutions
Systemd Timer (Linux)
# /etc/systemd/system/fivem-backup.service
[Unit]
Description=FiveM Server Backup
After=mysql.service
[Service]
Type=oneshot
User=fivem
ExecStart=/opt/fivem/scripts/backup_database.sh
ExecStartPost=/opt/fivem/scripts/backup_server.sh
StandardOutput=journal
StandardError=journal
# /etc/systemd/system/fivem-backup.timer
[Unit]
Description=Run FiveM backup daily at 3 AM
Requires=fivem-backup.service
[Timer]
OnCalendar=daily
Persistent=true
AccuracySec=1min
[Install]
WantedBy=timers.target
# Enable and start the timer
sudo systemctl enable fivem-backup.timer
sudo systemctl start fivem-backup.timer
# Check timer status
sudo systemctl list-timers | grep fivem
Windows Task Scheduler
<!-- FiveM_Backup_Task.xml -->
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2">
<RegistrationInfo>
<Description>Daily FiveM server backup</Description>
</RegistrationInfo>
<Triggers>
<CalendarTrigger>
<StartBoundary>2024-01-01T03:00:00</StartBoundary>
<ExecutionTimeLimit>PT2H</ExecutionTimeLimit>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal>
<UserId>FiveM_Service</UserId>
<LogonType>ServiceAccount</LogonType>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>true</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>false</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession>
<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT2H</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions>
<Exec>
<Command>PowerShell.exe</Command>
<Arguments>-ExecutionPolicy Bypass -File "C:\FiveM\scripts\backup_database.ps1"</Arguments>
</Exec>
</Actions>
</Task>
Docker Backup Solution
# docker-compose.backup.yml
version: '3.8'
services:
fivem-backup:
image: alpine:latest
container_name: fivem-backup
volumes:
- fivem_data:/fivem_data:ro
- ./backups:/backups
- ./scripts:/scripts
environment:
- MYSQL_HOST=mysql
- MYSQL_USER=fivem
- MYSQL_PASSWORD=password
- MYSQL_DATABASE=fivem
command: |
sh -c "
apk add --no-cache mysql-client curl tar gzip &&
/scripts/docker_backup.sh
"
depends_on:
- mysql
profiles:
- backup
mysql:
image: mariadb:10.11
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: fivem
MYSQL_USER: fivem
MYSQL_PASSWORD: password
volumes:
- mysql_data:/var/lib/mysql
volumes:
fivem_data:
mysql_data:
#!/bin/sh
# docker_backup.sh
BACKUP_DIR="/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="$BACKUP_DIR/$TIMESTAMP"
mkdir -p "$BACKUP_PATH"
echo "Starting Docker-based backup..."
# Database backup
mysqldump -h"$MYSQL_HOST" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" \
--single-transaction --routines --triggers \
"$MYSQL_DATABASE" > "$BACKUP_PATH/database.sql"
# Compress database backup
gzip "$BACKUP_PATH/database.sql"
# FiveM data backup
tar -czf "$BACKUP_PATH/fivem_data.tar.gz" -C /fivem_data .
# Create backup summary
cat > "$BACKUP_PATH/backup_info.txt" << EOF
Backup Date: $(date)
Backup Type: Docker Container Backup
Database: $MYSQL_DATABASE
Host: $MYSQL_HOST
Files:
- database.sql.gz (MySQL dump)
- fivem_data.tar.gz (FiveM server data)
EOF
echo "Backup completed: $BACKUP_PATH"
# Cleanup old backups (keep last 7 days)
find "$BACKUP_DIR" -maxdepth 1 -type d -name "20*" -mtime +7 -exec rm -rf {} \;
Recovery Procedures
Database Recovery
Complete Database Restore
#!/bin/bash
# restore_database.sh
BACKUP_FILE="$1"
DB_NAME="fivem"
DB_USER="fivem_user"
DB_PASS="password"
if [ -z "$BACKUP_FILE" ]; then
echo "Usage: $0 <backup_file>"
echo "Available backups:"
ls -la /backups/database/
exit 1
fi
if [ ! -f "$BACKUP_FILE" ]; then
echo "Backup file not found: $BACKUP_FILE"
exit 1
fi
echo "WARNING: This will completely replace the current database!"
read -p "Are you sure you want to continue? (yes/no): " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
echo "Recovery cancelled"
exit 0
fi
echo "Starting database recovery..."
# Create safety backup of current database
SAFETY_BACKUP="/tmp/safety_backup_$(date +%Y%m%d_%H%M%S).sql"
mysqldump -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" > "$SAFETY_BACKUP"
echo "Safety backup created: $SAFETY_BACKUP"
# Drop existing database
mysql -u"$DB_USER" -p"$DB_PASS" -e "DROP DATABASE IF EXISTS $DB_NAME;"
mysql -u"$DB_USER" -p"$DB_PASS" -e "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# Restore from backup
if [[ "$BACKUP_FILE" == *.gz ]]; then
gunzip -c "$BACKUP_FILE" | mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME"
else
mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" < "$BACKUP_FILE"
fi
if [ $? -eq 0 ]; then
echo "Database recovery completed successfully"
echo "Safety backup available at: $SAFETY_BACKUP"
else
echo "Database recovery failed!"
echo "Restoring from safety backup..."
mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" < "$SAFETY_BACKUP"
exit 1
fi
Point-in-Time Recovery
#!/bin/bash
# point_in_time_recovery.sh
FULL_BACKUP="$1"
RECOVERY_TIME="$2" # Format: YYYY-MM-DD HH:MM:SS
BINLOG_DIR="/backups/binlogs"
if [ -z "$FULL_BACKUP" ] || [ -z "$RECOVERY_TIME" ]; then
echo "Usage: $0 <full_backup_file> <recovery_time>"
echo "Example: $0 /backups/full_20240724.sql '2024-07-24 14:30:00'"
exit 1
fi
echo "Starting point-in-time recovery to: $RECOVERY_TIME"
# Restore full backup
echo "Restoring full backup..."
mysql -u fivem_user -p fivem < "$FULL_BACKUP"
# Apply binary logs up to recovery time
echo "Applying binary logs..."
for binlog in "$BINLOG_DIR"/mysql-bin.*; do
if [ -f "$binlog" ]; then
echo "Processing: $binlog"
mysqlbinlog --stop-datetime="$RECOVERY_TIME" "$binlog" | mysql -u fivem_user -p fivem
fi
done
echo "Point-in-time recovery completed"
Server File Recovery
#!/bin/bash
# restore_server.sh
BACKUP_PATH="$1"
SERVER_DIR="/opt/fivem"
RESTORE_TYPE="$2" # full, config, resources
if [ -z "$BACKUP_PATH" ]; then
echo "Usage: $0 <backup_path> [restore_type]"
echo "Restore types: full, config, resources"
echo "Available backups:"
ls -la /backups/server/
exit 1
fi
if [ ! -d "$BACKUP_PATH" ]; then
echo "Backup path not found: $BACKUP_PATH"
exit 1
fi
echo "Starting server file recovery..."
echo "Backup: $BACKUP_PATH"
echo "Restore type: ${RESTORE_TYPE:-full}"
# Stop server
echo "Stopping FiveM server..."
systemctl stop fivem
case "${RESTORE_TYPE:-full}" in
"full")
echo "Performing full server restore..."
# Create safety backup
SAFETY_DIR="/tmp/server_safety_$(date +%Y%m%d_%H%M%S)"
mv "$SERVER_DIR" "$SAFETY_DIR"
echo "Current server backed up to: $SAFETY_DIR"
# Extract full backup
mkdir -p "$SERVER_DIR"
tar -xzf "$BACKUP_PATH/server_files.tar.gz" -C "$(dirname $SERVER_DIR)"
;;
"config")
echo "Restoring configuration files..."
cp "$BACKUP_PATH/server.cfg" "$SERVER_DIR/"
if [ -d "$BACKUP_PATH/resources_config" ]; then
cp -r "$BACKUP_PATH/resources_config"/* "$SERVER_DIR/resources/"
fi
;;
"resources")
echo "Restoring resources..."
if [ -d "$BACKUP_PATH/resources_config" ]; then
rm -rf "$SERVER_DIR/resources"
cp -r "$BACKUP_PATH/resources_config" "$SERVER_DIR/resources"
fi
;;
*)
echo "Invalid restore type: $RESTORE_TYPE"
exit 1
;;
esac
# Set permissions
chown -R fivem:fivem "$SERVER_DIR"
chmod +x "$SERVER_DIR/FXServer"
# Start server
echo "Starting FiveM server..."
systemctl start fivem
echo "Server recovery completed"
Disaster Recovery Planning
Recovery Time Objectives (RTO) Planning
# disaster_recovery_plan.sh
# Define recovery objectives
RTO_CRITICAL=15 # minutes - Critical systems must be back online
RTO_IMPORTANT=60 # minutes - Important systems
RTO_NORMAL=240 # minutes - Normal operations
# Recovery procedures by priority
critical_recovery() {
echo "CRITICAL RECOVERY - RTO: $RTO_CRITICAL minutes"
# 1. Restore database from latest backup (5 min)
restore_database.sh /backups/database/latest.sql.gz
# 2. Start server with minimal configuration (2 min)
start_minimal_server.sh
# 3. Verify basic functionality (5 min)
verify_server_health.sh
# 4. Enable essential resources only (3 min)
enable_essential_resources.sh
}
important_recovery() {
echo "IMPORTANT RECOVERY - RTO: $RTO_IMPORTANT minutes"
# 1. Restore full server configuration (20 min)
restore_server.sh /backups/server/latest full
# 2. Restore all resources (20 min)
restore_resources.sh
# 3. Full system verification (20 min)
full_system_test.sh
}
normal_recovery() {
echo "NORMAL RECOVERY - RTO: $RTO_NORMAL minutes"
# 1. Restore from backup with full verification
# 2. Apply any missing updates
# 3. Restore custom configurations
# 4. Full testing and optimization
}
Monitoring and Alerting
#!/bin/bash
# backup_monitor.sh
BACKUP_DIR="/backups"
ALERT_EMAIL="[email protected]"
DISCORD_WEBHOOK="https://discord.com/api/webhooks/..."
check_backup_health() {
local status="OK"
local issues=()
# Check if backups are current (within 25 hours)
LATEST_DB_BACKUP=$(find "$BACKUP_DIR/database" -name "*.sql*" -mtime -1 | head -1)
if [ -z "$LATEST_DB_BACKUP" ]; then
issues+=("Database backup is outdated (>24h)")
status="WARNING"
fi
# Check backup sizes (detect corruption)
if [ -n "$LATEST_DB_BACKUP" ]; then
BACKUP_SIZE=$(stat -c%s "$LATEST_DB_BACKUP")
if [ "$BACKUP_SIZE" -lt 1048576 ]; then # Less than 1MB
issues+=("Database backup seems too small: $BACKUP_SIZE bytes")
status="ERROR"
fi
fi
# Check disk space
BACKUP_DISK_USAGE=$(df "$BACKUP_DIR" | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$BACKUP_DISK_USAGE" -gt 90 ]; then
issues+=("Backup disk usage critical: ${BACKUP_DISK_USAGE}%")
status="ERROR"
fi
# Send alerts if issues found
if [ ${#issues[@]} -gt 0 ]; then
send_alert "$status" "${issues[@]}"
fi
echo "Backup health check: $status"
for issue in "${issues[@]}"; do
echo " - $issue"
done
}
send_alert() {
local status="$1"
shift
local issues=("$@")
local message="🚨 Backup Alert - $status\n\n"
for issue in "${issues[@]}"; do
message+="• $issue\n"
done
# Discord notification
if [ -n "$DISCORD_WEBHOOK" ]; then
curl -H "Content-Type: application/json" \
-X POST \
-d "{\"content\": \"$message\"}" \
"$DISCORD_WEBHOOK"
fi
# Email notification
if [ -n "$ALERT_EMAIL" ]; then
echo -e "$message" | mail -s "FiveM Backup Alert - $status" "$ALERT_EMAIL"
fi
}
# Run health check
check_backup_health
Testing Recovery Procedures
#!/bin/bash
# test_recovery.sh
TEST_ENV="/opt/fivem/test"
BACKUP_TO_TEST="$1"
if [ -z "$BACKUP_TO_TEST" ]; then
echo "Usage: $0 <backup_path>"
exit 1
fi
echo "Testing recovery procedures with backup: $BACKUP_TO_TEST"
# Create test environment
mkdir -p "$TEST_ENV"
cd "$TEST_ENV"
# Test database restore
echo "Testing database restore..."
TEST_DB="fivem_test"
mysql -e "CREATE DATABASE $TEST_DB;"
if [[ "$BACKUP_TO_TEST" == *.gz ]]; then
gunzip -c "$BACKUP_TO_TEST" | mysql "$TEST_DB"
else
mysql "$TEST_DB" < "$BACKUP_TO_TEST"
fi
# Verify database integrity
TABLES_COUNT=$(mysql -sN -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$TEST_DB'")
echo "Restored tables: $TABLES_COUNT"
if [ "$TABLES_COUNT" -eq 0 ]; then
echo "❌ Database restore test failed"
exit 1
else
echo "✅ Database restore test passed"
fi
# Cleanup test database
mysql -e "DROP DATABASE $TEST_DB;"
# Test server file restore (if applicable)
# ... additional tests ...
echo "Recovery test completed successfully"
This comprehensive backup and recovery guide provides everything needed to protect your FiveM server data and ensure quick recovery in case of disasters.