Compare commits

...
20 Commits
Author SHA1 Message Date
julien 33bd2dfeed "Updates" 2026-09-19 15:31:55 +02:00
julien d3833df125 "Updates" 2026-09-17 08:52:54 +02:00
julien 97b1fa7dd1 "Updates" 2026-09-17 08:51:44 +02:00
julien 25c58446e9 "Updates" 2026-08-29 16:08:34 +02:00
julien d99e4b1e52 Updates 2026-08-23 12:39:04 +02:00
julien 9ef4b9ea8b "Updates" 2026-08-23 12:35:34 +02:00
julien 76d2d806a9 "Updates" 2026-07-31 18:23:18 +02:00
julien d4a4139a0c "Updates" 2026-07-29 12:40:25 +02:00
julien 6149daa549 "Updates" 2026-07-22 09:06:08 +02:00
julien ee6bd1a243 "Updates" 2026-07-21 10:16:31 +02:00
julien 8e3d295e8f "Updates" 2026-07-14 11:22:58 +02:00
julien d07497e061 "Updates" 2026-07-09 00:30:55 +02:00
julien 1efbee942d "Updates" 2026-07-08 19:20:59 +02:00
julien 9915b7a062 "Updates" 2026-07-08 19:20:28 +02:00
julien fbc4ee8ab9 "Updates" 2026-07-08 19:12:57 +02:00
julien e2dbd9620a "Updates" 2026-07-08 19:12:47 +02:00
julien 14c3f2d41b "Updates" 2026-07-08 18:48:07 +02:00
julien 317fc9cb9a "Updates" 2026-07-08 17:41:13 +02:00
julien 71bb00b711 "Updates" 2026-07-08 17:39:21 +02:00
julien 60560cc343 "Updates" 2026-07-08 16:44:22 +02:00
8 changed files with 393 additions and 493 deletions
+7 -4
View File
@@ -1,5 +1,5 @@
$TTL 3600
@ IN SOA dns106.ovh.net. tech.ovh.net. (2080297022 86400 3600 3600000 300)
@ IN SOA dns106.ovh.net. tech.ovh.net. (2089201818 86400 3600 3600000 300)
IN NS dns106.ovh.net.
IN NS ns106.ovh.net.
IN MX 100 mx3.mail.ovh.net.
@@ -36,7 +36,7 @@ ftp IN CNAME delmar.bzh.
gen IN A 176.188.240.123
git IN A 176.188.240.123
gotify IN A 176.188.240.123
hdlp IN A 176.188.240.123
hmr IN A 176.188.240.123
home-assistant IN A 176.188.240.123
homepage IN A 176.188.240.123
imgs IN A 176.188.240.123
@@ -45,13 +45,14 @@ it IN A 176.188.240.123
jellyfin IN A 176.188.240.123
jellyseerr IN A 176.188.240.123
kontadenn IN A 176.188.240.123
lghn IN A 176.188.240.123
lnk IN A 176.188.240.123
lud IN A 176.188.240.123
minio IN A 176.188.240.123
mmgr IN A 176.188.240.123
mmm IN A 176.188.240.123
nds IN A 176.188.240.123
nsns IN A 176.188.240.123
oec IN A 176.188.240.123
octoprint IN A 176.188.240.123
ovhemp1116203-selector1._domainkey 60 IN CNAME ovhemp1116203-selector1._domainkey.274927.cq.dkim.mail.ovh.net.
ovhemp1116203-selector2._domainkey 60 IN CNAME ovhemp1116203-selector2._domainkey.274928.az.dkim.mail.ovh.net.
@@ -64,8 +65,10 @@ send IN A 176.188.240.123
shop IN A 176.188.240.123
smk IN A 176.188.240.123
stream IN A 176.188.240.123
swpt IN A 176.188.240.123
tournoi IN A 176.188.240.123
tpml IN A 176.188.240.123
trfk IN A 176.188.240.123
trek IN A 176.188.240.123
trmx IN A 176.188.240.123
twip IN A 176.188.240.123
ugo IN A 176.188.240.123
+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.
+2 -1
View File
@@ -67,7 +67,8 @@ UUID=B23E-A140 /boot/efi vfat umask=0077
# swap was on /dev/mmcblk0p3 during installation
UUID=69691db2-1213-4f0b-8797-a246e6924c4a none swap sw 0 0
# ssd
UUID=6702c3b0-eb03-4c1f-9ddc-56f4f7736222 /mnt/ssd ext4 defaults 0 0
# UUID=6702c3b0-eb03-4c1f-9ddc-56f4f7736222 /mnt/tmp ext4 defaults 0 0
UUID=bac562df-e5ca-4b1c-acde-d6c541a89ddb /mnt/ssd ext4 defaults 0 0
/mnt/ssd/var /var none bind
/mnt/ssd/tmp /tmp none bind
/mnt/ssd/home /home none bind
+120 -67
View File
@@ -110,7 +110,7 @@ sudo iptables -I INPUT -p 50 -j ACCEPT # allows ipsec when secure overlay is ena
### Install docker...
```
```bash
sudo apt install -y docker docker-compose git
sudo usermod -aG docker pleb
@@ -125,23 +125,23 @@ sudo chmod 666 /var/run/docker.sock
#### On master
```
```bash
docker swarm init --advertise-addr 192.168.1.14
```
> *Swarm initialized: current node (3kdxixaa86m8pvag6jn0b70ut) is now a manager*.
> *Swarm initialized: current node (z76p66jcv4c1c9qstodlpcfii) is now a manager.*
#### On nodes
```
docker swarm join --token SWMTKN-1-2px1bindhl41x9h6l4ve7x15iwjryr0uf3ekmu7hz4bezjewwh-ae9vv4657zcki160s71vjn75z 192.168.1.14:2377
```bash
docker swarm join --token SWMTKN-1-27tm53ylrwyesiq3ntfumfixw3gpe2164rnsume8sg07saz12v-1fig1jzehaocodm6hv322l4ku 192.168.1.14:2377
```
> *To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions*.
> Change ip range if necessary ?!
```
```bash
docker network rm ingress
# Create in different range
docker network create --driver overlay --ingress --subnet 192.168.1.0/16 --gateway 192.168.1.254 ingress
@@ -149,27 +149,45 @@ docker network create --driver overlay --ingress --subnet 192.168.1.0/16 --gatew
---
```
```bash
docker node ls
```
```
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
lqu5nr20g6vt2g1hc5xwm2njg gary Ready Active 29.6.1
3aqxc8mj1wk2qp1k0llodgyxw pearl Ready Active 29.6.1
z76p66jcv4c1c9qstodlpcfii * sandy Ready Active Leader 29.6.1
i62rvg0fh6c0hv49btb1qrfv4 sheldon Ready Active 29.6.1
```
```bash
docker network ls
```
```
NETWORK ID NAME DRIVER SCOPE
3b8a522fd1ad bridge bridge local
8624696f7cbd docker_gwbridge bridge local
116b95142a3a host host local
jg83q53aljof ingress overlay swarm
46f3064ef5dc none null local
```
---
## 3. Keepalived
> *Load Balancer : install on all nodes*
```
sudo apt-get -y install keepalived
```bash
sudo apt install keepalived -y
```
### On master node (sandy)
#### Create/Edit
> sudo vim /etc/keepalived/keepalived.conf
```bash
sudo vim /etc/keepalived/keepalived.conf
```
```
! Configuration File for keepalived
@@ -208,7 +226,9 @@ vrrp_instance VI_1 {
#### Create/Edit
> sudo vim /etc/keepalived/keepalived.conf
```bash
sudo vim /etc/keepalived/keepalived.conf
```
```
! Configuration File for keepalived
@@ -272,13 +292,13 @@ sudo systemctl start glusterd
sudo mkfs.xfs -f /dev/nvme0n1
# Create brick folder location
sudo mkdir -p /data/glusterfs/volume/brick
sudo mkdir -p /mnt/ssd
# Add line to /etc/fstab to auto mount this disk to /data on boot
echo "/dev/sda1 /data/glusterfs/volume/brick xfs defaults 0 0" | sudo tee -a /etc/fstab
echo "UUID=... /mnt/ssd xfs defaults 0 0" | sudo tee -a /etc/fstab
# Create brick folder
# sudo mkdir -p /data/glusterfs/volume/brick/brick
sudo mkdir -p /mnt/ssd/brick
# Mount
sudo mount -a
@@ -292,24 +312,48 @@ sudo gluster peer probe pearl
> Check
```
df -h /data/glusterfs/volume/brick
```bash
df -h /mnt/ssd/brick
sudo gluster pool list
```
```
UUID Hostname State
a40ae0e1-90ec-4ccb-88eb-0a13c6c0de26 gary Connected
fc215f6f-38df-4cce-a0d3-5297e0290d3f sheldon Connected
13233744-1367-4e76-b486-a0e0b8a190e7 pearl Connected
07702514-efbc-4ca6-950d-d9c0b14ff61c localhost Connected
```
```bash
sudo gluster peer status
```
```
Number of Peers: 3
Hostname: gary
Uuid: a40ae0e1-90ec-4ccb-88eb-0a13c6c0de26
State: Peer in Cluster (Connected)
Hostname: sheldon
Uuid: fc215f6f-38df-4cce-a0d3-5297e0290d3f
State: Peer in Cluster (Connected)
Hostname: pearl
Uuid: 13233744-1367-4e76-b486-a0e0b8a190e7
State: Peer in Cluster (Connected)
> For 2 nodes: sandy and sheldon for example
```
sudo gluster volume create docker-volume replica 2 transport tcp sandy:/mnt/ssd sheldon:/mnt/data force
```bash
sudo gluster volume create docker-volume replica 2 transport tcp sandy:/mnt/ssd sheldon:/mnt/ssd force
```
> For 3 nodes: sandy, sheldon and pearl for example
> For all nodes: sandy, gary, sheldon and pearl for example
```
sudo gluster volume create dockervolume disperse 3 redundancy 1 sandy:/data/glusterfs/volume/brick sheldon:/data/glusterfs/volume/brick pearl:/data/glusterfs/volume/brick force
```bash
sudo gluster volume create dockervolume disperse 4 redundancy 1 sandy:/mnt/ssd/brick gary:/mnt/ssd/brick sheldon:/mnt/ssd/brick pearl:/mnt/ssd/brick force
```
#### Start Gluster Volume
@@ -323,9 +367,9 @@ sudo gluster volume start dockervolume
> On all nodes :
```
sudo mkdir /mnt/data
sudo mkdir /mnt/ssd
echo "localhost:/dockervolume /mnt/data glusterfs defaults,_netdev 0 0" | sudo tee -a /etc/fstab
echo "sandy:/dockervolume /mnt/ssd/brick glusterfs defaults,_netdev 0 0" | sudo tee -a /etc/fstab
sudo mount -a
@@ -334,10 +378,10 @@ sudo systemctl daemon-reload
> Check
> > Persistent shared storage will be "/mnt/data"
> > Persistent shared storage will be "/mnt/ssd/brick"
```
df -h /mnt/data
df -h /mnt/ssd/brick
sudo gluster volume set dockervolume auth.allow 127.0.0.1,192.168.1.14,192.168.1.13,192.168.1.16,192.168.1.19
```
@@ -351,59 +395,68 @@ sudo gluster volume add-brick **volume** replica X **server:brick**
> Option
```
docker plugin install --alias glusterfs mikebarkmin/glusterfs SERVERS=sandy,gary,sheldon,pearl VOLNAME=dockervolume
docker plugin install --alias glusterfs mochoa/glusterfs-volume-plugin --grant-all-permissions --disable
docker plugin set glusterfs SERVERS=192.168.1.14,192.168.1.13,192.168.1.16,192.168.1.19
docker plugin enable glusterfs
docker plugin install --alias glusterfs chrisbecke/glusterfs-volume GFS_VOLUME=dockervolume GFS_SERVERS=192.168.1.14,192.168.1.13,192.168.1.16,192.168.1.19
docker plugin enable glusterfs
```
### 5. Portainer
# Use in a stack file
Create container directory
```
sudo mkdir -p /var/lib/docker/volumes
sudo mkdir -p /mnt/data/portainer
```
> Edit
```
sudo vim portainer-agent-stack.yml
```
> Down at the bottom, remove :
```
volumes:
portainer_data:
```
> And in Section services -&gt; portainer -&gt; volumes, change it to :
```
```ỳaml
services:
app:
image: myapp/web:latest
volumes:
- type: bind
source: /mnt/data/portainer/portainer_data
target: /data
- app_data:/app/data
volumes:
app_data:
driver: trajano/glusterfs-volume-plugin
driver_opts:
glusteropts: "--volfile-server=sandy --volfile-id=dockervolume"
subdir: "app_data"
```
> Deploy
### 5. data-dir
```
docker stack deploy -c portainer-agent-stack.yml portainer
Relocate /var/lib/docker
```bash
#Stop docker
sudo systemctl stop docker.service
sudo systemctl stop docker.socket
# Move data to SSD
sudo rsync -aP /var/lib/docker/ /mnt/ssd/brick/
# Edit docker config
sudo vim /etc/docker/daemon.json
```
#### Upgrade
```
docker service ls
{
"data-root": "/mnt/ssd/brick",
"log-driver": "local",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"dns": ["8.8.8.8", "8.8.4.4"]
}
```
docker pull portainer/portainer-ce:latest
```bash
# Restart docker
sudo systemctl start docker.socket
sudo systemctl start docker.service
docker service update --image portainer/portainer-ce:latest --publish-add 9443:9443 --force portainer_portainer
# Verify...
sudo systemctl status docker
sudo docker info | grep "Docker Root Dir"
docker pull portainer/agent:latest
docker service update --image portainer/agent:latest --force portainer_agent
# Delete old data folder
sudo rm -rf /var/lib/docker/
```
+12 -7
View File
@@ -31,6 +31,9 @@ ff02::3 ip6-allhosts
192.168.1.19 pearl
192.168.1.20 karen
# turinpi
192.168.1.21 bikinibottom bikinibottom.local
#
192.168.1.53 retropie
```
@@ -50,18 +53,20 @@ ssh-copy-id -f -i ~/.ssh/id_ed25519.pub pleb@<hostname>
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBLk24u7FT8PhAdM8EVUFGlOi0hle4CW8L284E1foUhS julien@julien-pc
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE6wr+DUbcfVTltoWT6gbPRY3geUYNhgN7/CLcMaMu0B eliot@toile-win
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKmttayKqj6Z290hMCc97v4dMZTSUz4lYgXR0NtcRr8U delmar@thinkpad
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFlta/YdJ0ztVpZsP/nh37Fn+H5Hxg/Mw+jR91f5Gf08 pleb@bob
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN9mn7tuYWTPLbH2MViAkiKSUkXj4NauCUgzj778LoQ2 pleb@carlo
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKL7A0xvHSfmWo+LUHdWWb03a5NXN1IlbLS5iSHxs3zw pleb@sandy
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHpnC0ftrLwzhsmonDtSvh38Oi5OGe1iOaQjlsm1RlD2 pleb@gary
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII9USnPFJx/PbV+KnlTN4o3f4SwiuHCWaAlE8aKcY4Ne pleb@sheldon
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMrWtBN3yRh3PHG+7UUeRUIvhuMcwtR88FzUn4xS5FyV pleb@pearl
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMfd7PrJ50jHrG6yjIJ5u7jKTyXi9mPn8/oa+HNAVNsf pleb@krabs
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKe0iX+Al5bdMf93lMj/V/1FUpXJsYG6XNw4C4Bpj7SS pleb@sandy
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPq4T605pUQsUAntUbFousyuJADISEHAk+MnZoq7lAYB pleb@gary
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKvQwj0zeF18hl/BcX7eRFgXb3TNFUGooOE1NJeNlX0z pleb@sheldon
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEPUeE7PCnDcEW7dtyWUdxDBD57GmIzeY1a0eFn0JiRX pleb@pearl
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILPeyUpr/R9KgX+0Yr8R9VLIoxREc+XC7EJA1aWDS/08 pleb@krabs
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJtrn0Oj2IstMwzheIZcJGBA8RNWTyNtksaK2LhvjNul pleb@bernie
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHh1LTS5PzsQ45xiRbyxBbCurI7JdEpdkCsbx3mlYaaR pleb@patrick
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAaOsYNLsoYcXDV3I7zbJABeIo7CJOdMxUN86rH1/IH9 pleb@karen
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG+Odc/LY/fPcapj6yxF6of+pygbaFvMg0P9RUA69pQV borg@3c6ee04b29c5
## krabs
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJDsfMBjZO/uWCoyvuYEKVq4wr6Z67gZRC3wRAimxdLS root@nextcloud-aio-borgbackup
## karen
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCiAt5dNKbcaYmmsMGitBTaYoKkfEMJ5dGBzbSIDF4WdmPQGFmNf3urAxM1MDjlW7ifzfia7I49pc+Sndo0+b/FiS9FIMWwlNmOrr5c24XXZvYhPvk2M9ghEhXgLGDO3N/xPG6M+rrEzeRFXCdlHZuan6/iUdGcDQtgS2q4ciAMCk8V0QptmvWYqgs/CMsnU3WULUA+abKdx605zzG0inF6pNFaIO50mRraPQsWq34SxXknkJnTBHFogj9NyV/Y9BhiKBm3IXn2f9cVK5/+8Af91w5uI07kpLnPXSDkEZFKK4J48dCdDLhOk2H1/PzapkwW5pF8dhBeKVW2W7oyvuWplKj7cS2dS9ODqqae5kgI83IBdOxQ3VONRCHiUVbezREON66xpOncmWq/YhegWCCHviHUzO0DtpxQXPnZTcX6LwiWhZ9vzGdostJ72i2PxtU155S5PBqaXF+u01cbD0yOuA364LIri2mJxgXHqQH6VwgUJYA7G3Ln4KTueHc9oQxZS5+zZA6LfIu0AB0o7RGcvRV2F+434svy9aXceOUQ2n/5qWo6jmqtOsx2IGkiBkCCHBK8G/83mWBWcfbzXdfPDj7a2xeMoiAU5Rr99JIJkiplDMVrR48diGtCxRYjitWUBigbz6Yy0ozowT6TUvK2PaPUgfhxVK2lhi/et37+/w== NVIDIA-SDK-Manager
```
-375
View File
@@ -1,375 +0,0 @@
Kubernetes (K3Sup)
===
### Installation de kubectl ?
```bash
sudo apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
sudo chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.33/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo chmod 644 /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl
```
### Installation de k3sup :
```bash
# Avec curl
curl -sLS https://get.k3sup.dev | sh
sudo install k3sup /usr/local/bin/
# Ou téléchargement direct depuis GitHub
wget https://github.com/alexellis/k3sup/releases/download/0.13.11/k3sup
chmod +x k3sup
sudo mv k3sup /usr/local/bin/
```
#### Premier déploiement :
```bash
# Installation du master node
k3sup install --ip 192.168.1.14 --user pleb --ssh-key $HOME/.ssh/bikiniBottom
# Ajout des worker nodes
k3sup join --ip 192.168.1.13 --server-ip 192.168.1.14 --user pleb --ssh-key $HOME/.ssh/bikiniBottom
k3sup join --ip 192.168.1.16 --server-ip 192.168.1.14 --user pleb --ssh-key $HOME/.ssh/bikiniBottom
k3sup join --ip 192.168.1.19 --server-ip 192.168.1.14 --user pleb --ssh-key $HOME/.ssh/bikiniBottom
```
#### Vérification du cluster :
```bash
# Export du kubeconfig
export KUBECONFIG=~/delmar.bzh/kubernetes/kubeconfig
# Vérification des nœuds
kubectl get nodes -o wide
# Check des pods système
kubectl get pods -n kube-system
```
#### Setting labels:
```bash
# sudo k3s kubectl label nodes xxx kubernetes.io/role=worker
kubectl label nodes gary kubernetes.io/role=worker
kubectl label nodes sheldon kubernetes.io/role=worker
kubectl label nodes pearl kubernetes.io/role=worker
# sudo k3s kubectl label nodes sandy node-type=worker
kubectl label nodes gary node-type=worker
kubectl label nodes sheldon node-type=worker
kubectl label nodes pearl node-type=worker
```
```bash
kubectl get nodes
```
```
NAME STATUS ROLES AGE VERSION
gary Ready worker 82m v1.35.5+k3s1
pearl Ready worker 81m v1.35.5+k3s1
sandy Ready control-plane 84m v1.35.5+k3s1
sheldon Ready worker 82m v1.35.5+k3s1
```
### Helm
> On sandy
```bash
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh
helm version
```
#### [Headlamp](https://headlamp.dev/docs/latest/)
```bash
# first add our custom repo to your local helm repositories
helm repo add headlamp https://kubernetes-sigs.github.io/headlamp/
# now you should be able to install headlamp via helm
helm install bb-headlamp headlamp/headlamp --namespace kube-system
```
```
NAME: bb-headlamp
LAST DEPLOYED: Tue Feb 17 17:17:03 2026
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
```
1. Get the application URL by running these commands:
```bash
export POD_NAME=$(kubectl get pods --namespace kube-system -l "app.kubernetes.io/name=headlamp,app.kubernetes.io/instance=bb-headlamp" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace kube-system $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace kube-system port-forward $POD_NAME 8080:$CONTAINER_PORT
```
2. Get the token using
```bash
kubectl create token bb-headlamp --namespace kube-system
```
#### metallb
```bash
# Add MetalLB repository to Helm
helm repo add metallb https://metallb.github.io/metallb
# Check the added repository
helm search repo metallb
helm upgrade --install metallb metallb/metallb --create-namespace \
--namespace metallb-system --wait
```
```bash
Release "metallb" does not exist. Installing it now.
NAME: metallb
LAST DEPLOYED: Wed Feb 18 11:46:03 2026
NAMESPACE: metallb-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
MetalLB is now running in the cluster.
Now you can configure it via its CRs. Please refer to the metallb official docs
on how to use the CRs.
```
```bash
vim metallb-config.yaml
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default-pool
namespace: metallb-system
spec:
addresses:
- 192.168.1.21-192.168.1.30
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default
namespace: metallb-system
spec:
ipAddressPools:
- default-pool
```
#### traefik
```bash
vim traefik-ingress.yml
```
```
---
apiVersion: v1
kind: Secret
metadata:
name: traefik-basic-auth-secret
namespace: kube-system
data:
users: |2
YWRtaW46JGFwcjEkMmp5TzMwYmskRE5IV0VEQW1VQXFVajVGOHNvdXNVMAoK
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: traefik-basic-auth-middleware
spec:
basicAuth:
secret: traefik-basic-auth-secret
realm: "Traefik Dashboard"
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: traefik-ingress
namespace: kube-system
annotations:
spec.ingressClassName: traefik
traefik.ingress.kubernetes.io/router.middlewares: kube-system-traefik-basic-auth-middleware@kubernetescrd
spec:
serviceAccountName: traefik-ingress
env:
name: OVH_ENDPOINT
value: ovh-eu
name: OVH_APPLICATION_KEY
value: 3f8bdfed17f848d8
name: OVH_APPLICATION_SECRET
value: 6946758d7515ecef108aeb286bf3c7d0
name: OVH_CONSUMER_KEY
value: 94b2ddf482d36421a33aa6b3aa51595
args:
- --configFile=/config/traefik.toml
volumeMounts:
- name: traefik-config
mountPath: /config/
- name: traefik-custom
mountPath: /custom/
- name: traefik-certs
mountPath: /certs/
ports:
name: http
containerPort: 80
name: https
containerPort: 443
name: dashboard
containerPort: 8080
volumes:
- name: traefik-config
configMap:
- name: traefik-config
- name: traefik-custom
- name: traefik-certs
persistentVolumeClaim:
claimName: traefik-certsrules:
- http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: traefik-dashboard
port:
number: 8080
---
apiVersion: v1
kind: Service
metadata:
name: traefik-dashboard
spec:
ports:
- name: http
port: 8080
targetPort: 8080
selector:
app: traefik-dashboard
type: LoadBalancer
```
```bash
sudo k3s kubectl -n kube-system apply -f traefik-ingress.yml
```
#### [longhorn](https://longhorn.io)
```bash
helm repo add longhorn https://charts.longhorn.io
helm repo update
kubectl create namespace longhorn-system
helm install longhorn longhorn/longhorn --namespace longhorn-system
USER=admin; PASSWORD=v5bB4OQRDfY5tFJ1; echo "${USER}:$(openssl passwd -stdin -apr1 <<< ${PASSWORD})" >> auth
kubectl -n longhorn-system create secret generic basic-auth --from-file=auth
vim longhorn/longhorn-middelwares.yaml
````
```
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: longhorn-auth
namespace: longhorn-system
spec:
basicAuth:
secret: basic-auth
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: longhorn-buffering
namespace: longhorn-system
spec:
buffering:
# Allows backing image uploads up to 10,000MB
maxRequestBodyBytes: 10485760000
```
```bash
kubectl apply -f longhorn-middlewares.yml
```
```bash
vim longhorn/longhorn-ingress.yaml
```
```
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: longhorn-ingress
namespace: longhorn-system
annotations:
# Connect the middlewares defined in step 2
traefik.ingress.kubernetes.io/router.middlewares:
longhorn-system-longhorn-auth@kubernetescrd,
longhorn-system-longhorn-buffering@kubernetescrd
spec:
ingressClassName: traefik
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: longhorn-frontend
port:
number: 80
```
```bash
kubectl -n longhorn-system get pod
kubectl get storageclass
```
```
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false 93m
longhorn (default) driver.longhorn.io Delete Immediate true 59m
longhorn-static driver.longhorn.io Delete Immediate true 59m
```
##### Unset default for "local-path" (emmc)
```bash
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
```
##### Mark longhorn as "default"
```bash
kubectl patch storageclass longhorn -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
```
##### Apply ingress
```bash
kubectl -n longhorn-system apply -f longhorn/longhorn-ingress.yaml
kubectl -n longhorn-system get ingress
```
+75 -38
View File
@@ -92,7 +92,7 @@ borg.delmar.bzh {
gzip
minimum_length 1024
}
reverse_proxy patrick:8080
reverse_proxy carlo:8081
}
cap.delmar.bzh {
@@ -187,7 +187,7 @@ dev.delmar.bzh {
gzip
minimum_length 1024
}
reverse_proxy patrick:19080
reverse_proxy patrick:5678
}
dia.delmar.bzh {
@@ -235,6 +235,15 @@ gotify.delmar.bzh {
reverse_proxy carlo:41901
}
hmr.delmar.bzh {
encode {
zstd
gzip
minimum_length 1024
}
reverse_proxy bob:39084
}
homepage.delmar.bzh {
encode {
zstd
@@ -290,7 +299,7 @@ jellyfin.delmar.bzh {
gzip
minimum_length 1024
}
reverse_proxy patrick:8096
reverse_proxy carlo:8096
}
jellyseerr.delmar.bzh {
@@ -299,7 +308,7 @@ jellyseerr.delmar.bzh {
gzip
minimum_length 1024
}
reverse_proxy patrick:5055
reverse_proxy carlo:5055
}
kontadenn.delmar.bzh {
@@ -320,6 +329,15 @@ kontadenn.delmar.bzh {
}
}
lghn.delmar.bzh {
encode {
zstd
gzip
minimum_length 1024
}
reverse_proxy bikinibottom:80
}
lnk.delmar.bzh {
encode {
zstd
@@ -335,7 +353,11 @@ lud.delmar.bzh {
gzip
minimum_length 1024
}
reverse_proxy carlo:3002
reverse_proxy carlo:3002 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
mmgr.delmar.bzh {
@@ -344,7 +366,16 @@ mmgr.delmar.bzh {
gzip
minimum_length 1024
}
reverse_proxy patrick:38274
reverse_proxy carlo:38274
}
mmm.delmar.bzh {
encode {
zstd
gzip
minimum_length 1024
}
reverse_proxy bikinibottom:8001
}
nds.delmar.bzh {
@@ -383,6 +414,15 @@ nsns.delmar.bzh {
}
}
oec.delmar.bzh {
encode {
zstd
gzip
minimum_length 1024
}
reverse_proxy carlo:32768
}
octoprint.delmar.bzh {
encode {
zstd
@@ -524,6 +564,24 @@ stream.delmar.bzh {
}
}
swpt.delmar.bzh {
encode {
zstd
gzip
minimum_length 1024
}
reverse_proxy bikinibottom:888
}
tournoi.delmar.bzh {
encode {
zstd
gzip
minimum_length 1024
}
reverse_proxy carlo:2020
}
tpml.delmar.bzh {
encode {
zstd
@@ -551,6 +609,15 @@ trek.delmar.bzh {
reverse_proxy carlo:32776
}
trfk.delmar.bzh {
encode {
zstd
gzip
minimum_length 1024
}
reverse_proxy bikinibottom:9000
}
trmx.delmar.bzh {
encode {
zstd
@@ -601,7 +668,7 @@ wizarr.delmar.bzh {
gzip
minimum_length 1024
}
reverse_proxy patrick:5690
reverse_proxy carlo:5690
}
www.delmar.bzh {
@@ -643,35 +710,5 @@ zik.delmar.bzh {
gzip
minimum_length 1024
}
reverse_proxy patrick:4533
}
trfk.delmar.bzh {
encode {
zstd
gzip
minimum_length 1024
}
reverse_proxy 192.168.1.21
}
hdlp.delmar.bzh {
encode {
zstd
gzip
minimum_length 1024
}
reverse_proxy 192.168.1.22
}
lghn.delmar.bzh {
encode {
zstd
gzip
minimum_length 1024
}
reverse_proxy 192.168.1.21
basic_auth / {
admin $2a$14$RsUJ13kC9DcRMoQa8hlmNOljcXGMSem.1XwYFeV1El8Drw7DIJbVu
}
reverse_proxy carlo:4533
}