Configure automated GitLab backups with disaster recovery procedures and automated restoration scripts. Includes monitoring, alerting, and production-grade recovery workflows.
Prerequisites
- GitLab CE/EE installed
- AWS S3 bucket or compatible storage
- Root or sudo access
- At least 50GB free disk space
What this solves
GitLab contains critical source code, issues, merge requests, and CI/CD configurations that need reliable backup and recovery systems. This tutorial sets up automated backups with disaster recovery procedures, including automated restoration scripts and monitoring to ensure your GitLab instance can be quickly restored in case of hardware failure, data corruption, or accidental deletion.
Step-by-step configuration
Update system packages
Start by updating your package manager to ensure you get the latest versions of backup tools.
sudo apt update && sudo apt upgrade -y
sudo apt install -y rsync gpg awscli
Configure GitLab backup settings
Configure GitLab's built-in backup system with retention policies and custom backup paths.
gitlab_rails['backup_path'] = '/var/opt/gitlab/backups'
gitlab_rails['backup_keep_time'] = 604800
gitlab_rails['backup_gitaly_backup_path'] = '/opt/gitlab/embedded/bin/gitaly-backup'
gitlab_rails['backup_archive_permissions'] = 0644
gitlab_rails['backup_pg_schema'] = 'public'
gitlab_rails['backup_upload_connection'] = {
'provider' => 'AWS',
'region' => 'eu-west-1',
'aws_access_key_id' => 'your-access-key',
'aws_secret_access_key' => 'your-secret-key'
}
gitlab_rails['backup_upload_remote_directory'] = 'gitlab-backups'
gitlab_rails['backup_multipart_chunk_size'] = 104857600
gitlab_rails['backup_encryption'] = 'AES256'
Create backup directories and set permissions
Create the backup directory structure with proper ownership and permissions for GitLab.
sudo mkdir -p /var/opt/gitlab/backups
sudo mkdir -p /etc/gitlab/backup-scripts
sudo mkdir -p /var/log/gitlab-backup
sudo chown -R git:git /var/opt/gitlab/backups
sudo chmod 755 /var/opt/gitlab/backups
sudo chown -R root:root /etc/gitlab/backup-scripts
sudo chmod 755 /etc/gitlab/backup-scripts
Create automated backup script
Create a comprehensive backup script that handles GitLab data, configuration files, and SSL certificates.
#!/bin/bash
GitLab Automated Backup Script
Version: 1.0
set -euo pipefail
Configuration
BACKUP_DIR="/var/opt/gitlab/backups"
CONFIG_BACKUP_DIR="/var/opt/gitlab/config-backups"
LOG_FILE="/var/log/gitlab-backup/backup.log"
RETENTION_DAYS=7
S3_BUCKET="gitlab-backups"
ENCRYPTION_KEY="/etc/gitlab/backup-encryption.key"
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
Create directories
mkdir -p "$CONFIG_BACKUP_DIR"
mkdir -p "$(dirname "$LOG_FILE")"
Logging function
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
Error handling
handle_error() {
log "ERROR: Backup failed at step: $1"
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"🔴 GitLab backup failed: '"$1"'"}' \
"$SLACK_WEBHOOK" 2>/dev/null || true
exit 1
}
Start backup process
log "Starting GitLab backup process"
Stop GitLab services temporarily for consistent backup
log "Stopping GitLab services for consistent backup"
sudo gitlab-ctl stop unicorn || handle_error "stopping unicorn"
sudo gitlab-ctl stop puma || handle_error "stopping puma"
sudo gitlab-ctl stop sidekiq || handle_error "stopping sidekiq"
Create GitLab data backup
log "Creating GitLab data backup"
sudo gitlab-backup create BACKUP=gitlab_$(date +%Y%m%d_%H%M%S) || handle_error "creating data backup"
Backup GitLab configuration files
log "Backing up GitLab configuration"
CONFIG_BACKUP_FILE="$CONFIG_BACKUP_DIR/gitlab-config-$(date +%Y%m%d_%H%M%S).tar.gz"
sudo tar -czf "$CONFIG_BACKUP_FILE" \
/etc/gitlab/gitlab.rb \
/etc/gitlab/gitlab-secrets.json \
/var/opt/gitlab/nginx/conf \
/etc/ssl/certs/gitlab* \
/etc/ssl/private/gitlab* 2>/dev/null || true
Encrypt backups
log "Encrypting backups"
for backup_file in "$BACKUP_DIR"/*.tar; do
if [[ -f "$backup_file" && ! -f "${backup_file}.gpg" ]]; then
gpg --cipher-algo AES256 --compress-algo 1 --s2k-cipher-algo AES256 \
--s2k-digest-algo SHA512 --s2k-mode 3 --s2k-count 65536 \
--symmetric --output "${backup_file}.gpg" "$backup_file" < "$ENCRYPTION_KEY"
rm "$backup_file"
log "Encrypted backup: $(basename "${backup_file}.gpg")"
fi
done
Upload to S3
log "Uploading backups to S3"
aws s3 sync "$BACKUP_DIR" "s3://$S3_BUCKET/data/" --exclude "" --include ".gpg"
aws s3 sync "$CONFIG_BACKUP_DIR" "s3://$S3_BUCKET/config/"
Clean up old backups
log "Cleaning up old local backups"
find "$BACKUP_DIR" -name "*.gpg" -mtime +$RETENTION_DAYS -delete
find "$CONFIG_BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
Start GitLab services
log "Starting GitLab services"
sudo gitlab-ctl start || handle_error "starting services"
Verify backup integrity
log "Verifying backup integrity"
latest_backup=$(ls -t "$BACKUP_DIR"/*.gpg 2>/dev/null | head -1 || echo "")
if [[ -n "$latest_backup" ]]; then
gpg --list-packets "$latest_backup" >/dev/null 2>&1 || handle_error "backup integrity check"
log "Backup integrity verified: $(basename "$latest_backup")"
else
handle_error "no backup files found"
fi
Send success notification
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"✅ GitLab backup completed successfully"}' \
"$SLACK_WEBHOOK" 2>/dev/null || true
log "GitLab backup process completed successfully"
Create disaster recovery restoration script
Create an automated restoration script for disaster recovery scenarios.
#!/bin/bash
GitLab Automated Restoration Script
Version: 1.0
set -euo pipefail
Configuration
BACKUP_DIR="/var/opt/gitlab/backups"
CONFIG_BACKUP_DIR="/var/opt/gitlab/config-backups"
LOG_FILE="/var/log/gitlab-backup/restore.log"
S3_BUCKET="gitlab-backups"
ENCRYPTION_KEY="/etc/gitlab/backup-encryption.key"
Logging function
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
Error handling
handle_error() {
log "ERROR: Restoration failed at step: $1"
exit 1
}
Check if backup timestamp provided
if [[ $# -eq 0 ]]; then
log "Usage: $0 "
log "Available backups:"
aws s3 ls "s3://$S3_BUCKET/data/" | grep ".gpg" | awk '{print $4}' | sed 's/gitlab_//g' | sed 's/_.*\.tar\.gpg//g' | sort -u
exit 1
fi
BACKUP_TIMESTAMP=$1
BACKUP_FILE="gitlab_${BACKUP_TIMESTAMP}_gitlab_backup.tar"
log "Starting GitLab restoration process for backup: $BACKUP_TIMESTAMP"
Download backup from S3
log "Downloading backup from S3"
aws s3 cp "s3://$S3_BUCKET/data/${BACKUP_FILE}.gpg" "$BACKUP_DIR/${BACKUP_FILE}.gpg" || handle_error "downloading backup"
Download latest config backup
log "Downloading configuration backup"
latest_config=$(aws s3 ls "s3://$S3_BUCKET/config/" | sort | tail -n 1 | awk '{print $4}')
aws s3 cp "s3://$S3_BUCKET/config/$latest_config" "$CONFIG_BACKUP_DIR/$latest_config" || handle_error "downloading config"
Decrypt backup
log "Decrypting backup file"
gpg --batch --yes --passphrase-file "$ENCRYPTION_KEY" \
--output "$BACKUP_DIR/$BACKUP_FILE" \
--decrypt "$BACKUP_DIR/${BACKUP_FILE}.gpg" || handle_error "decrypting backup"
Stop GitLab services
log "Stopping GitLab services"
sudo gitlab-ctl stop || handle_error "stopping GitLab services"
Restore configuration files
log "Restoring GitLab configuration"
sudo tar -xzf "$CONFIG_BACKUP_DIR/$latest_config" -C / || handle_error "restoring configuration"
Reconfigure GitLab with restored config
log "Reconfiguring GitLab"
sudo gitlab-ctl reconfigure || handle_error "reconfiguring GitLab"
Restore GitLab data
log "Restoring GitLab data"
BACKUP_NAME=$(basename "$BACKUP_FILE" .tar)
sudo gitlab-backup restore BACKUP="$BACKUP_NAME" || handle_error "restoring data"
Start GitLab services
log "Starting GitLab services"
sudo gitlab-ctl start || handle_error "starting GitLab services"
Run GitLab check
log "Running GitLab system check"
sudo gitlab-rake gitlab:check SANITIZE=true || handle_error "GitLab system check"
Clean up temporary files
rm -f "$BACKUP_DIR/${BACKUP_FILE}.gpg" "$BACKUP_DIR/$BACKUP_FILE"
log "GitLab restoration completed successfully for backup: $BACKUP_TIMESTAMP"
Set up backup encryption
Create an encryption key for securing backups at rest and in transit.
sudo openssl rand -base64 32 > /etc/gitlab/backup-encryption.key
sudo chmod 600 /etc/gitlab/backup-encryption.key
sudo chown root:root /etc/gitlab/backup-encryption.key
Make backup scripts executable
Set proper permissions for the backup and restoration scripts.
sudo chmod +x /etc/gitlab/backup-scripts/gitlab-backup.sh
sudo chmod +x /etc/gitlab/backup-scripts/gitlab-restore.sh
sudo chown root:root /etc/gitlab/backup-scripts/*.sh
Configure automated backup scheduling
Set up systemd timers for automated daily backups with proper service definition.
[Unit]
Description=GitLab Backup Service
After=network.target
Wants=network-online.target
[Service]
Type=oneshot
User=root
ExecStart=/etc/gitlab/backup-scripts/gitlab-backup.sh
TimeoutSec=7200
StandardOutput=journal
StandardError=journal
Create backup timer
Configure the systemd timer to run backups daily at 2 AM.
[Unit]
Description=GitLab Backup Timer
Requires=gitlab-backup.service
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=300
AccuracySec=1min
[Install]
WantedBy=timers.target
Enable and start backup automation
Enable the systemd timer and reload GitLab configuration with backup settings.
sudo systemctl daemon-reload
sudo systemctl enable gitlab-backup.timer
sudo systemctl start gitlab-backup.timer
sudo gitlab-ctl reconfigure
Set up backup monitoring script
Create a monitoring script that checks backup health and sends alerts.
#!/bin/bash
GitLab Backup Monitoring Script
set -euo pipefail
BACKUP_DIR="/var/opt/gitlab/backups"
LOG_FILE="/var/log/gitlab-backup/monitor.log"
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
MAX_AGE_HOURS=26
Logging function
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
Check latest backup age
latest_backup=$(find "$BACKUP_DIR" -name "*.gpg" -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)
if [[ -z "$latest_backup" ]]; then
log "WARNING: No backup files found"
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"⚠️ GitLab backup monitoring: No backup files found"}' \
"$SLACK_WEBHOOK" 2>/dev/null || true
exit 1
fi
Calculate backup age
backup_time=$(stat -c %Y "$latest_backup")
current_time=$(date +%s)
age_hours=$(( (current_time - backup_time) / 3600 ))
log "Latest backup: $(basename "$latest_backup"), Age: ${age_hours} hours"
if [[ $age_hours -gt $MAX_AGE_HOURS ]]; then
log "ALERT: Backup is older than $MAX_AGE_HOURS hours"
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"🔴 GitLab backup is '"$age_hours"' hours old (max: '"$MAX_AGE_HOURS"')"}' \
"$SLACK_WEBHOOK" 2>/dev/null || true
exit 1
fi
Test backup integrity
if gpg --list-packets "$latest_backup" >/dev/null 2>&1; then
log "Backup integrity check passed"
else
log "ALERT: Backup integrity check failed"
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"🔴 GitLab backup integrity check failed"}' \
"$SLACK_WEBHOOK" 2>/dev/null || true
exit 1
fi
Check disk space
available_space=$(df "$BACKUP_DIR" | awk 'NR==2 {print $4}')
if [[ $available_space -lt 10485760 ]]; then # 10GB in KB
log "ALERT: Low disk space in backup directory"
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"⚠️ GitLab backup directory has less than 10GB free space"}' \
"$SLACK_WEBHOOK" 2>/dev/null || true
fi
log "Backup monitoring completed successfully"
Set up monitoring automation
Create systemd service and timer for backup monitoring every 4 hours.
sudo chmod +x /etc/gitlab/backup-scripts/backup-monitor.sh
Create monitoring service
sudo tee /etc/systemd/system/gitlab-backup-monitor.service > /dev/null <Create monitoring timer
sudo tee /etc/systemd/system/gitlab-backup-monitor.timer > /dev/null <--* 00,06,12,18:30:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable gitlab-backup-monitor.timer
sudo systemctl start gitlab-backup-monitor.timer
Create disaster recovery documentation
Document the disaster recovery procedures for your team.
# GitLab Disaster Recovery Procedures
Quick Recovery Steps
- Identify backup timestamp to restore:
aws s3 ls s3://gitlab-backups/data/ | grep .gpg
- Run restoration script:
sudo /etc/gitlab/backup-scripts/gitlab-restore.sh YYYYMMDD_HHMMSS
- Verify restoration:
sudo gitlab-rake gitlab:check
Emergency Contacts
- Primary Admin: admin@example.com
- Secondary Admin: backup-admin@example.com
- Cloud Provider Support: +1-xxx-xxx-xxxx
Recovery Time Objectives
- RTO: 4 hours
- RPO: 24 hours
Infrastructure Requirements
- Minimum 4 CPU cores
- 8GB RAM
- 100GB storage
- Ubuntu 24.04/Debian 12/AlmaLinux 9/Rocky Linux 9
Testing Schedule
- Monthly restoration tests
- Quarterly full disaster recovery drills
Verify your setup
Test your backup and recovery system to ensure it works correctly.
# Check backup timer status
sudo systemctl status gitlab-backup.timer
Check monitoring timer status
sudo systemctl status gitlab-backup-monitor.timer
Run manual backup test
sudo /etc/gitlab/backup-scripts/gitlab-backup.sh
Check backup files
ls -la /var/opt/gitlab/backups/
View backup logs
sudo tail -f /var/log/gitlab-backup/backup.log
Test monitoring script
sudo /etc/gitlab/backup-scripts/backup-monitor.sh
Check GitLab status
sudo gitlab-ctl status
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Backup script fails with permission denied | Incorrect file ownership or permissions | sudo chown root:root /etc/gitlab/backup-scripts/.sh && sudo chmod +x /etc/gitlab/backup-scripts/.sh |
| S3 upload fails with authentication error | Invalid AWS credentials | Configure aws configure or verify IAM permissions |
| Backup encryption fails | Missing or invalid encryption key | Regenerate key with sudo openssl rand -base64 32 > /etc/gitlab/backup-encryption.key |
| GitLab services don't start after restore | Configuration mismatch | Run sudo gitlab-ctl reconfigure && sudo gitlab-ctl restart |
| Timer not running backups | Systemd timer not enabled | sudo systemctl enable gitlab-backup.timer && sudo systemctl start gitlab-backup.timer |
| Monitoring alerts not working | Incorrect Slack webhook URL | Update webhook URL in monitoring scripts and test with curl |
Next steps
- Configure GitLab LDAP authentication and user management to integrate with enterprise directories
- Integrate GitLab with Kubernetes for automated deployments for CI/CD pipeline setup
- Set up GitLab high availability with PostgreSQL clustering for enterprise deployments
- Configure GitLab Runner autoscaling with Kubernetes for dynamic CI/CD capacity
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# GitLab Backup and Disaster Recovery Setup Script
# Version: 1.0
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Configuration
GITLAB_USER="git"
BACKUP_BASE_DIR="/var/opt/gitlab/backups"
CONFIG_BACKUP_DIR="/var/opt/gitlab/config-backups"
SCRIPT_DIR="/etc/gitlab/backup-scripts"
LOG_DIR="/var/log/gitlab-backup"
RETENTION_DAYS="${RETENTION_DAYS:-7}"
# Functions
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}
warn() {
echo -e "${YELLOW}[WARNING] $1${NC}"
}
error() {
echo -e "${RED}[ERROR] $1${NC}"
}
cleanup() {
if [ $? -ne 0 ]; then
error "Installation failed. Cleaning up..."
rm -rf "$SCRIPT_DIR/gitlab-backup.sh" 2>/dev/null || true
fi
}
trap cleanup ERR
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -r, --retention-days DAYS Set backup retention period (default: 7)"
echo " -h, --help Show this help message"
echo ""
echo "Environment variables:"
echo " AWS_ACCESS_KEY_ID AWS access key for S3 uploads"
echo " AWS_SECRET_ACCESS_KEY AWS secret key for S3 uploads"
echo " AWS_REGION AWS region (default: us-east-1)"
echo " S3_BUCKET S3 bucket name for backups"
exit 1
}
check_prerequisites() {
log "[1/10] Checking prerequisites..."
if [ "$EUID" -ne 0 ]; then
error "This script must be run as root or with sudo"
exit 1
fi
if ! command -v gitlab-ctl &> /dev/null; then
error "GitLab is not installed or gitlab-ctl is not in PATH"
exit 1
fi
if [ ! -f /etc/gitlab/gitlab.rb ]; then
error "GitLab configuration file not found at /etc/gitlab/gitlab.rb"
exit 1
fi
}
detect_distro() {
log "[2/10] Detecting distribution..."
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update -y"
PKG_INSTALL="apt install -y"
PKG_UPGRADE="apt upgrade -y"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf check-update || true"
PKG_INSTALL="dnf install -y"
PKG_UPGRADE="dnf upgrade -y"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum check-update || true"
PKG_INSTALL="yum install -y"
PKG_UPGRADE="yum update -y"
;;
*)
error "Unsupported distribution: $ID"
exit 1
;;
esac
log "Detected: $PRETTY_NAME"
else
error "Cannot detect distribution. /etc/os-release not found."
exit 1
fi
}
install_dependencies() {
log "[3/10] Installing dependencies..."
$PKG_UPDATE
$PKG_INSTALL rsync awscli
# Install GPG based on distro
if [ "$PKG_MGR" = "apt" ]; then
$PKG_INSTALL gpg
else
$PKG_INSTALL gnupg2
fi
log "Dependencies installed successfully"
}
create_directories() {
log "[4/10] Creating backup directories..."
mkdir -p "$BACKUP_BASE_DIR"
mkdir -p "$CONFIG_BACKUP_DIR"
mkdir -p "$SCRIPT_DIR"
mkdir -p "$LOG_DIR"
# Set proper ownership and permissions
chown -R $GITLAB_USER:$GITLAB_USER "$BACKUP_BASE_DIR"
chown -R $GITLAB_USER:$GITLAB_USER "$CONFIG_BACKUP_DIR"
chown -R root:root "$SCRIPT_DIR"
chown -R $GITLAB_USER:$GITLAB_USER "$LOG_DIR"
chmod 755 "$BACKUP_BASE_DIR"
chmod 755 "$CONFIG_BACKUP_DIR"
chmod 755 "$SCRIPT_DIR"
chmod 755 "$LOG_DIR"
log "Directories created with proper permissions"
}
configure_gitlab_backups() {
log "[5/10] Configuring GitLab backup settings..."
# Backup GitLab configuration
cp /etc/gitlab/gitlab.rb /etc/gitlab/gitlab.rb.backup.$(date +%Y%m%d_%H%M%S)
# Add backup configuration
cat >> /etc/gitlab/gitlab.rb << 'EOF'
# GitLab Backup Configuration
gitlab_rails['backup_path'] = '/var/opt/gitlab/backups'
gitlab_rails['backup_keep_time'] = 604800
gitlab_rails['backup_archive_permissions'] = 0644
gitlab_rails['backup_pg_schema'] = 'public'
EOF
if [ -n "${AWS_ACCESS_KEY_ID:-}" ] && [ -n "${AWS_SECRET_ACCESS_KEY:-}" ]; then
cat >> /etc/gitlab/gitlab.rb << EOF
# S3 Backup Upload Configuration
gitlab_rails['backup_upload_connection'] = {
'provider' => 'AWS',
'region' => '${AWS_REGION:-us-east-1}',
'aws_access_key_id' => '${AWS_ACCESS_KEY_ID}',
'aws_secret_access_key' => '${AWS_SECRET_ACCESS_KEY}'
}
gitlab_rails['backup_upload_remote_directory'] = '${S3_BUCKET:-gitlab-backups}'
gitlab_rails['backup_multipart_chunk_size'] = 104857600
gitlab_rails['backup_encryption'] = 'AES256'
EOF
log "S3 backup upload configured"
else
warn "AWS credentials not provided. S3 upload not configured."
fi
log "GitLab configuration updated"
}
create_backup_script() {
log "[6/10] Creating automated backup script..."
cat > "$SCRIPT_DIR/gitlab-backup.sh" << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
# GitLab Automated Backup Script
# Configuration
BACKUP_DIR="/var/opt/gitlab/backups"
CONFIG_BACKUP_DIR="/var/opt/gitlab/config-backups"
LOG_FILE="/var/log/gitlab-backup/backup.log"
RETENTION_DAYS="${RETENTION_DAYS:-7}"
# Create log directory
mkdir -p "$(dirname "$LOG_FILE")"
# Logging function
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# Error handling
handle_error() {
log "ERROR: Backup failed at step: $1"
# Restart GitLab services if they were stopped
gitlab-ctl start 2>/dev/null || true
exit 1
}
log "Starting GitLab backup process"
# Create GitLab data backup
log "Creating GitLab data backup"
gitlab-backup create BACKUP=gitlab_$(date +%Y%m%d_%H%M%S) || handle_error "creating data backup"
# Backup GitLab configuration files
log "Backing up GitLab configuration"
mkdir -p "$CONFIG_BACKUP_DIR"
CONFIG_BACKUP_FILE="$CONFIG_BACKUP_DIR/gitlab-config-$(date +%Y%m%d_%H%M%S).tar.gz"
tar -czf "$CONFIG_BACKUP_FILE" \
/etc/gitlab/gitlab.rb \
/etc/gitlab/gitlab-secrets.json 2>/dev/null || handle_error "backing up configuration"
# Set proper permissions
chown git:git "$CONFIG_BACKUP_FILE"
chmod 644 "$CONFIG_BACKUP_FILE"
# Clean up old backups
log "Cleaning up old backups (keeping last $RETENTION_DAYS days)"
find "$BACKUP_DIR" -name "*.tar" -type f -mtime +$RETENTION_DAYS -delete 2>/dev/null || true
find "$CONFIG_BACKUP_DIR" -name "*.tar.gz" -type f -mtime +$RETENTION_DAYS -delete 2>/dev/null || true
log "GitLab backup completed successfully"
EOF
chmod 755 "$SCRIPT_DIR/gitlab-backup.sh"
chown root:root "$SCRIPT_DIR/gitlab-backup.sh"
log "Backup script created"
}
create_restore_script() {
log "[7/10] Creating disaster recovery restore script..."
cat > "$SCRIPT_DIR/gitlab-restore.sh" << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
# GitLab Restore Script
if [ $# -ne 1 ]; then
echo "Usage: $0 <backup_timestamp>"
echo "Example: $0 20240115_142300"
echo ""
echo "Available backups:"
ls -la /var/opt/gitlab/backups/gitlab_*_gitlab_backup.tar 2>/dev/null | awk '{print $9}' | sed 's/.*gitlab_\(.*\)_gitlab_backup.tar/\1/' || echo "No backups found"
exit 1
fi
BACKUP_TIMESTAMP="$1"
BACKUP_FILE="/var/opt/gitlab/backups/gitlab_${BACKUP_TIMESTAMP}_gitlab_backup.tar"
CONFIG_BACKUP_DIR="/var/opt/gitlab/config-backups"
if [ ! -f "$BACKUP_FILE" ]; then
echo "ERROR: Backup file not found: $BACKUP_FILE"
exit 1
fi
echo "WARNING: This will restore GitLab from backup and overwrite current data!"
echo "Backup file: $BACKUP_FILE"
echo "Press Ctrl+C to cancel or Enter to continue..."
read
echo "Stopping GitLab services..."
gitlab-ctl stop unicorn 2>/dev/null || true
gitlab-ctl stop puma 2>/dev/null || true
gitlab-ctl stop sidekiq
echo "Restoring GitLab from backup..."
gitlab-backup restore BACKUP=$BACKUP_TIMESTAMP
echo "Starting GitLab services..."
gitlab-ctl start
echo "Reconfiguring GitLab..."
gitlab-ctl reconfigure
echo "Restore completed successfully!"
EOF
chmod 755 "$SCRIPT_DIR/gitlab-restore.sh"
chown root:root "$SCRIPT_DIR/gitlab-restore.sh"
log "Restore script created"
}
setup_cron_job() {
log "[8/10] Setting up automated backup schedule..."
# Create cron job for daily backups at 2 AM
cat > /etc/cron.d/gitlab-backup << EOF
# GitLab automated backup - runs daily at 2 AM
0 2 * * * root $SCRIPT_DIR/gitlab-backup.sh
EOF
chmod 644 /etc/cron.d/gitlab-backup
log "Cron job scheduled for daily backups at 2 AM"
}
apply_gitlab_config() {
log "[9/10] Applying GitLab configuration changes..."
gitlab-ctl reconfigure
log "GitLab configuration applied"
}
verify_setup() {
log "[10/10] Verifying backup setup..."
# Check if backup directory exists and has correct permissions
if [ ! -d "$BACKUP_BASE_DIR" ]; then
error "Backup directory not created"
exit 1
fi
# Check if scripts exist and are executable
if [ ! -x "$SCRIPT_DIR/gitlab-backup.sh" ] || [ ! -x "$SCRIPT_DIR/gitlab-restore.sh" ]; then
error "Backup scripts not properly created"
exit 1
fi
# Check cron job
if [ ! -f /etc/cron.d/gitlab-backup ]; then
error "Cron job not created"
exit 1
fi
# Test backup creation (without actually running full backup)
if ! gitlab-backup create SKIP=uploads,builds,artifacts,lfs,registry,pages DRY_RUN=true &>/dev/null; then
warn "Backup test failed, but setup is complete. Check GitLab status."
fi
log "Setup verification completed successfully!"
}
show_summary() {
echo ""
echo -e "${BLUE}=== GitLab Backup and Disaster Recovery Setup Complete ===${NC}"
echo ""
echo "Backup script location: $SCRIPT_DIR/gitlab-backup.sh"
echo "Restore script location: $SCRIPT_DIR/gitlab-restore.sh"
echo "Backup directory: $BACKUP_BASE_DIR"
echo "Log directory: $LOG_DIR"
echo ""
echo "Manual backup: sudo $SCRIPT_DIR/gitlab-backup.sh"
echo "Restore backup: sudo $SCRIPT_DIR/gitlab-restore.sh <timestamp>"
echo ""
echo "Automated backups run daily at 2 AM via cron"
echo "Backup retention: $RETENTION_DAYS days"
echo ""
if [ -n "${S3_BUCKET:-}" ]; then
echo "S3 bucket configured: $S3_BUCKET"
else
echo "Note: Configure AWS credentials for S3 uploads if desired"
fi
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-r|--retention-days)
RETENTION_DAYS="$2"
shift 2
;;
-h|--help)
usage
;;
*)
error "Unknown option: $1"
usage
;;
esac
done
# Main execution
main() {
check_prerequisites
detect_distro
install_dependencies
create_directories
configure_gitlab_backups
create_backup_script
create_restore_script
setup_cron_job
apply_gitlab_config
verify_setup
show_summary
}
main "$@"
Review the script before running. Execute with: bash install.sh