> /home/jd
ES / EN

Automated Rsync Backup from Synology NAS to USB via Proxmox LXC

An automated incremental contingency system using rsync between Synology DSM and an LXC container.

📌 Context and Architecture

đź”™ Main context: This guide is part of the architecture documented in the Infrastructure MOC.

Objective: Build an automated, local backup system that remains usable during an internet outage and incrementally copies the Dropbox and G-Drive directories from the Synology NAS—the source of truth—to an external drive.

Target hardware: A 320 GB external HDD formatted as exFAT for portability. If the server fails, the drive can be disconnected and read immediately by the primary workstation (Hackintosh).

Architecture: Synology NAS → Local network → Proxmox node → Privileged LXC → 320 GB USB HDD.


⚙️ Phase 1: Prepare the Source (Synology DSM)

To follow the principle of least privilege, the container uses a dedicated, restricted account instead of a DSM administrator account.

  1. Go to Control Panel > User & Group > Create.
  2. Credentials:
    • User: backupjd.
    • A unique, strong password stored outside the script.
  3. Shared-folder permissions: Grant Read only access exclusively to the Drive folder that contains the source data.
  4. Application permissions: Ensure that the account is allowed to use SMB.

đź’˝ Phase 2: Persistently Mount the HDD on the Proxmox Host

Proxmox may assign a different device name after the USB drive is reconnected—for example, /dev/sdb may become /dev/sde. Mounting by UUID avoids depending on that dynamic name.

  1. Get the partition UUID:
Bash
blkid /dev/sde2  # Replace with the correct partition

Record the alphanumeric value, for example 69A3-7677.

  1. Edit the filesystem table:
Bash
nano /etc/fstab
  1. Add the mount rule at the end of the file:
Text
UUID=69A3-7677 /mnt/backup_contingencia exfat defaults,nofail 0 0

The nofail option is important: if the server starts while the USB drive is disconnected, Proxmox can continue booting instead of treating the missing mount as fatal.

  1. Reload systemd and mount the filesystems:
Bash
systemctl daemon-reload
mount -a

📦 Phase 3: Create and Configure the LXC

The intermediary container needs elevated permissions to mount a network filesystem.

  1. Create the LXC:
    • ID: 102 (name: rsync-backup).
    • OS: Debian 12.
    • Resources: 1 CPU core, 1024 MB RAM, DHCP networking.
    • ⚠️ Critical: On the General tab, clear the Unprivileged container option.
  2. Enable network-filesystem support:
    • With the LXC created but stopped, go to Options > Features.
    • Enable SMB/CIFS. Without it, AppArmor blocks mount.cifs.
  3. Bind-mount the host directory into the LXC:

Run this command on the Proxmox host, not inside the container:

Bash
pct set 102 -mp0 /mnt/backup_contingencia,mp=/mnt/usb_contingencia

📜 Phase 4: Script and Automate the Backup Inside the LXC

  1. Start the LXC, open its console, and install the dependencies:
Bash
apt update && apt install rsync cifs-utils nano -y
mkdir -p /mnt/dsm_drive
  1. Create the Bash script:
Bash
nano /root/backup.sh
  1. Add the final script (backup.sh):
Bash
#!/bin/bash

LOG_GENERAL="/var/log/backup_estado.log"
echo "========================================" >> $LOG_GENERAL
echo "Starting contingency backup: $(date)" >> $LOG_GENERAL

# echo_interval=60 and serverino keep the CIFS session responsive.
if mount -t cifs -o username=[YOUR_USER],password=[YOUR_PASSWORD],ro,vers=3.0,echo_interval=60,serverino //192.168.X.X/Drive /mnt/dsm_drive; then

    echo "Connection established. Copying files..." >> $LOG_GENERAL

    # Mirror Dropbox while tolerating I/O latency.
    rsync -rtvh --delete --timeout=600 --log-file=/var/log/backup_archivos.log /mnt/dsm_drive/Dropbox/ /mnt/usb_contingencia/Dropbox/

    # Mirror Google Drive while tolerating I/O latency.
    rsync -rtvh --delete --timeout=600 --log-file=/var/log/backup_archivos.log /mnt/dsm_drive/G-Drive/ /mnt/usb_contingencia/G-Drive/

    umount /mnt/dsm_drive
    echo "Backup completed. Network share disconnected: $(date)" >> $LOG_GENERAL

else
    echo "CRITICAL ERROR: Unable to connect to DSM." >> $LOG_GENERAL
    exit 1
fi

Replace the bracketed credentials before using the script and ensure the file remains readable only by root.

  1. Make the script executable:
Bash
chmod +x /root/backup.sh
  1. Schedule the cron job to run every day at 1:00 a.m.:
Bash
crontab -e

Add this line at the end of the file:

Text
0 1 * * * /root/backup.sh > /var/log/cron_backup.log 2>&1

🛑 Troubleshooting Log

The following issues appeared while implementing the architecture.

  • Error: mount error(1): Operation not permitted

    • Diagnosis: The container was created as unprivileged. The Linux kernel prevents it from mounting SMB/CIFS filesystems.
    • Solution: Recreate the LXC with Unprivileged container cleared.
  • Error: mount error(13): Permission denied even though the credentials are correct.

    • Diagnosis (AppArmor): Proxmox blocked the mount operation. The kernel log (dmesg | tail -n 10) showed apparmor="DENIED" operation="mount" fstype="cifs".
    • Solution: Enable SMB/CIFS under the LXC’s Options > Features, then restart the container.
  • Error: mount error(13): Permission denied caused by protocol negotiation.

    • Diagnosis: Debian 12 and Synology DSM negotiated incompatible SMB dialect settings.
    • Solution: Add vers=3.0 to the mount.cifs options.
  • Error: rsync error: some files/attrs were not transferred (code 23)

    • Diagnosis: The -a archive option attempts to preserve Unix ownership and permissions. The exFAT destination cannot represent those attributes.
    • Solution: Replace -avh with -rtvh to copy recursively while preserving modification times and retaining verbose, human-readable output.
  • Error: After disconnecting and reconnecting the USB drive, mounting fails with can't find in /etc/fstab.

    • Diagnosis: The kernel assigned a different device name; for example, /dev/sdb became /dev/sde.
    • Solution: Obtain the UUID with blkid and use it in /etc/fstab, as shown in Phase 2.
  • Error: rsync: [sender] write error: Broken pipe (32) accompanied by repeated code 23 failures.

    • Diagnosis: The CIFS session timed out while processing thousands of small files, such as files in Python virtual environments.
    • Solution: Add echo_interval=60,serverino to the CIFS mount options and --timeout=600 to the rsync commands.
  • Error: rsync: [receiver] mkstemp ... failed: Invalid argument (22)

    • Diagnosis: exFAT does not support several characters that may appear in source filenames: ?, ", |, <, >, *, :, \, and /.
    • Solution: Rename the affected source files and remove unsupported characters.