Profile-based backup orchestration for Ubuntu using BorgBackup with external HDD power management & optional Docker stop/start + DB dump.
- π§© Modular Architecture - 13 main + 5 PRE/POST segments, independently testable
- π Profile-Based - Multiple backup configurations, one installation
- π³ Docker Integration - Automated container stop/start with state preservation
- ποΈ Database Automation - Nextcloud DB dumps with maintenance mode & compression
- β‘ Hardware Integration - Shelly Plug power management for external HDDs
- π Safe HDD Shutdown - Automatic head parking and spindown
- β° systemd Integration - Scheduled backups with timer units
- β Production-Ready - Comprehensive testing and error handling
- π Dual Logging - Local and backup location logging (common.env based locally + backup target location)
- π‘οΈ UUID Validation - Prevents accidental backup to wrong disk
- π Automated Recovery Keys - Automatic export of repository keys for disaster recovery
- π© SMS/RCS Notifyer - Reliable API based optional and segmentend notifyer for critical states through SMS/RCS
- π Automated Recovery Keys - Automatic ssh export of repository keys for disaster recovery
- ποΈ Auto-Close File Managers - Safe & Automated Unmount
- π Pop-Up Notifyer - Reliable notifyer for errors
- π© E-Mail Notifyer - Reliable notifyer for errors
# 1. Clone repository
git clone https://github.com/JoZapf/segmented-borg-backup-system.git
cd segmented-borg-backup-system
# 2. Copy example configurations
sudo cp config/common.env.example config/common.env
sudo cp config/profiles/system.env.example config/profiles/system.env
# 3. Edit configurations (adjust UUID, hostname, etc.)
sudo nano config/common.env
sudo nano config/profiles/system.env
# 4. Create Borg passphrase
sudo mkdir -p /root/.config/borg
echo "your-secure-passphrase" | sudo tee /root/.config/borg/passphrase
sudo chmod 600 /root/.config/borg/passphrase
# 5. Install to /opt
sudo mkdir -p /opt/backup-system
sudo cp -r * /opt/backup-system/
# 6. Install systemd units
cd /opt/backup-system/systemd
sudo ./install-systemd-units.sh
# 7. Configure /etc/fstab for automount (see docs/SYSTEMD.md)
# 8. Test manual backup
sudo /opt/backup-system/run-backup.sh systemAuto-Generated API Reference:
View in docs/api/ - Complete reference for all 18 segments with extracted variables, commands, exit codes, and error messages.
Manual Documentation:
- Installation Guide - Complete setup instructions
- Deployment Guide - SMB and Git workflows
- Profiles Guide - Profile configuration
- Segments Overview - Segment architecture
- systemd Integration - Timer and mount configuration
- Security Guide - Security best practices
- Testing Guide - Verification procedures
This system uses separate directories for development and production:
π Development (Git Repository) π Production (Live System)
/home/user/Projekte/linux-backup-system β /opt/backup-system
βββ Git working directory βββ Root-owned installation
βββ Owned by: user:user βββ Owned by: root:root
βββ Permissions: 755 (user can edit) βββ Permissions: 755 (root only)
βββ Config files: 644 (user readable) βββ Config files: 600 (root only)
βββ Purpose: Development, updates, Git βββ Purpose: Production backups
Why not run directly from the Git repository?
-
Secrets Protection
# Development (UNSAFE) /home/user/Projekte/linux-backup-system/config/common.env βββ Owned by: user:user (644) βββ Contains: Borg passphrase path, Shelly IP, DB passwords β Regular user can read all secrets! # Production (SAFE) /opt/backup-system/config/common.env βββ Owned by: root:root (600) βββ Contains: Borg passphrase path, Shelly IP, DB passwords β Only root can read secrets!
-
Privilege Separation
- Development: Regular user can edit, test, commit to Git
- Production: Only root can modify running system
- Security: Prevents accidental or malicious changes during backup
-
System Integration
- systemd services run as root
/opt/is the standard Linux location for add-on application software packages- Clear separation between development and production code
-
Audit Trail
- Production changes require
sudo(logged in auth.log) - Development changes are tracked in Git
- No confusion about which version is running
- Production changes require
# 1. DEVELOPMENT (as regular user)
cd /home/user/Projekte/linux-backup-system
git pull # Update from repository
nano segments/08_borg_backup.sh # Make changes
git add segments/08_borg_backup.sh
git commit -m "Improve backup logging"
git push
# 2. DEPLOYMENT (as root)
sudo cp segments/08_borg_backup.sh /opt/backup-system/segments/
sudo chmod +x /opt/backup-system/segments/08_borg_backup.sh
# 3. TESTING (as root)
sudo /opt/backup-system/run-backup.sh system
# 4. PRODUCTION (via systemd)
sudo systemctl start backup-system@system.serviceSee Deployment Guide for detailed deployment workflows.
| Location | Owner | Permissions | Purpose |
|---|---|---|---|
/home/user/Projekte/linux-backup-system/ |
user:user | 755 | Development, Git |
/home/user/Projekte/linux-backup-system/config/*.env |
user:user | 644 | Local dev configs (NOT in Git) |
/opt/backup-system/ |
root:root | 755 | Production installation |
/opt/backup-system/main.sh |
root:root | 755 | Executable scripts |
/opt/backup-system/config/*.env |
root:root | 600 | Production secrets |
/root/.config/borg/passphrase |
root:root | 600 | Borg encryption key |
- Full Documentation - Complete feature overview
- Installation Guide - Detailed setup instructions
- Deployment Guide - SMB and Git deployment workflows
- Docker & Nextcloud Backup - Container & database backup guide
- systemd Integration - Timer configuration and troubleshooting
- Testing Documentation - Test results and validation
- Security Guide - Security best practices
backup-system/
βββ main.sh # Orchestrator with PRE/POST support
βββ run-backup.sh # Wrapper for reliable file logging
βββ config/
β βββ common.env.example # Shared configuration template
β βββ profiles/
β βββ system.env.example # System backup template
β βββ data.env.example # Data backup template
β βββ dev-data.env.example # Docker/Nextcloud backup template
βββ segments/ # 13 main + 4 PRE/POST segments
β βββ 01_validate_config.sh
β βββ 02_init_logging.sh # common.env based locally + backup target location
β βββ 03_shelly_power_on.sh
β βββ 04_wait_device.sh
β βββ 05_mount_backup.sh
β βββ 06_validate_mount.sh
β βββ 07_init_borg_repo.sh
β βββ 08_borg_backup.sh
β βββ 09_borg_verify.sh
β βββ 10_borg_prune.sh
β βββ 11_hdd_spindown.sh
β βββ 12_unmount_backup.sh
β βββ 13_shelly_power_off.sh
β βββ pre_01_nextcloud_db_dump.sh # PRE: Nextcloud DB dump
β βββ pre_02_docker_stop.sh # PRE: Docker stop
β βββ post_01_docker_start.sh # POST: Docker start for DB dump
β βββ post_01_export_recovery_keys.sh # POST: main Recovery key export
β βββ post_02_export_recovery_keys.sh # POST: "dev-data" (Profile based) Recovery key export
βββ systemd/ # systemd integration
βββ backup-system@.service
βββ backup-system-daily.timer
βββ mnt-extern_backup.mount
βββ mnt-extern_backup.automount
PRE-BACKUP Phase (Profile-specific, optional)
- Pre-01 Nextcloud DB dump with maintenance mode (if enabled)
- Pre-02 Docker container stop with state preservation (if enabled)
MAIN BACKUP Phase - Part 1 (All profiles)
- Validate configuration and dependencies
- Initialize logging (common.env based locally + backup target location)
- Power On external HDD via Shelly Plug
- Wait for device availability
- Mount backup device (with automount fallback)
- Validate correct UUID is mounted (safety check!)
- Initialize Borg repository (if needed)
- Backup configured sources with Borg
POST-BACKUP Phase (Profile-specific, optional)
- Docker container restart (if enabled) β Services back online!
- Recovery key export (automated disaster recovery preparation) MAIN BACKUP Phase - Part 2 (All profiles, services online!)
- Verify backup integrity (full data check)
- Prune old backups per retention policy
CLEANUP Phase (All profiles) 11. Spindown HDD (park heads safely) 12. Unmount backup device 13. Power Off HDD via Shelly Plug
- β Testable - Each segment can be tested independently
- β Maintainable - Easy to modify or replace segments
- β Debuggable - Clear error location in logs (common.env based locally + backup target location)
- β Flexible - Segments can be enabled/disabled
- β Reusable - Segments can be shared across profiles
- β Profile-Specific - PRE/POST segments run only for configured profiles
MAIN_SEGMENTS (Universal) - Defined in main.sh
β Run for ALL profiles (system, data, dev-data)
β Core backup logic: mount, backup, verify, unmount
PRE/POST_SEGMENTS (Profile-Specific) - Defined in profile.env
β Run ONLY for profiles that define them
β Custom actions: DB dumps, container management, notifications
Why separate?
- System backup doesn't need Docker segments
- Docker backup doesn't need Shelly Plug segments
- Each profile gets exactly what it needs
The main.sh orchestrator dynamically executes segments based on profile configuration:
# 1. Source profile config
source /opt/backup-system/config/profiles/${PROFILE}.env
# 2. Run PRE-BACKUP segments (if defined in profile)
if [ -n "${PRE_BACKUP_SEGMENTS:-}" ]; then
for segment in "${PRE_BACKUP_SEGMENTS[@]}"; do
execute_segment "$segment"
done
fi
# 3. Run MAIN segments (always, for all profiles)
for segment in "${MAIN_SEGMENTS[@]}"; do
execute_segment "$segment"
done
# 4. Run POST-CLEANUP segments (if defined in profile)
if [ -n "${POST_CLEANUP_SEGMENTS:-}" ]; then
for segment in "${POST_CLEANUP_SEGMENTS[@]}"; do
execute_segment "$segment"
done
fiResult: Profiles are modular and composable!
- β Encrypted backups (Borg repokey BLAKE2b)
- β UUID validation prevents wrong disk writes
- β Safe HDD head parking before power-off
- β Comprehensive error handling
- β Dual logging for audit trail (common.env based locally + backup target location)
Protected Configuration Files:
# Never committed to Git (protected by .gitignore)
config/common.env # Shelly Plug IP, shared settings
config/profiles/system.env # System backup credentials, UUIDs
config/profiles/data.env # Data backup credentials, UUIDs
config/profiles/dev-data.env # Docker credentials, DB passwords, UUIDs
/root/.config/borg/passphrase # Borg encryption passphraseTemplate Files (Safe to share):
# Committed to Git with placeholder values
config/common.env.example
config/profiles/system.env.example
config/profiles/data.env.example
config/profiles/dev-data.env.exampleSecurity Best Practices:
-
File Permissions - All config files must be
600(root only)sudo chmod 600 /opt/backup-system/config/common.env sudo chmod 600 /opt/backup-system/config/profiles/*.env sudo chmod 600 /root/.config/borg/passphrase -
Borg Passphrase - Store separately and back up securely
- Password manager (LastPass, 1Password, Bitwarden)
- Encrypted USB drive in safe
- Paper backup in secure location
- Critical: Without passphrase, backups are unrecoverable!
-
Sensitive Data in Configs
- Database passwords (Nextcloud, MariaDB, PostgreSQL)
- Device UUIDs (backup HDD identifiers)
- IP addresses (Shelly Plug, internal network)
- Hostnames (system identifiers)
-
Git Safety -
.gitignoreprotects:config/common.env config/profiles/*.env !config/profiles/*.example /root/.config/borg/passphrase *.key *.pem
See Security Guide for complete security documentation.
Disaster Recovery Protection:
The system automatically exports your Borg repository keys after each successful backup, ensuring you always have the necessary credentials for disaster recovery.
What's Exported:
recovery/
βββ system_CREA-think_2d92c4c5_2026-01-16.zip # Password-protected archive
βββ dev-data_CREA-think_3194a634_2026-01-16.zip
βββ repo-key.txt # Borg repository key
βββ recovery-info.txt # Complete recovery metadata
βββ RECOVERY-README.txt # Step-by-step restore guide
Key Features:
- β Smart Detection: Only creates export when repository is new or keys missing
- β
Password Protection: ZIP archives can be encrypted with
RECOVERY_ZIP_PASSWORD - β Unique Identification: Filename includes profile, hostname, and repository ID
- β Complete Information: Includes repository key, UUIDs, paths, and restore instructions
- β One-Time Export: Repository keys are static β only one export needed per repository
Configuration (in common.env):
export RECOVERY_ENABLED="true" # Enable/disable recovery exports
export RECOVERY_DIR="/home/user/Projekte/linux-backup-system/recovery"
export RECOVERY_ZIP_PASSWORD="your-secure-password" # ZIP encryption
export RECOVERY_OWNER="user:user" # File ownershipImportant Security Notes:
- Repository Key (exported by this system to recovery/)
- Borg Passphrase (store separately in password manager/safe!)
- Encrypted USB drive (offsite)
- Secure cloud storage (encrypted)
- Physical safe
- Multiple secure locations for redundancy
See exported RECOVERY-README.txt for detailed disaster recovery instructions.
- Ubuntu 24.04 LTS (or compatible)
- BorgBackup (
sudo apt install borgbackup) - curl (
sudo apt install curl) - hdparm (
sudo apt install hdparm) - for HDD spindown - zip (
sudo apt install zip) - for recovery key export - Shelly Plug Plus (optional, can be disabled)
- External HDD with ext4 filesystem
MIT License - see LICENSE for details.
- BorgBackup - The excellent deduplicating backup program
Questions? Issues? Open an issue!
