2 changed files with 3 additions and 103 deletions
+1 -32
View File
@@ -47,37 +47,6 @@ Run once, by hand, on the server:
# create a remote named "scaleway", type S3, matching your Scaleway # create a remote named "scaleway", type S3, matching your Scaleway
# Object Storage credentials and region # Object Storage credentials and region
``` ```
7. Set up email notifications (optional — skip this if you don't want mail):
Install a mail transport agent (choose one):
```bash
# Option A: mailutils (Debian/Ubuntu) — includes /usr/bin/mail
apt install -y mailutils
# Option B: msmtp + mailutils (lightweight, no full MTA)
apt install -y msmtp msmtp-mta mailutils
```
Configure the recipient address. Either set `MAIL_TO` in the environment
(`/etc/environment` or the cron file itself) or edit `borg-backup.sh`:
```
MAIL_TO="${MAIL_TO:-you@example.com}"
```
If using `msmtp`, configure `/etc/msmtprc`:
```
# /etc/msmtprc — example for Gmail SMTP
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
account default
host smtp.gmail.com
port 587
from backup-sender@gmail.com
user backup-sender@gmail.com
password your-app-password
```
The script auto-detects which command is available (`mail` → `sendmail` → `msmtp`)
and falls back silently if none is present — notifications are never fatal.
## 2. Deploying the Scripts ## 2. Deploying the Scripts
@@ -93,9 +62,9 @@ chmod +x /opt/backup-agent/dump_db.sh
## 3. Scheduling ## 3. Scheduling
Add a cron entry to run the backup daily, off-peak: Add a cron entry to run the backup daily, off-peak:
``` ```
# /etc/cron.d/borg-backup # /etc/cron.d/borg-backup
MAIL_TO=you@example.com
30 2 * * * root /opt/backup-agent/borg-backup.sh >> /var/log/borg/cron.log 2>&1 30 2 * * * root /opt/backup-agent/borg-backup.sh >> /var/log/borg/cron.log 2>&1
``` ```
+2 -71
View File
@@ -21,7 +21,7 @@ umask 077
# ========================= CONFIGURATION ========================= # ========================= CONFIGURATION =========================
NAME="borg-2025" NAME="chaudron"
REPO="/home/srv/files/backups/$NAME" REPO="/home/srv/files/backups/$NAME"
TARGET="/home/srv/files/content" TARGET="/home/srv/files/content"
@@ -37,8 +37,7 @@ LOGDIR="/var/log/borg"
LOG_RETENTION_DAYS=90 LOG_RETENTION_DAYS=90
RCLONE_REMOTE="scaleway" RCLONE_REMOTE="scaleway"
RCLONE_PATH="par-backup-1/$NAME" RCLONE_PATH="cyanet-backups-prod/$NAME"
RCLONE_MAX_DELETE=200
LOCKFILE="/var/lock/borg-backup.lock" LOCKFILE="/var/lock/borg-backup.lock"
@@ -58,10 +57,6 @@ REQUIRED_CMDS=(borg rclone docker timeout flock date find)
# ================================================================= # =================================================================
# Email notification (set to your address to receive backup summary).
# Leave empty to skip notifications. The script auto-detects mail/sendmail/msmtp.
MAIL_TO="${MAIL_TO:-}"
SELF="$(readlink -f "$0")" SELF="$(readlink -f "$0")"
mkdir -p "$LOGDIR" mkdir -p "$LOGDIR"
LOGFILE="${BORG_BACKUP_LOGFILE:-$LOGDIR/backup-$(date +%Y-%m-%d-%H%M%S).log}" LOGFILE="${BORG_BACKUP_LOGFILE:-$LOGDIR/backup-$(date +%Y-%m-%d-%H%M%S).log}"
@@ -93,68 +88,6 @@ run_cmd() {
"$@" "$@"
} }
# Detect available mail transport. Returns the command name or empty string.
find_mail_cmd() {
for cmd in mail sendmail msmtp; do
command -v "$cmd" >/dev/null 2>&1 && { echo "$cmd"; return 0; }
done
return 1
}
# Send an email notification about the backup result.
# Called from cleanup() so the log file is complete.
# Non-fatal — mail failures do not affect the backup exit code.
send_notification() {
local exit_code=$1 status started ended
[[ -n "$MAIL_TO" ]] || return 0
local mail_cmd
mail_cmd="$(find_mail_cmd)" || { log "Notification skipped: no mail command found"; return 0; }
if [ "$exit_code" -eq 0 ]; then
status="SUCCESS"
else
status="FAILED (exit $exit_code)"
fi
started="$(head -1 "$LOGFILE" 2>/dev/null | sed -n 's/^\[\(.*\)\].*/\1/p' || echo "?")"
ended="$(date '+%F %T')"
# Build message body with headers and log tail.
{
echo "Subject: Backup $status - $(hostname -s)"
echo "To: $MAIL_TO"
echo ""
echo "===== Backup Summary ====="
echo "Host: $(hostname -f)"
echo "Archive: $ARCHIVE"
echo "Status: $status"
echo "Started: $started"
echo "Ended: $ended"
echo "Log: $LOGFILE"
echo ""
echo "----- Last 80 lines of backup log -----"
tail -80 "$LOGFILE" 2>/dev/null || echo "(log unavailable)"
} | case "$mail_cmd" in
mail)
mail -s "Backup $status - $(hostname -s)" "$MAIL_TO"
;;
sendmail)
sendmail "$MAIL_TO"
;;
msmtp)
msmtp "$MAIL_TO"
;;
esac
local rc=$?
if [ "$rc" -eq 0 ]; then
log "Notification sent to $MAIL_TO (via $mail_cmd)"
else
log "WARNING: notification via $mail_cmd failed (exit $rc)"
fi
}
container_running() { container_running() {
[[ "$(docker inspect -f '{{.State.Running}}' "$1" 2>/dev/null || echo false)" == "true" ]] [[ "$(docker inspect -f '{{.State.Running}}' "$1" 2>/dev/null || echo false)" == "true" ]]
} }
@@ -209,7 +142,6 @@ cleanup() {
log "=== Backup FAILED (exit $exit_code) ===" log "=== Backup FAILED (exit $exit_code) ==="
fi fi
log "Full log: $LOGFILE" log "Full log: $LOGFILE"
send_notification "$exit_code"
} }
# Returns 0 if the repo is encrypted, 1 if not. # Returns 0 if the repo is encrypted, 1 if not.
@@ -371,5 +303,4 @@ run_cmd timeout "$SYNC_TIMEOUT" \
--retries-sleep 10s \ --retries-sleep 10s \
--low-level-retries 20 \ --low-level-retries 20 \
--s3-no-check-bucket \ --s3-no-check-bucket \
--max-delete "$RCLONE_MAX_DELETE" \
"$REPO" "${RCLONE_REMOTE}:${RCLONE_PATH}" "$REPO" "${RCLONE_REMOTE}:${RCLONE_PATH}"