Set up automated backup and disaster recovery for Nexus Repository Manager 3 with blob store migration

Intermediate 45 min May 17, 2026 377 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Configure comprehensive backup automation for Nexus Repository Manager 3 including blob store migration, disaster recovery procedures, and automated scheduling with systemd timers for production environments.

Prerequisites

  • Nexus Repository Manager 3 installed
  • Root or sudo access
  • At least 50GB free disk space for backups
  • GPG installed for encryption

What this solves

Nexus Repository Manager stores artifacts across multiple blob stores that require coordinated backup strategies. This tutorial sets up automated backup procedures that capture both the database and blob store data, implements blob store migration capabilities, and creates disaster recovery workflows with systemd timer automation.

Install backup prerequisites and dependencies

Update system packages

Start by updating your package manager to ensure you have the latest versions of required tools.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install backup utilities

Install required tools for backup compression, encryption, and remote storage synchronization.

sudo apt install -y rsync gzip tar gpg curl jq postgresql-client-common awscli
sudo dnf install -y rsync gzip tar gnupg2 curl jq postgresql awscli

Create backup directories

Set up directory structure for local backups with proper permissions.

sudo mkdir -p /opt/nexus-backup/{scripts,snapshots,blob-stores,logs}
sudo mkdir -p /opt/nexus-backup/recovery/{staging,restored}
sudo useradd -r -s /bin/bash -d /opt/nexus-backup nexus-backup
sudo chown -R nexus-backup:nexus-backup /opt/nexus-backup
sudo chmod 750 /opt/nexus-backup
Never use chmod 777. It gives every user on the system full access to your backup files. Instead, use specific ownership with chown and minimal permissions like 750 for directories containing sensitive data.

Configure GPG encryption for backups

Generate encryption keys for secure backup storage.

sudo -u nexus-backup gpg --batch --generate-key << EOF
Key-Type: RSA
Key-Length: 4096
Subkey-Type: RSA
Subkey-Length: 4096
Name-Real: Nexus Backup System
Name-Email: backup@example.com
Expire-Date: 2y
Passphrase: $(openssl rand -base64 32)
%commit
EOF

Configure Nexus Repository backup with blob store migration

Create main backup script

This script handles database backup, blob store synchronization, and metadata collection.

#!/bin/bash

# Nexus Repository Manager 3 Backup Script
set -euo pipefail

# Configuration
NEXUS_DATA_DIR="/opt/sonatype-work/nexus3"
BACKUP_BASE_DIR="/opt/nexus-backup"
BACKUP_RETENTION_DAYS=30
COMPRESSION_LEVEL=6
GPG_RECIPIENT="backup@example.com"
S3_BUCKET="nexus-backups-example"

# Logging
LOG_FILE="$BACKUP_BASE_DIR/logs/backup-$(date +%Y%m%d-%H%M%S).log"
exec 1> >(tee -a "$LOG_FILE")
exec 2>&1

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

# Nexus service management
stop_nexus() {
    log "Stopping Nexus Repository Manager"
    sudo systemctl stop nexus
    sleep 10
}

start_nexus() {
    log "Starting Nexus Repository Manager"
    sudo systemctl start nexus
    
    # Wait for service to be ready
    local retries=0
    while ! curl -sf http://localhost:8081/service/rest/v1/status > /dev/null; do
        if [ $retries -ge 30 ]; then
            log "ERROR: Nexus failed to start within 5 minutes"
            exit 1
        fi
        sleep 10
        ((retries++))
    done
    log "Nexus Repository Manager started successfully"
}

# Database backup
backup_database() {
    local backup_dir="$1"
    log "Backing up OrientDB database"
    
    if [ -d "$NEXUS_DATA_DIR/db" ]; then
        tar -czf "$backup_dir/orientdb-$(date +%Y%m%d-%H%M%S).tar.gz" \
            -C "$NEXUS_DATA_DIR" db/
        log "Database backup completed"
    else
        log "WARNING: Database directory not found at $NEXUS_DATA_DIR/db"
    fi
}

# Blob store backup with migration support
backup_blob_stores() {
    local backup_dir="$1"
    log "Backing up blob stores"
    
    local blob_backup_dir="$backup_dir/blob-stores"
    mkdir -p "$blob_backup_dir"
    
    if [ -d "$NEXUS_DATA_DIR/blobs" ]; then
        # Create blob store manifest
        find "$NEXUS_DATA_DIR/blobs" -type f -name "*.properties" > "$blob_backup_dir/blob-manifest.txt"
        
        # Backup each blob store
        for blob_store in "$NEXUS_DATA_DIR/blobs"/*; do
            if [ -d "$blob_store" ]; then
                local store_name=$(basename "$blob_store")
                log "Backing up blob store: $store_name"
                
                tar -czf "$blob_backup_dir/$store_name-$(date +%Y%m%d-%H%M%S).tar.gz" \
                    -C "$NEXUS_DATA_DIR/blobs" "$store_name/"
            fi
        done
        log "Blob store backup completed"
    else
        log "WARNING: Blob stores directory not found"
    fi
}

# Configuration backup
backup_configuration() {
    local backup_dir="$1"
    log "Backing up Nexus configuration"
    
    local config_items=(
        "etc"
        "log"
        "tmp"
        "orient.cfg"
    )
    
    for item in "${config_items[@]}"; do
        if [ -e "$NEXUS_DATA_DIR/$item" ]; then
            tar -czf "$backup_dir/$item-$(date +%Y%m%d-%H%M%S).tar.gz" \
                -C "$NEXUS_DATA_DIR" "$item"
        fi
    done
    
    log "Configuration backup completed"
}

# Encrypt and compress final backup
encrypt_backup() {
    local backup_dir="$1"
    local encrypted_file="$backup_dir.tar.gz.gpg"
    
    log "Creating encrypted backup archive"
    tar -czf - -C "$(dirname "$backup_dir")" "$(basename "$backup_dir")" | \
        gpg --trust-model always --encrypt --recipient "$GPG_RECIPIENT" \
        --compress-algo 2 --compress-level "$COMPRESSION_LEVEL" \
        --output "$encrypted_file"
    
    # Clean up unencrypted directory
    rm -rf "$backup_dir"
    
    log "Encrypted backup created: $encrypted_file"
    echo "$encrypted_file"
}

# Upload to remote storage
upload_backup() {
    local backup_file="$1"
    
    if [ -n "${S3_BUCKET:-}" ] && command -v aws >/dev/null; then
        log "Uploading backup to S3: $S3_BUCKET"
        aws s3 cp "$backup_file" "s3://$S3_BUCKET/$(basename "$backup_file")"
        log "Upload completed"
    else
        log "S3 upload skipped (no bucket configured or AWS CLI missing)"
    fi
}

# Cleanup old backups
cleanup_old_backups() {
    log "Cleaning up backups older than $BACKUP_RETENTION_DAYS days"
    find "$BACKUP_BASE_DIR/snapshots" -name "*.tar.gz.gpg" -mtime +"$BACKUP_RETENTION_DAYS" -delete
    
    if [ -n "${S3_BUCKET:-}" ] && command -v aws >/dev/null; then
        # Clean up S3 backups (requires AWS CLI with lifecycle policies)
        log "S3 cleanup managed by bucket lifecycle policies"
    fi
}

# Main backup workflow
main() {
    log "Starting Nexus Repository Manager backup"
    
    local timestamp=$(date +%Y%m%d-%H%M%S)
    local backup_dir="$BACKUP_BASE_DIR/snapshots/nexus-backup-$timestamp"
    
    mkdir -p "$backup_dir"
    
    # Stop Nexus for consistent backup
    stop_nexus
    
    trap 'start_nexus' EXIT
    
    # Perform backups
    backup_database "$backup_dir"
    backup_blob_stores "$backup_dir"
    backup_configuration "$backup_dir"
    
    # Create backup metadata
    cat > "$backup_dir/backup-metadata.json" << EOF
{
    "timestamp": "$timestamp",
    "nexus_version": "$(cat $NEXUS_DATA_DIR/log/nexus.log | grep 'Started Sonatype Nexus' | tail -1 | cut -d' ' -f6 || echo 'unknown')",
    "backup_type": "full",
    "blob_stores": $(find $NEXUS_DATA_DIR/blobs -maxdepth 1 -type d | wc -l),
    "hostname": "$(hostname)"
}
EOF
    
    # Restart Nexus
    start_nexus
    trap - EXIT
    
    # Encrypt and upload
    local encrypted_backup
    encrypted_backup=$(encrypt_backup "$backup_dir")
    upload_backup "$encrypted_backup"
    
    # Cleanup
    cleanup_old_backups
    
    log "Backup completed successfully: $(basename "$encrypted_backup")"
}

main "$@"

Create blob store migration script

This script handles migrating blob stores between different storage backends.

#!/bin/bash

# Nexus Blob Store Migration Script
set -euo pipefail

NEXUS_DATA_DIR="/opt/sonatype-work/nexus3"
BACKUP_BASE_DIR="/opt/nexus-backup"
LOG_FILE="$BACKUP_BASE_DIR/logs/migration-$(date +%Y%m%d-%H%M%S).log"

exec 1> >(tee -a "$LOG_FILE")
exec 2>&1

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

# Blob store operations
list_blob_stores() {
    log "Available blob stores:"
    if [ -d "$NEXUS_DATA_DIR/blobs" ]; then
        for store in "$NEXUS_DATA_DIR/blobs"/*; do
            if [ -d "$store" ]; then
                local store_name=$(basename "$store")
                local size=$(du -sh "$store" | cut -f1)
                log "  - $store_name ($size)"
            fi
        done
    else
        log "No blob stores found"
    fi
}

migrate_blob_store() {
    local source_store="$1"
    local target_path="$2"
    local migration_type="${3:-copy}"
    
    log "Starting blob store migration: $source_store -> $target_path"
    
    if [ ! -d "$NEXUS_DATA_DIR/blobs/$source_store" ]; then
        log "ERROR: Source blob store not found: $source_store"
        return 1
    fi
    
    mkdir -p "$target_path"
    
    case "$migration_type" in
        "copy")
            log "Copying blob store (source preserved)"
            rsync -avP "$NEXUS_DATA_DIR/blobs/$source_store/" "$target_path/"
            ;;
        "move")
            log "Moving blob store (source will be removed)"
            rsync -avP --remove-source-files "$NEXUS_DATA_DIR/blobs/$source_store/" "$target_path/"
            find "$NEXUS_DATA_DIR/blobs/$source_store" -type d -empty -delete
            ;;
        *)
            log "ERROR: Invalid migration type: $migration_type (use 'copy' or 'move')"
            return 1
            ;;
    esac
    
    # Verify migration
    local source_count=$(find "$NEXUS_DATA_DIR/blobs/$source_store" -type f | wc -l)
    local target_count=$(find "$target_path" -type f | wc -l)
    
    if [ "$migration_type" = "copy" ] && [ "$source_count" -eq "$target_count" ]; then
        log "Migration verified: $target_count files copied successfully"
    elif [ "$migration_type" = "move" ] && [ "$target_count" -gt 0 ]; then
        log "Migration verified: $target_count files moved successfully"
    else
        log "WARNING: Migration verification failed (source: $source_count, target: $target_count)"
    fi
}

# Usage information
usage() {
    cat << EOF
Usage: $0 

Create disaster recovery script

This script handles full system restoration from encrypted backups.

#!/bin/bash

# Nexus Repository Manager 3 Disaster Recovery Script
set -euo pipefail

NEXUS_DATA_DIR="/opt/sonatype-work/nexus3"
BACKUP_BASE_DIR="/opt/nexus-backup"
RECOVERY_DIR="$BACKUP_BASE_DIR/recovery"
LOG_FILE="$BACKUP_BASE_DIR/logs/restore-$(date +%Y%m%d-%H%M%S).log"

exec 1> >(tee -a "$LOG_FILE")
exec 2>&1

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

# List available backups
list_backups() {
    log "Available backup files:"
    
    # Local backups
    if [ -d "$BACKUP_BASE_DIR/snapshots" ]; then
        find "$BACKUP_BASE_DIR/snapshots" -name "*.tar.gz.gpg" -printf "%T@ %Tc %p\n" | sort -rn | head -10 | while read -r epoch date time tz file; do
            log "  Local: $(basename "$file") ($date $time)"
        done
    fi
    
    # S3 backups (if configured)
    if command -v aws >/dev/null && [ -n "${S3_BUCKET:-}" ]; then
        log "  S3 backups in bucket: $S3_BUCKET"
        aws s3 ls "s3://$S3_BUCKET/" --recursive | tail -10 | while read -r date time size file; do
            log "    S3: $(basename "$file") ($date $time)"
        done
    fi
}

# Download backup from S3
download_backup() {
    local backup_name="$1"
    local local_path="$BACKUP_BASE_DIR/snapshots/$backup_name"
    
    if [ -f "$local_path" ]; then
        log "Backup already exists locally: $local_path"
        echo "$local_path"
        return 0
    fi
    
    if command -v aws >/dev/null && [ -n "${S3_BUCKET:-}" ]; then
        log "Downloading backup from S3: $backup_name"
        aws s3 cp "s3://$S3_BUCKET/$backup_name" "$local_path"
        echo "$local_path"
    else
        log "ERROR: Cannot download backup (AWS CLI not configured)"
        return 1
    fi
}

# Decrypt and extract backup
extract_backup() {
    local backup_file="$1"
    local extract_dir="$RECOVERY_DIR/staging/$(basename "$backup_file" .tar.gz.gpg)"
    
    log "Extracting backup: $(basename "$backup_file")"
    
    # Clean staging area
    rm -rf "$extract_dir"
    mkdir -p "$extract_dir"
    
    # Decrypt and extract
    gpg --trust-model always --decrypt "$backup_file" | tar -xzf - -C "$extract_dir"
    
    # Verify extraction
    if [ -f "$extract_dir"/*/backup-metadata.json ]; then
        log "Backup extracted successfully to: $extract_dir"
        echo "$extract_dir"
    else
        log "ERROR: Backup extraction failed or incomplete"
        return 1
    fi
}

# Restore from extracted backup
restore_nexus() {
    local extract_dir="$1"
    local force_restore="${2:-false}"
    
    # Find the actual backup directory
    local backup_content_dir
    backup_content_dir=$(find "$extract_dir" -maxdepth 1 -type d -name "nexus-backup-*" | head -1)
    
    if [ -z "$backup_content_dir" ]; then
        log "ERROR: Backup content directory not found in $extract_dir"
        return 1
    fi
    
    # Display backup metadata
    if [ -f "$backup_content_dir/backup-metadata.json" ]; then
        log "Backup metadata:"
        cat "$backup_content_dir/backup-metadata.json" | jq .
    fi
    
    # Safety check
    if [ "$force_restore" != "true" ] && [ -d "$NEXUS_DATA_DIR" ]; then
        log "WARNING: Nexus data directory exists. This will overwrite existing data."
        log "Use --force to proceed with restoration."
        return 1
    fi
    
    # Stop Nexus
    log "Stopping Nexus Repository Manager"
    sudo systemctl stop nexus || true
    
    # Backup existing data
    if [ -d "$NEXUS_DATA_DIR" ]; then
        local backup_existing="$RECOVERY_DIR/existing-$(date +%Y%m%d-%H%M%S)"
        log "Backing up existing data to: $backup_existing"
        sudo mv "$NEXUS_DATA_DIR" "$backup_existing"
    fi
    
    # Create new data directory
    sudo mkdir -p "$NEXUS_DATA_DIR"
    
    # Restore database
    if [ -f "$backup_content_dir"/orientdb-*.tar.gz ]; then
        log "Restoring OrientDB database"
        sudo tar -xzf "$backup_content_dir"/orientdb-*.tar.gz -C "$NEXUS_DATA_DIR"
    fi
    
    # Restore blob stores
    if [ -d "$backup_content_dir/blob-stores" ]; then
        log "Restoring blob stores"
        sudo mkdir -p "$NEXUS_DATA_DIR/blobs"
        
        for blob_archive in "$backup_content_dir/blob-stores"/*.tar.gz; do
            if [ -f "$blob_archive" ]; then
                local store_name=$(basename "$blob_archive" | sed 's/-[0-9]*-[0-9]*.tar.gz$//')
                log "Restoring blob store: $store_name"
                sudo tar -xzf "$blob_archive" -C "$NEXUS_DATA_DIR/blobs"
            fi
        done
    fi
    
    # Restore configuration files
    for config_archive in "$backup_content_dir"/etc-*.tar.gz "$backup_content_dir"/orient.cfg-*.tar.gz; do
        if [ -f "$config_archive" ]; then
            log "Restoring configuration: $(basename "$config_archive")"
            sudo tar -xzf "$config_archive" -C "$NEXUS_DATA_DIR"
        fi
    done
    
    # Fix permissions
    sudo chown -R nexus:nexus "$NEXUS_DATA_DIR"
    sudo chmod -R 755 "$NEXUS_DATA_DIR"
    
    # Start Nexus
    log "Starting Nexus Repository Manager"
    sudo systemctl start nexus
    
    # Wait for startup and verify
    local retries=0
    while ! curl -sf http://localhost:8081/service/rest/v1/status > /dev/null; do
        if [ $retries -ge 30 ]; then
            log "ERROR: Nexus failed to start within 5 minutes"
            return 1
        fi
        sleep 10
        ((retries++))
    done
    
    log "Nexus Repository Manager restored and started successfully"
}

# Usage information
usage() {
    cat << EOF
Usage: $0 

Make scripts executable

Set proper permissions for all backup and recovery scripts.

sudo chmod 755 /opt/nexus-backup/scripts/*.sh
sudo chown nexus-backup:nexus-backup /opt/nexus-backup/scripts/*.sh

Set up automated scheduling with systemd timers

Create systemd service for backup

Define the backup service tha

Automated install script

Run this to automate the entire setup

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. Fully managed, with one fixed contact who knows your setup.

You get one fixed contact who knows your setup

At their desk in Rotterdam 16:12 · reachable in a message, no ticket form