A simple, configurable backup solution for git repositories using rsync with automatic rotation, managed by systemd with journald logging.
- ✓ Incremental backups using rsync
- ✓ Timestamped backup directories
- ✓ Automatic rotation (keeps N most recent backups)
- ✓ Automatic compression of old backups (keep recent ones uncompressed for fast restoration)
- ✓ Configuration file for all settings
- ✓ systemd service and timer for reliable scheduling
- ✓ Centralized logging via journald
- ✓ Easy monitoring with systemctl and journalctl
sudo ./install.sh# build script (requires go)
make
# Copy script to system
sudo cp ./bin/git-backup /usr/local/bin/git-backup
sudo chmod +x /usr/local/bin/git-backup
# Create config directory
sudo mkdir -p /etc/git-backup
sudo cp backup-config.conf /etc/git-backup/
# Install systemd units
sudo cp git-backup.service /etc/systemd/system/
sudo cp git-backup.timer /etc/systemd/system/
sudo systemctl daemon-reloadEdit /etc/git-backup/backup-config.conf:
# Source directory containing git repositories
SOURCE_PATH="/srv/git/repos"
# Destination directory for backups
BACKUP_PATH="/mnt/backups/git"
# Number of backup copies to keep
KEEP_COPIES=7
# Number of recent backups to keep uncompressed (for fast restoration)
# Older backups will be compressed to .tar.gz to save space
# Set to 0 to disable compression
KEEP_UNCOMPRESSED=2
# Backup interval (documentation only, actual schedule via timer)
BACKUP_INTERVAL_HOURS=24| Option | Description | Example |
|---|---|---|
SOURCE_PATH |
Directory containing git repositories to backup | /srv/git |
BACKUP_PATH |
Where backup directories will be created | /mnt/backups/git |
KEEP_COPIES |
Number of recent backups to keep (older ones deleted) | 7 (keep 1 week) |
KEEP_UNCOMPRESSED |
Number of recent backups to keep uncompressed (older ones compressed to .tar.gz) | 2 (keep 2 newest uncompressed) |
BACKUP_INTERVAL_HOURS |
Documentation of intended schedule | 24 (daily) |
# Enable and start the timer
sudo systemctl enable --now git-backup.timer
# Check timer status
sudo systemctl list-timers git-backup.timer# Run backup immediately
sudo systemctl start git-backup.service
# Check if it's running
sudo systemctl status git-backup.service# Follow live logs
sudo journalctl -u git-backup.service -f
# View last 50 lines
sudo journalctl -u git-backup.service -n 50
# View logs from today
sudo journalctl -u git-backup.service --since today
# View logs from specific date
sudo journalctl -u git-backup.service --since "2024-12-01" --until "2024-12-18"
# Show only errors
sudo journalctl -u git-backup.service -p err# Check service status
sudo systemctl status git-backup.service
# Check timer status (when next backup will run)
sudo systemctl list-timers git-backup.timer
# Check if timer is enabled
sudo systemctl is-enabled git-backup.timerEdit /etc/systemd/system/git-backup.timer:
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=30min[Timer]
OnCalendar=*-*-* 00,06,12,18:00:00
Persistent=true
RandomizedDelaySec=30min[Timer]
OnCalendar=*-*-* 02,14:00:00
Persistent=true
RandomizedDelaySec=30min[Timer]
OnCalendar=Sun *-*-* 03:00:00
Persistent=true
RandomizedDelaySec=30min[Timer]
OnCalendar=Mon,Wed,Fri *-*-* 01:00:00
Persistent=true
RandomizedDelaySec=30minAfter editing the timer:
sudo systemctl daemon-reload
sudo systemctl restart git-backup.timerBackups are stored with timestamps. Recent backups remain as directories, older ones are compressed:
/mnt/backups/git/
├── backup_20241218_020000/ (newest - uncompressed directory)
├── backup_20241217_020000/ (recent - uncompressed directory)
├── backup_20241216_020000.tar.gz (older - compressed)
├── backup_20241215_020000.tar.gz (older - compressed)
└── ...
- Script creates new backup with timestamp:
backup_YYYYMMDD_HHMMSS - After backup completes, script counts existing backups
- If count >
KEEP_COPIES, oldest backups are deleted - Only the N most recent backups are kept
Example with KEEP_COPIES=3:
- Before: 3 backups exist
- Run backup → creates 4th backup
- Rotation → deletes oldest, keeps 3 newest
Old backups are automatically compressed to save disk space while keeping recent backups as directories for fast restoration.
Process:
- After successful backup and before rotation
- Script identifies backups older than the N most recent (where N =
KEEP_UNCOMPRESSED) - Compresses them to
.tar.gzusing gzip - Removes original directory after successful compression
- Rotation then considers both compressed and uncompressed backups
Example with KEEP_UNCOMPRESSED=2, KEEP_COPIES=7:
- Latest 2 backups: uncompressed directories (fast access)
- Next 5 oldest: compressed
.tar.gzfiles (space efficient) - Anything older: deleted
Disable compression: Set KEEP_UNCOMPRESSED=0 in config file
ls -lh /mnt/backups/git/sudo journalctl -u git-backup.service -n 100 --no-pagerdu -sh /mnt/backups/git/backup_*find /mnt/backups/git -maxdepth 1 -type d -name "backup_*" | sort# Start backup immediately
sudo systemctl start git-backup.service
# Stop a running backup
sudo systemctl stop git-backup.service
# Restart the timer
sudo systemctl restart git-backup.timer
# Disable automatic backups (but don't stop current)
sudo systemctl disable git-backup.timer
# Enable automatic backups
sudo systemctl enable git-backup.timer
# Check service status
sudo systemctl status git-backup.service
# Check timer status
sudo systemctl status git-backup.timer# Restore specific repository
rsync -av /mnt/backups/git/backup_20241218_020000/myrepo.git/ /srv/git/myrepo.git/
# Restore all repositories
rsync -av /mnt/backups/git/backup_20241218_020000/ /srv/git/# Extract first, then restore
cd /mnt/backups/git
tar -xzf backup_20241218_020000.tar.gz
rsync -av backup_20241218_020000/ /srv/git/
# Or extract and restore specific repository
tar -xzf /mnt/backups/git/backup_20241218_020000.tar.gz -C /tmp
rsync -av /tmp/backup_20241218_020000/myrepo.git/ /srv/git/myrepo.git/
rm -rf /tmp/backup_20241218_020000sudo journalctl -u git-backup.service -p err --since today# Run directly (not via systemd)
sudo /usr/local/bin/git-backup# Ensure backup script can read source
sudo chmod +r /path/to/repos
# Ensure backup destination is writable
sudo chown root:root /mnt/backups/git# Check if timer is active
sudo systemctl is-active git-backup.timer
# Check if timer is enabled
sudo systemctl is-enabled git-backup.timer
# Enable and start if needed
sudo systemctl enable --now git-backup.timer# Check available space
df -h /mnt/backups/git
# See backup sizes
du -sh /mnt/backups/git/backup_*
# Consider reducing KEEP_COPIES if space is tight# List all timers
sudo systemctl list-timers --all
# Show timer details
sudo systemctl show git-backup.timer# Compare file counts
find /srv/git -type f | wc -l
find /mnt/backups/git/backup_20241218_020000 -type f | wc -l
# Check specific git repository
cd /mnt/backups/git/backup_20241218_020000/myrepo.git
git fsckCreate /etc/systemd/system/git-backup-notify@.service:
[Unit]
Description=Backup notification for %i
[Service]
Type=oneshot
ExecStart=/usr/bin/bash -c 'echo "Backup %i failed" | mail -s "Backup Alert" admin@example.com'Edit /etc/systemd/system/git-backup.service and add:
[Unit]
OnFailure=git-backup-notify@%n.serviceUpdate BACKUP_PATH in config to use SSH:
BACKUP_PATH="user@backup-server:/backups/git"Ensure SSH key authentication is set up for passwordless access.
Edit /etc/systemd/system/git-backup.service:
[Service]
# Run with lower priority
Nice=19
IOSchedulingClass=idle
# Limit CPU usage
CPUQuota=50%
# Memory limit
MemoryMax=1G- Service runs as root by default (needed for file access)
- Uses
NoNewPrivileges=yesandPrivateTmp=yesfor security hardening - Consider running as dedicated backup user with appropriate permissions
- Secure backup destination (proper filesystem permissions)
- Consider encrypting backup destination
- For remote backups, use SSH key authentication
- Regularly test restore procedures
- Better logging: All output goes to journald (structured logging)
- Status monitoring:
systemctl statusshows current state - Dependency handling: Service can depend on network, mounts, etc.
- Failure recovery: Built-in retry on failure
- Persistent timers: Missed backups run on next boot
- Resource control: Easy CPU/memory limits
- Notification: Built-in failure notifications
The Persistent=true option means:
- If server was off when backup should have run
- Backup runs immediately after boot
- Ensures you don't miss backups due to downtime
# Stop and disable timer
sudo systemctl disable --now git-backup.timer
# Remove systemd units
sudo rm /etc/systemd/system/git-backup.{service,timer}
sudo systemctl daemon-reload
# Remove script and config
sudo rm /usr/local/bin/git-backup
sudo rm -rf /etc/git-backup
# Optionally remove backups
# sudo rm -rf /mnt/backups/gitFree to use and modify for your needs.