Compare commits

..
2 Commits
Author SHA1 Message Date
julien d99e4b1e52 Updates 2026-08-23 12:39:04 +02:00
julien 9ef4b9ea8b "Updates" 2026-08-23 12:35:34 +02:00
4 changed files with 184 additions and 8 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
$TTL 3600 $TTL 3600
@ IN SOA dns106.ovh.net. tech.ovh.net. (2085478106 86400 3600 3600000 300) @ IN SOA dns106.ovh.net. tech.ovh.net. (2086784538 86400 3600 3600000 300)
IN NS dns106.ovh.net. IN NS dns106.ovh.net.
IN NS ns106.ovh.net. IN NS ns106.ovh.net.
IN MX 100 mx3.mail.ovh.net. IN MX 100 mx3.mail.ovh.net.
+176
View File
@@ -0,0 +1,176 @@
A practical tutorial for people who moved to a ZimaBoard for the faster CPU and extra RAM, but want to keep using their existing Synology as a rock-solid backup target.
Disclaimer: this is a setup that works well for me. I take no responsibility for your data or hardware — test on your own gear, verify the results yourself, and be very careful with any command that writes to a disk.
The idea
Your ZimaBoard is now the workhorse — apps, containers, everything runs there. Your Synology is perfect for what its genuinely great at: reliable, cheap, roomy storage you write to and rarely read. This tutorial wires the two together so the ZimaBoard pushes versioned, space-efficient backups to the Synology every night, automatically.
What you get:
Dated snapshots — one folder per run (e.g. 2026-08-06_000006), each a complete browsable copy of your data at that moment.
Almost no extra space per snapshot — unchanged files are hardlinked, so keeping 14 daily snapshots costs roughly one copy + the daily changes, not 14 full copies. (In practice: one snapshot looks like 517 GB, but 14 of them plus the live mirror total just 542 GB on disk.)
Only changed data crosses the network each night — quick after the first run.
A locked-down backup key that cant get a shell or touch anything outside the backup folder.
How it works (two steps, and why)
Rather than one big rsync, the nightly job does two things:
Snapshot — runs on the Synology itself: copy the previous backup into a new dated folder with cp -al (a hardlink copy). Done locally this is near-instant.
Mirror — runs over SSH from the ZimaBoard: a plain rsync into a single CURRENT/ folder.
Snapshot first, mirror second — that way, if a mirror is ever interrupted, the previous nights snapshot is already complete and safe.
The Synology ends up looking like:
/volume2/Backups/
├── CURRENT/ ← the live mirror (updated in place each night)
├── 2026-08-05_000006/ ← dated hardlinked snapshot
├── 2026-08-06_000006/ ← dated hardlinked snapshot
└── ... ← up to however many you keep
Restore from a dated snapshot, never from CURRENT — CURRENT is the live mirror and could be mid-update.
Why the snapshot runs on the Synology, not over a mounted share: cp -al creates one hardlink per file. Run locally on the Synology, hardlinking a large tree takes a couple of minutes. Run across an SMB/network mount, each hardlink becomes a separate network round-trip — for a large file count that turns minutes into many hours. So we trigger the snapshot on the Synology. This one detail makes or breaks the whole thing.
Step 1 — Turn on rsync-over-SSH on the Synology
In DSM:
Control Panel → File Services → rsync → enable the rsync service. Note the SSH encryption port shown (often non-standard). Your backups use this port.
Leave the “rsync account” option off (thats an unencrypted mode you dont need).
Make sure your DSM user is in the administrators group (Control Panel → User & Group) — DSM only allows admin users to SSH in by default.
Enable user home service (Control Panel → User & Group → Advanced), so your user has a home directory with a ~/.ssh folder.
DSMs SSH is strict about a couple of things:
~/.ssh must be 700 and ~/.ssh/authorized_keys must be 600, or DSM quietly ignores your key and falls back to password auth.
If rapid failed attempts get your source blocked, clear it under Control Panel → Security → Account (Auto Block).
Step 2 — Create a key and restrict it with rrsync
On the ZimaBoard, generate a key with no passphrase (an automated job cant type one). Put it somewhere that survives reboots — on ZimaOS, keep it on your data pool, e.g. under /media/YOURPOOL/scripts/.ssh/:
ssh-keygen -t ed25519 -f /media/YOURPOOL/scripts/.ssh/synology_backup -N ""
rrsync is the official wrapper that confines a key to rsync-only, in one folder. Fetch it into your DSM users home (over SSH to the Synology):
mkdir -p ~/bin
curl -o ~/bin/rrsync https://raw.githubusercontent.com/RsyncProject/rsync/master/support/rrsync
chmod +x ~/bin/rrsync
(head -1 ~/bin/rrsync tells you if it wants python3 or perl — DSM has both.)
Install the public key on the Synology with a forced command, so this key can only run rsync into your backup folder — no shell, no other paths:
# on the Synology, append (>> not >) to ~/.ssh/authorized_keys:
cat >> ~/.ssh/authorized_keys <<'EOF'
command="/var/services/homes/YOURUSER/bin/rrsync '/volume2/Backups'",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding ssh-ed25519 AAAA...your_public_key... zimaboard
EOF
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Use plain rrsync '...', not -wo — write-only mode blocks reading the destination, which the snapshot approach and any restore need.
Test: an rsync of a small file should work; a plain ssh with this key should not give you a shell. That refusal is the lockdown doing its job.
Step 3 — The snapshot script (lives on the Synology)
Put this on the Synology as ~/bin/snapshot.sh (adjust path and retention):
#!/bin/sh
BACKUP_ROOT="/volume2/Backups"
RETENTION=14
TS=$(date +%Y-%m-%d_%H%M%S)
cd "$BACKUP_ROOT" || exit 1
[ -d CURRENT ] || { echo "No CURRENT yet, skipping"; exit 0; }
echo "Snapshotting CURRENT -> $TS"
mkdir -p "$TS"
cp -al CURRENT/. "$TS/" || { rm -rf "$TS"; echo "cp -al failed"; exit 1; }
TOTAL=$(ls -1d [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_* 2>/dev/null | wc -l)
if [ "$TOTAL" -gt "$RETENTION" ]; then
ls -1d [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_* | sort | head -n $((TOTAL-RETENTION)) \
| while read -r old; do echo "Pruning $old"; rm -rf "$old"; done
fi
echo "Done: $TS"
chmod +x ~/bin/snapshot.sh and test it once by hand — it should finish in a couple of minutes and leave a dated folder.
Trigger it remotely with a second restricted key, so the nightly job can run it without a shell. Generate a second key on the ZimaBoard (synology_snapshot), and install its public key on the Synology forced to run only this script:
command="/var/services/homes/YOURUSER/bin/snapshot.sh",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding ssh-ed25519 AAAA...second_public_key... zimaboard
Now SSHing in with that key just runs the snapshot and disconnects.
Step 4 — The nightly backup script (on the ZimaBoard)
#!/bin/sh
SSH="ssh -p PORT -o StrictHostKeyChecking=accept-new"
SNAPKEY=/media/YOURPOOL/scripts/.ssh/synology_snapshot
MIRRORKEY=/media/YOURPOOL/scripts/.ssh/synology_backup
USER_HOST="youruser@your.synology.ip"
# 1. Snapshot CURRENT server-side (fast, local cp -al)
$SSH -i "$SNAPKEY" "$USER_HOST" || { echo "snapshot failed"; exit 1; }
# 2. Mirror your data -> CURRENT
rsync -aH --delete --stats \
-e "$SSH -i $MIRRORKEY" \
/media/YOURPOOL/ "$USER_HOST":/CURRENT/
Adjust the pool path, port, and add any excludes you need (see the traps below).
Scheduling on ZimaOS: ZimaOS clears the system crontab on reboot, so dont rely on plain cron. Run the schedule from a small Docker container instead (e.g. the lightweight Ofelia scheduler, deployed as a Portainer stack) — Docker state lives on your pool and survives reboots, so the schedule sticks. Point one Ofelia job at your backup script on your chosen time (e.g. daily at 00:00).
Two traps worth knowing before your first run
1. rsync include/exclude order is first-match-wins. If you exclude a big directory but want to keep a subfolder of it, the --include for that subfolder must come before the broad --exclude, or it never takes effect and you silently lose that data. Correct order:
--include='/somedir/'
--include='/somedir/keepthis/***'
--exclude='/somedir/**'
2. Some objects cant be rsynced, and thats normal. Live special files (block devices, sockets, certain container-runtime internals under Dockers overlay2) make rsync return exit 23 (“some files not transferred”) — the real files still went across fine. If you back up your whole pool, exclude Dockers runtime layers (keep docker/volumes, skip docker/overlay2 and friends), plus the SMB recycle bin (.trash) and @eaDir metadata. Treat exit codes 23 and 24 (files vanished mid-copy — normal on a live system) as OK in your script; anything else is a real failure.
Verify it — the important part
Check hardlinks are real (snapshots share storage instead of duplicating). On the Synology:
find "/volume2/Backups/<a-dated-snapshot>/<some/known/file/path>" -type f -printf '%n %p\n' | head
Link count 2 or more = shared with CURRENT, working correctly. 1 = somethings full-copying.
Check the real total size — the decisive space test. On the Synology:
du -sh "/volume2/Backups"
du counts hardlinked files only once, so CURRENT + all snapshots together should total roughly one copy plus accumulated changes — not N copies. (Example: 14 snapshots + mirror = 542 GB, versus 517 GB for a single snapshot. The 25 GB difference is two weeks of real changed data.) Note: File Station → Properties on one snapshot shows the full ~517 GB because it counts each hardlink as a full file — that number is misleading. du -sh on the whole Backups folder is the true figure.
Check the data you expect is actually there:
ls "/volume2/Backups/CURRENT/"
ls "/volume2/Backups/CURRENT/<a/subfolder/you/care/about>"
Restoring
One file: browse the right dated snapshot on the Synology (File Station or SSH) and copy it out — plain hardlinked folders, no special tooling.
Full restore: rsync a dated snapshot back to the ZimaBoard using your normal Synology login (the restricted keys are for pushing):
rsync -aH --info=progress2 -e "ssh -p PORT" \ "youruser@synology:/volume2/Backups/2026-MM-DD_HHMMSS/" /media/YOURPOOL/
Fix ownership afterward if an app expects specific file owners (databases, web apps, etc.).
Getting the most out of the Synology as a target
Throughput will be modest, and thats expected. A single rsync stream over gigabit, with lots of small files, runs well under line speed because per-file overhead dominates. A link-aggregation bond doesnt speed up a single stream. The first full run takes a while; every run after is fast because only changes move.
Keep the versioning on the Synology (Step 3). Its the difference between a 2-minute snapshot and an hours-long one.
If a run ever fails with “another instance is already accessing this directory,” a killed transfer left a process running on the Synology holding the lock. SSH in, ps aux | grep rsync, and kill the leftover rrsync/rsync --server PIDs — but not the SynoRsync … --daemon one (thats DSMs own service).
Expect a “login at an unusual time” notice if you back up at night — thats DSM (via Active Insight) noticing your own nightly login. Either widen the “usual” window in Active Insight or accept the notice; dont disable login alerts entirely.
Keep the disks healthy (SMART checks, periodic scrubs). This is where your safety net lives.
One habit to keep
Verify on disk. Exit codes and “done” messages dont prove your data is there. Check the destination contents, the link counts, and the real du size yourself now and then. It takes a minute and its the difference between hoping you have a backup and knowing you do.
Binary file not shown.
+7 -7
View File
@@ -92,7 +92,7 @@ borg.delmar.bzh {
gzip gzip
minimum_length 1024 minimum_length 1024
} }
reverse_proxy patrick:8080 reverse_proxy carlo:8081
} }
cap.delmar.bzh { cap.delmar.bzh {
@@ -241,7 +241,7 @@ hmr.delmar.bzh {
gzip gzip
minimum_length 1024 minimum_length 1024
} }
reverse_proxy bob:17080 reverse_proxy bob:39084
} }
homepage.delmar.bzh { homepage.delmar.bzh {
@@ -299,7 +299,7 @@ jellyfin.delmar.bzh {
gzip gzip
minimum_length 1024 minimum_length 1024
} }
reverse_proxy patrick:8096 reverse_proxy carlo:8096
} }
jellyseerr.delmar.bzh { jellyseerr.delmar.bzh {
@@ -308,7 +308,7 @@ jellyseerr.delmar.bzh {
gzip gzip
minimum_length 1024 minimum_length 1024
} }
reverse_proxy patrick:5055 reverse_proxy carlo:5055
} }
kontadenn.delmar.bzh { kontadenn.delmar.bzh {
@@ -366,7 +366,7 @@ mmgr.delmar.bzh {
gzip gzip
minimum_length 1024 minimum_length 1024
} }
reverse_proxy bikinibottom:38274 reverse_proxy carlo:38274
} }
mmm.delmar.bzh { mmm.delmar.bzh {
@@ -659,7 +659,7 @@ wizarr.delmar.bzh {
gzip gzip
minimum_length 1024 minimum_length 1024
} }
reverse_proxy patrick:5690 reverse_proxy carlo:5690
} }
www.delmar.bzh { www.delmar.bzh {
@@ -701,5 +701,5 @@ zik.delmar.bzh {
gzip gzip
minimum_length 1024 minimum_length 1024
} }
reverse_proxy patrick:4533 reverse_proxy carlo:4533
} }