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

Intermediate 45 min May 17, 2026 94 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 [options] Commands: list List all blob stores migrate Migrate blob store (copy by default) migrate move Move blob store to new location Examples: $0 list $0 migrate default /mnt/new-storage/default $0 migrate default /mnt/new-storage/default move EOF }

Main execution

case "${1:-}" in "list") list_blob_stores ;; "migrate") if [ $# -lt 3 ]; then usage exit 1 fi # Stop Nexus during migration log "Stopping Nexus for blob store migration" sudo systemctl stop nexus migrate_blob_store "$2" "$3" "${4:-copy}" log "Starting Nexus after migration" sudo systemctl start nexus ;; *) usage exit 1 ;; esac

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 [options] Commands: list List available backups restore [--force] Restore from backup file download Download backup from S3 Examples: $0 list $0 restore /path/to/backup.tar.gz.gpg $0 restore /path/to/backup.tar.gz.gpg --force $0 download nexus-backup-20241201-120000.tar.gz.gpg EOF }

Main execution

case "${1:-}" in "list") list_backups ;; "restore") if [ $# -lt 2 ]; then usage exit 1 fi backup_file="$2" force_restore="false" if [ "${3:-}" = "--force" ]; then force_restore="true" fi if [ ! -f "$backup_file" ]; then log "ERROR: Backup file not found: $backup_file" exit 1 fi extract_dir=$(extract_backup "$backup_file") restore_nexus "$extract_dir" "$force_restore" ;; "download") if [ $# -lt 2 ]; then usage exit 1 fi downloaded_file=$(download_backup "$2") log "Downloaded: $downloaded_file" ;; *) usage exit 1 ;; esac

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 that will be triggered by the timer.

[Unit]
Description=Nexus Repository Manager Backup Service
After=network.target nexus.service

[Service]
Type=oneshot
User=nexus-backup
Group=nexus-backup
ExecStart=/opt/nexus-backup/scripts/nexus-backup.sh
Environment=PATH=/usr/local/bin:/usr/bin:/bin
Environment=AWS_CONFIG_FILE=/opt/nexus-backup/.aws/config
Environment=AWS_SHARED_CREDENTIALS_FILE=/opt/nexus-backup/.aws/credentials
StandardOutput=journal
StandardError=journal
SyslogIdentifier=nexus-backup

Security settings

NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/opt/nexus-backup /opt/sonatype-work/nexus3

Timeout after 4 hours

TimeoutStartSec=14400

Create systemd timer for automated backups

Schedule daily backups with systemd timer for reliable execution.

[Unit]
Description=Daily Nexus Repository Manager Backup
Requires=nexus-backup.service

[Timer]

Run daily at 2 AM

OnCalendar=daily AccuracySec=1m Persistent=true RandomizedDelaySec=300 [Install] WantedBy=timers.target

Create weekly blob store verification service

Set up periodic verification of blob store integrity.

[Unit]
Description=Nexus Blob Store Verification Service
After=network.target

[Service]
Type=oneshot
User=nexus-backup
Group=nexus-backup
ExecStart=/bin/bash -c '/opt/nexus-backup/scripts/blob-migration.sh list && echo "Blob verification completed"'
StandardOutput=journal
StandardError=journal
SyslogIdentifier=nexus-blob-verify

Security settings

NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true ReadOnlyPaths=/opt/sonatype-work/nexus3

Create weekly verification timer

Schedule weekly blob store verification checks.

[Unit]
Description=Weekly Nexus Blob Store Verification
Requires=nexus-blob-verify.service

[Timer]

Run weekly on Sunday at 1 AM

OnCalendar=weekly AccuracySec=1m Persistent=true [Install] WantedBy=timers.target

Enable and start systemd timers

Activate the backup automation timers.

sudo systemctl daemon-reload
sudo systemctl enable nexus-backup.timer
sudo systemctl enable nexus-blob-verify.timer
sudo systemctl start nexus-backup.timer
sudo systemctl start nexus-blob-verify.timer

Configure AWS credentials for S3 backup

Set up AWS credentials for remote backup storage (optional).

sudo -u nexus-backup mkdir -p /opt/nexus-backup/.aws
[default]
aws_access_key_id = YOUR_ACCESS_KEY_ID
aws_secret_access_key = YOUR_SECRET_ACCESS_KEY
[default]
region = us-east-1
output = json
sudo chown -R nexus-backup:nexus-backup /opt/nexus-backup/.aws
sudo chmod 600 /opt/nexus-backup/.aws/credentials
sudo chmod 644 /opt/nexus-backup/.aws/config

Implement disaster recovery and restoration procedures

Test backup creation

Manually trigger a backup to verify the automation works correctly.

sudo systemctl start nexus-backup.service
sudo journalctl -u nexus-backup.service -f

Test blob store migration

Verify blob store migration functionality with a test migration.

sudo -u nexus-backup /opt/nexus-backup/scripts/blob-migration.sh list

Create disaster recovery runbook

Document the complete disaster recovery procedure for your team.

# Nexus Repository Manager 3 Disaster Recovery Runbook

Emergency Recovery Steps

1. Assess the Situation

  • Check system status: systemctl status nexus
  • Check disk space: df -h
  • Check recent logs: journalctl -u nexus -n 50

2. List Available Backups

sudo -u nexus-backup /opt/nexus-backup/scripts/nexus-restore.sh list

3. Download Backup (if from S3)

sudo -u nexus-backup /opt/nexus-backup/scripts/nexus-restore.sh download BACKUP_NAME

4. Perform Full Restoration

# WARNING: This will overwrite existing data
sudo -u nexus-backup /opt/nexus-backup/scripts/nexus-restore.sh restore /path/to/backup.tar.gz.gpg --force

5. Verify Recovery

  • Check service status: systemctl status nexus
  • Test web interface: http://localhost:8081
  • Verify repositories and artifacts

Partial Recovery Scenarios

Blob Store Corruption

  1. Stop Nexus: sudo systemctl stop nexus
  2. Extract specific blob store from backup
  3. Replace corrupted blob store
  4. Start Nexus: sudo systemctl start nexus

Configuration Corruption

  1. Extract configuration files from backup
  2. Replace corrupted configs in /opt/sonatype-work/nexus3/etc/
  3. Restart Nexus

Contact Information

  • System Administrator: admin@example.com
  • Emergency Contact: +1-555-0123
  • Backup Location: S3 bucket nexus-backups-example

Recovery Time Objectives

  • RPO (Recovery Point Objective): 24 hours (daily backups)
  • RTO (Recovery Time Objective): 2 hours (complete restoration)
Last Updated: $(date)

Set up monitoring and alerting

Create a monitoring script to check backup health and send alerts.

#!/bin/bash

Nexus Backup Monitoring Script

set -euo pipefail BACKUP_BASE_DIR="/opt/nexus-backup" MAX_BACKUP_AGE_HOURS=26 EMAIL_ALERT="admin@example.com" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" }

Check recent backup existence

check_recent_backup() { local latest_backup latest_backup=$(find "$BACKUP_BASE_DIR/snapshots" -name "*.tar.gz.gpg" -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-) if [ -z "$latest_backup" ]; then log "CRITICAL: No backups found" return 1 fi local backup_age backup_age=$(stat -c %Y "$latest_backup") local current_time=$(date +%s) local age_hours=$(( (current_time - backup_age) / 3600 )) if [ $age_hours -gt $MAX_BACKUP_AGE_HOURS ]; then log "WARNING: Latest backup is $age_hours hours old (max: $MAX_BACKUP_AGE_HOURS)" return 1 else log "OK: Latest backup is $age_hours hours old" return 0 fi }

Check backup service status

check_backup_service() { if systemctl is-enabled nexus-backup.timer >/dev/null 2>&1; then if systemctl is-active nexus-backup.timer >/dev/null 2>&1; then log "OK: Backup timer is active" return 0 else log "CRITICAL: Backup timer is not active" return 1 fi else log "CRITICAL: Backup timer is not enabled" return 1 fi }

Send email alert

send_alert() { local subject="$1" local message="$2" if command -v mail >/dev/null 2>&1; then echo "$message" | mail -s "$subject" "$EMAIL_ALERT" log "Alert sent to $EMAIL_ALERT" else log "WARNING: Cannot send email alert (mail command not available)" fi }

Main monitoring logic

main() { local errors=0 local messages="" if ! check_recent_backup; then errors=$((errors + 1)) messages="${messages}Backup age check failed\n" fi if ! check_backup_service; then errors=$((errors + 1)) messages="${messages}Backup service check failed\n" fi if [ $errors -gt 0 ]; then local alert_message="Nexus backup monitoring detected $errors issues:\n\n${messages}\nHost: $(hostname)\nTime: $(date)" send_alert "Nexus Backup Alert - $(hostname)" "$alert_message" exit 1 else log "All backup monitoring checks passed" exit 0 fi } main "$@"

Create monitoring timer

Set up automated monitoring with systemd timer.

[Unit]
Description=Nexus Backup Monitoring Service
After=network.target

[Service]
Type=oneshot
User=nexus-backup
Group=nexus-backup
ExecStart=/opt/nexus-backup/scripts/backup-monitor.sh
StandardOutput=journal
StandardError=journal
SyslogIdentifier=nexus-backup-monitor
[Unit]
Description=Nexus Backup Monitoring Timer
Requires=nexus-backup-monitor.service

[Timer]

Run every 4 hours

OnCalendar=--* 00,04,08,12,16,20:00:00 AccuracySec=1m Persistent=true [Install] WantedBy=timers.target
sudo chmod 755 /opt/nexus-backup/scripts/backup-monitor.sh
sudo systemctl daemon-reload
sudo systemctl enable nexus-backup-monitor.timer
sudo systemctl start nexus-backup-monitor.timer

Verify your setup

# Check timer status
sudo systemctl list-timers | grep nexus

Verify backup directory structure

ls -la /opt/nexus-backup/

Test backup script permissions

sudo -u nexus-backup /opt/nexus-backup/scripts/nexus-backup.sh --help || echo "Backup script is executable"

Check GPG key setup

sudo -u nexus-backup gpg --list-keys

Verify last backup

find /opt/nexus-backup/snapshots -name "*.tar.gz.gpg" -type f -exec ls -la {} \;

Check monitoring

sudo systemctl status nexus-backup-monitor.timer

Review recent logs

sudo journalctl -u nexus-backup.service -n 20

Common issues

SymptomCauseFix
Backup script fails with permission deniedIncorrect ownership or permissionssudo chown -R nexus-backup:nexus-backup /opt/nexus-backup && sudo chmod 755 /opt/nexus-backup/scripts/*.sh
GPG encryption failsMissing GPG keys or passphraseRegenerate GPG keys: sudo -u nexus-backup gpg --batch --generate-key
Nexus won't start after restorePermission issues or corrupted datasudo chown -R nexus:nexus /opt/sonatype-work/nexus3 && sudo systemctl restart nexus
S3 upload failsAWS credentials not configuredCheck /opt/nexus-backup/.aws/credentials and IAM permissions
Timer doesn't runSystemd timer not enabledsudo systemctl enable --now nexus-backup.timer
Blob store migration incompleteInsufficient disk spaceCheck available space: df -h and free up space
Monitoring alerts not sentMail service not configuredInstall and configure mail: sudo apt install postfix mailutils

Next steps

Running this in production?

Want this handled for you? Setting up automated backups and disaster recovery once is straightforward. Keeping backup strategies tested, storage optimized, and recovery procedures updated across environments is the harder part. See how we run infrastructure like this for European teams needing reliable repository management.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.