That’s what I thought.
Anyway, after few days I figured it out and - spoiler - it works!
…I mean I must test it with some more read/write intensive containers (like home assistant, jellyfin, nextcloud, etc) but 'till now with portainer, traefik, mosquitto and few others works without issues.
Everytime that a container is scheduled, the volume is dynamically attached.
Ok, with that swarm is the limited (I think) of just 1 replica (data instead is replicated on all nodes - my choice), but I am no longer dependant by the NAS (NFS), it has some sort of auto healing (I am still reading the docs) and the resources it uses are very low compared to ceph, longhorn etc.
I have created a sort of guide (I am lazy so the description are in italian but the commands are there)
If you are interested, enjoy
all suggestions and improvement are welcome
I tried to attach my .md files but I am not allowed, so this is the extended version of the post 
password, names, etc are just place holders 
I used debian 13, docker is already installed as well as ufw
due to the kernel 6.12 used by debian I prefer to use the previous version of the module (the latest is the 9.4.x)
I used lvm for the storage so it easy to increase if needed
STEP 0 . module compile
wget -O /tmp/linbit-keyring.deb \
https://packages.linbit.com/public/linbit-keyring.deb
sudo dpkg -i /tmp/linbit-keyring.deb
echo 'deb [signed-by=/etc/apt/trusted.gpg.d/linbit-keyring.gpg] https://packages.linbit.com/public/ trixie misc' \
| sudo tee /etc/apt/sources.list.d/linbit.list
sudo apt update
sudo apt install -y \
build-essential \
linux-headers-$(uname -r) \
drbd-utils
ls -ld /lib/modules/$(uname -r)/build
mkdir -p ~/linstor
cd ~/linstor
wget https://pkg.linbit.com/downloads/drbd/9/drbd-9.2.19.tar.gz
tar xzf drbd-9.2.19.tar.gz
cd drbd-9.2.19
make
# deve tornare
# Module build was successful.
sudo apt install -y dkms linux-headers-amd64
sudo mkdir -p /usr/src/drbd-9.2.19-1/src
sudo cp -a ~/linstor/drbd-9.2.19/drbd \
/usr/src/drbd-9.2.19-1/src/
sudo cp ~/linstor/drbd-9.2.19/misc/dkms.conf \
/usr/src/drbd-9.2.19-1/dkms.conf
sudo sed -i \
's/#MODULE_VERSION#/9.2.19-1/' \
/usr/src/drbd-9.2.19-1/dkms.conf
# verifica
head -5 /usr/src/drbd-9.2
sudo dkms add -m drbd -v 9.2.19-1
sudo dkms status
sudo dkms build -m drbd -v 9.2.19-1
sudo dkms install -m drbd -v 9.2.19-1
sudo dkms status
sudo modinfo drbd | grep -E '^(filename|version):'
sudo modprobe -r drbd 2>/dev/null || true
sudo modprobe drbd
cat /sys/module/drbd/version
STEP 1 . swarm nodes preparation
installazione del software e preparazione del cluster
Base
da fare su tutti e 3 i nodi
entrare nel bios e disattivare secure boot nella vm!
il modulo kernel che verrà compilato non è firmato e se c’è il secure boot attivo non lo carica.
su tutti e tre i nodi
configurazione hosts e verifica comunicazione tra i nodi
# preparazione nodi swam
cat /etc/hosts
aggiungere
10.8.80.32 swarm-01.zerocinque.cloud swarm-01
10.8.80.33 swarm-02.zerocinque.cloud swarm-02
10.8.80.34 swarm-03.zerocinque.cloud swarm-03
verifica
getent hosts swarm-01
getent hosts swarm-02
getent hosts swarm-03
ping -c 3 swarm-01
ping -c 3 swarm-02
ping -c 3 swarm-03
configurazione firewall UFW
swarm-01
permette traffico da e per 2 e 3
sudo ufw allow from 10.8.80.33 to any port 3366 proto tcp
sudo ufw allow from 10.8.80.34 to any port 3366 proto tcp
sudo ufw allow from 10.8.80.33 to any port 3370 proto tcp
sudo ufw allow from 10.8.80.34 to any port 3370 proto tcp
sudo ufw allow from 10.8.80.33 to any port 7000:7999 proto tcp
sudo ufw allow from 10.8.80.34 to any port 7000:7999 proto tcp
swarm-02
permette traffico da e per 1 e 3
sudo ufw allow from 10.8.80.32 to any port 3366 proto tcp
sudo ufw allow from 10.8.80.34 to any port 3366 proto tcp
sudo ufw allow from 10.8.80.32 to any port 3370 proto tcp
sudo ufw allow from 10.8.80.34 to any port 3370 proto tcp
sudo ufw allow from 10.8.80.32 to any port 7000:7999 proto tcp
sudo ufw allow from 10.8.80.34 to any port 7000:7999 proto tcp
swarm-03
permette traffico da e per 1 e 2
sudo ufw allow from 10.8.80.32 to any port 3366 proto tcp
sudo ufw allow from 10.8.80.33 to any port 3366 proto tcp
sudo ufw allow from 10.8.80.32 to any port 3370 proto tcp
sudo ufw allow from 10.8.80.33 to any port 3370 proto tcp
sudo ufw allow from 10.8.80.32 to any port 7000:7999 proto tcp
sudo ufw allow from 10.8.80.33 to any port 7000:7999 proto tcp
# firewall
# vedi codice per nodo (sotto), dopo l'aggiunta delle regole
sudo ufw status numbered
Installazione dei pacchetti
da fare su tutti e 3 i nodi
aggiunta del repository e installazione pacchetti
# Repository pubblico LINBIT per drbd-utils / LINSTOR
wget -O /tmp/linbit-keyring.deb \
https://packages.linbit.com/public/linbit-keyring.deb
sudo dpkg -i /tmp/linbit-keyring.deb
echo 'deb [signed-by=/etc/apt/trusted.gpg.d/linbit-keyring.gpg] https://packages.linbit.com/public/ trixie misc' \
| sudo tee /etc/apt/sources.list.d/linbit.list
sudo apt update
# Dipendenze necessarie
sudo apt install -y \
build-essential \
curl \
gnupg \
dkms \
linux-headers-amd64 \
linux-headers-$(uname -r) \
drbd-utils \
resource-agents \
lvm2 \
thin-provisioning-tools \
linstor-controller \
linstor-satellite \
linstor-client \
drbd-reactor
Compilazione e installazione del modulo kernel
da fare su tutti e 3 i nodi
il modulo non viene fornito da Linstor (viene dato il pacchetto solo ai clienti) ma bisogna compilarlo da sorgenti
# Verifica headers kernel
ls -ld /lib/modules/$(uname -r)/build
# Scarica sorgente ufficiale DRBD
mkdir -p ~/linstor
cd ~/linstor
wget https://pkg.linbit.com/downloads/drbd/9/drbd-9.3.3.tar.gz
tar xzf drbd-9.3.3.tar.gz
# Prepara sorgente per DKMS
sudo mkdir -p /usr/src/drbd-9.3.3-1/src
sudo cp -a ~/linstor/drbd-9.3.3/drbd /usr/src/drbd-9.3.3-1/src/
sudo cp ~/linstor/drbd-9.3.3/misc/dkms.conf \
/usr/src/drbd-9.3.3-1/dkms.conf
# Inject della versione nel file di configurazione
sudo sed -i 's/#MODULE_VERSION#/9.3.3-1/' \
/usr/src/drbd-9.3.3-1/dkms.conf
# Verifica configurazione
head -10 /usr/src/drbd-9.3.3-1/dkms.conf
# Registra, compila e installa
sudo dkms add -m drbd -v 9.3.3-1
sudo dkms build -m drbd -v 9.3.3-1
sudo dkms install -m drbd -v 9.3.3-1
# Verifica
sudo dkms status
sudo modinfo drbd | grep -E '^(filename|version):'
abilitazione servizi
sudo systemctl enable --now linstor-controller
sudo systemctl enable --now linstor-satellite
riavviare il nodo e quindi verificare che al reboot il sistema carichi nel kernel carichi il modulo corretto e non quello di base.
sudo reboot now
verifica 2
# Verifica
sudo dkms status
sudo modinfo drbd | grep -E '^(filename|version):'
sudo modprobe -r drbd 2>/dev/null || true
sudo modprobe drbd
cat /sys/module/drbd/version
Creazione dello storage
da fare su tutti e 3 i nodi
preparazione del disco per lo storage Linstor
il nome del disco passato alla vm è vdb
verifica del disco
lsblk
creazione partizione e file system
# disco di storage vdb
sudo wipefs /dev/vdb
sudo pvcreate /dev/vdb
sudo vgcreate linstor_vg /dev/vdb
# non lo sfrutto al 100%
sudo lvcreate -l 90%FREE -T linstor_vg/thinpool
sudo pvs
sudo vgs
sudo lvs -a
# verificare che sia stato creato thinpool in vdb con
lsblk
Creazione nodi e pool
SOLO swarm-01!
creazione dei nodi da swarm-01
sudo linstor node create swarm-01 10.8.80.32
sudo linstor node create swarm-02 10.8.80.33
sudo linstor node create swarm-03 10.8.80.34
creazione dello storage pool
sudo linstor storage-pool create lvmthin swarm-dmz-01 linstor-pool linstor_vg/thinpool
sudo linstor storage-pool create lvmthin swarm-dmz-02 linstor-pool linstor_vg/thinpool
sudo linstor storage-pool create lvmthin swarm-dmz-03 linstor-pool linstor_vg/thinpool
# verificare con
sudo linstor storage-pool list
# deve tornare una tabella con i 3 nodi e "OK"
creazione del resource group per il database
# bootstrap
sudo linstor resource-group create \
--storage-pool linstor-pool \
--place-count 3 \
--diskless-on-remaining true \
linstor-db-grp
sudo linstor resource-group drbd-options \
--auto-promote=no \
--quorum=majority \
--on-suspended-primary-outdated=force-secondary \
--on-no-quorum=io-error \
--on-no-data-accessible=io-error \
linstor-db-grp
sudo linstor resource-group modify \
--storage-pool linstor-pool \
--place-count 3 \
DfltRscGrp
sudo linstor resource-group drbd-options \
--quorum=majority \
--on-no-quorum=suspend-io \
--on-no-data-accessible=suspend-io \
--on-suspended-primary-outdated=force-secondary \
--rr-conflict=retry-connect \
DfltRscGrp
creazione volume per i db interno
sudo linstor volume-group create linstor-db-grp
sudo linstor resource-group spawn-resources \
linstor-db-grp \
linstor_db \
200M
# verifica dei volumi
sudo linstor resource list
sudo linstor volume list
attivazione dello storage Linstor, quindi move del database nel volume in modo da avere HA
sudo systemctl disable linstor-controller
sudo systemctl stop linstor-controller
verifica che sia veramente “inactive”
sudo systemctl is-active linstor-controller
creazione del mount
sudo tee /etc/systemd/system/var-lib-linstor.mount >/dev/null <<'EOF'
[Unit]
Description=Filesystem for the LINSTOR controller
[Mount]
What=/dev/drbd/by-res/linstor_db/0
Where=/var/lib/linstor
EOF
sposto il db in una posizione temporanea
sudo mv /var/lib/linstor /var/lib/linstor.orig
sudo mkdir /var/lib/linstor
sudo chattr +i /var/lib/linstor
promozione volume a primario
sudo drbdadm primary linstor_db
verifica, deve tornare “Primary”, gli altri due saranno “Secondary”
sudo drbdadm status linstor_db
format del volume con ext4
sudo mkfs.ext4 -b 4096 /dev/drbd/by-res/linstor_db/0
avvio dello storage db
sudo systemctl daemon-reload
sudo systemctl start var-lib-linstor.mount
copia dei dati del database da locale a HA
sudo cp -a /var/lib/linstor.orig/. /var/lib/linstor/
avvio del controller
sudo systemctl start linstor-controller
verifica, nelle tabelle deve tornare “OK” e swarm-01 come “InUse” mentre gli altri 2 come “Unused”
sudo linstor node list
sudo linstor storage-pool list
sudo linstor resource list
deve tornare qualcosa simile a questo
╭────────────────────────────────────────────────────────────────────────╮
│ Node │ Platform │ NodeType │ Addresses │ State │
╞════════════════════════════════════════════════════════════════════════╡
│ swarm-01 │ LINUX │ SATELLITE │ 10.8.80.32:3366 (PLAIN) │ Online │
│ swarm-02 │ LINUX │ SATELLITE │ 10.8.80.33:3366 (PLAIN) │ Online │
│ swarm-03 │ LINUX │ SATELLITE │ 10.8.80.34:3366 (PLAIN) │ Online │
╰────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ StoragePool │ Node │ Driver │ PoolName │ FreeCapacity │ TotalCapacity │ CanSnapshots │ State │ SharedName │
╞════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╡
│ DfltDisklessStorPool │ swarm-01 │ DISKLESS │ │ │ │ False │ Ok │ swarm-01;DfltDisklessStorPool │
│ DfltDisklessStorPool │ swarm-02 │ DISKLESS │ │ │ │ False │ Ok │ swarm-02;DfltDisklessStorPool │
│ DfltDisklessStorPool │ swarm-03 │ DISKLESS │ │ │ │ False │ Ok │ swarm-03;DfltDisklessStorPool │
│ linstor-pool │ swarm-01 │ LVM_THIN │ linstor_vg/thinpool │ 28.73 GiB │ 28.70 GiB │ True │ Ok │ swarm-01;linstor-pool │
│ linstor-pool │ swarm-02 │ LVM_THIN │ linstor_vg/thinpool │ 28.73 GiB │ 28.70 GiB │ True │ Ok │ swarm-02;linstor-pool │
╭───────────────────────────────────────────────────────────────────────────────╮
│ ResourceName │ Node │ Layers │ Usage │ Conns │ State │ Vote │
╞═══════════════════════════════════════════════════════════════════════════════╡
│ linstor_db │ swarm-01 │ DRBD,STORAGE │ InUse │ Ok │ UpToDate │ Yes │
│ linstor_db │ swarm-02 │ DRBD,STORAGE │ Unused │ Ok │ UpToDate │ Yes │
│ linstor_db │ swarm-03 │ DRBD,STORAGE │ Unused │ Ok │ UpToDate │ Yes │
╰───────────────────────────────────────────────────────────────────────────────╯
Installazione del reactor
SOLO su swarm-02 e swarm-03!
sudo tee /etc/systemd/system/var-lib-linstor.mount >/dev/null <<'EOF'
[Unit]
Description=Filesystem for the LINSTOR controller
[Mount]
What=/dev/drbd/by-res/linstor_db/0
Where=/var/lib/linstor
EOF
creazione cartella, set permessi e attivazione
sudo mkdir -p /var/lib/linstor
sudo chattr +i /var/lib/linstor
sudo systemctl daemon-reload
da fare su tutti e 3 i nodi
disattivazione e stop del controller
Il servizio linstor-controller non viene gestito direttamente da systemd in avvio automatico. Sarà DRBD Reactor a promuovere la risorsa linstor_db, montare /var/lib/linstor e avviare linstor-controller sul nodo attivo
sudo systemctl disable linstor-controller
sudo systemctl stop linstor-controller
verifica
in pratica solo il controller deve risultare “disabled”, gli altri 2 devono essere “enabled”
sudo systemctl is-enabled linstor-controller
sudo systemctl is-enabled linstor-satellite
sudo systemctl is-enabled drbd-reactor
creazione del controller
sudo mkdir -p /etc/drbd-reactor.d
configurazione
sudo tee /etc/drbd-reactor.d/linstor_db.toml >/dev/null <<'EOF'
[[promoter]]
[promoter.resources.linstor_db]
start = ["var-lib-linstor.mount", "linstor-controller.service"]
EOF
attivazione e verifica
sudo systemctl enable drbd-reactor
sudo systemctl restart drbd-reactor
# verifica, deve dare "active (running)"
sudo systemctl status drbd-reactor --no-pager
# verifica dello stato, i pallini verdi indicano il nodo dove è in carico
sudo drbd-reactorctl status linstor_db
test della HA
SOLO su swarm-01!
stop del servizio
sudo systemctl start drbd-reactor
sudo drbd-reactorctl status linstor_db
sudo drbd-reactorctl evict linstor_db
quindi aspettare qualche secondo e controllare swarm-02 e swarm-03. Uno dei 2 nodi dovrebbe esser promosso primary e “InUse” (pallini verdi)
sudo drbd-reactorctl status linstor_db
quindi su quel nodo verifica
sudo drbdadm status linstor_db
findmnt /var/lib/linstor
systemctl is-active linstor-controller
sudo linstor node list
sudo linstor storage-pool list
se tutto funziona riattivare swarm-01 e verificare che il primary (pallini verdi) non si muova
sudo systemctl start drbd-reactor
sudo drbd-reactorctl status linstor_db
Attivazione del VIP per il servizio Linstor
da fare su tutti e 3 i nodi
creazione del file di configurazione
sudo nano /etc/drbd-reactor.d/linstor_db.toml
sostituire TUTTO il contenuto con questo
il 10.8.80.220 sarà il VIP
[[promoter]]
[promoter.resources.linstor_db]
start = [
"ocf:heartbeat:IPaddr2 linstor_vip cidr_netmask=24 ip=10.8.80.220",
"var-lib-linstor.mount",
"linstor-controller.service",
]
quindi riavvio del reactor e verifica se il nodo primary è anche il VIP
sudo systemctl restart drbd-reactor
controllo del nodo primary (pallini verdi) che dovrà restare sull’ultimo nodo.
sudo drbd-reactorctl status linstor_db
il nodo primary sarà l’unico che avrà un output di return da questo comando
ip addr | grep 10.8.80.220
quindi configurazione dei client in modo che seguano il vip
sudo mkdir -p /etc/linstor
sudo tee /etc/linstor/linstor-client.conf >/dev/null <<'EOF'
[global]
controllers = 10.8.80.220
EOF
# e infine verifica che tutti i client vedano il cluster
sudo linstor node list
su tutti e tre deve tornare una tabella simile a questa
╭────────────────────────────────────────────────────────────────────╮
│ Node │ Platform │ NodeType │ Addresses │ State │
╞════════════════════════════════════════════════════════════════════╡
│ swarm-01 │ LINUX │ SATELLITE │ 10.8.80.32:3366 (PLAIN) │ Online │
│ swarm-02 │ LINUX │ SATELLITE │ 10.8.80.33:3366 (PLAIN) │ Online │
│ swarm-03 │ LINUX │ SATELLITE │ 10.8.80.34:3366 (PLAIN) │ Online │
╰────────────────────────────────────────────────────────────────────╯
Creazione resource group per TEST Docker
SOLO su un nodo!
creazione del resource group per docker
# da un nodo qualsiasi
sudo linstor resource-group create \
--storage-pool linstor-pool \
--place-count 3 \
docker-rg
sudo linstor resource-group drbd-options \
--quorum=majority \
--on-no-quorum=suspend-io \
--on-no-data-accessible=suspend-io \
docker-rg
sudo linstor volume-group create docker-rg
Creazione volume test
SOLO su un nodo!
creazione volume di test
# su un nodo qualsiasi
sudo linstor resource-group spawn-resources \
docker-rg \
test-volume \
256M
# da un altro nodo verifica che sia stato creato
sudo linstor resource list
sudo linstor volume list
# dall'ultimo nodo formattazione volume e test scrittura
sudo drbdadm primary test-volume
# format in ext4
sudo mkfs.ext4 /dev/drbd/by-res/test-volume/0
sudo mkdir -p /mnt/test-volume
sudo mount /dev/drbd/by-res/test-volume/0 /mnt/test-volume
echo "ciao da swarm-XX $(date)" | sudo tee /mnt/test-volume/prova.txt
sync
cat /mnt/test-volume/prova.txt
sudo umount /mnt/test-volume
sudo drbdadm secondary test-volume
# test replica da un altro nodo
sudo drbdadm primary test-volume
sudo mkdir -p /mnt/test-volume
sudo mount /dev/drbd/by-res/test-volume/0 /mnt/test-volume
# deve stampare "ciao da swarm-xx e la data di creazione"
cat /mnt/test-volume/prova.txt
sudo umount /mnt/test-volume
sudo drbdadm secondary test-volume
# se tutto funziona il volume non serve più e può esser cancellato
sudo linstor resource delete test-volume
sudo linstor resource-definition delete test-volume
sudo linstor resource-group delete docker-rg
STEP 2 . docker plugin
istruzioni per l’installazione del plugin per far usare a Docker i volumi Linstor
da fare su tutti e 3 i nodi
installazione e disattivazione temporanea
docker plugin install linbit/linstor-docker-volume --grant-all-permissions
docker plugin disable linbit/linstor-docker-volume:latest
configurazione del plugin in modo che legga il VIP del cluster Linstor
docker plugin set linbit/linstor-docker-volume:latest \
LS_CONTROLLERS=linstor://10.8.82.224:3370
docker plugin enable linbit/linstor-docker-volume:latest
quindi verifica
docker plugin inspect linbit/linstor-docker-volume:latest \
--format '{{json .Settings.Env}}'
deve tornare
["LS_CONTROLLERS=linstor://10.8.82.224:3370","LS_USERNAME=","LS_PASSWORD=","LS_CERT_FILE=","LS_KEY_FILE=","LS_CA_FILE="]
creazione di un volume test
docker volume create \
--driver linbit/linstor-docker-volume \
--opt size=256M \
--opt replicas=3 \
--opt storagepool=linstor-pool \
--opt fs=ext4 \
test-linstor
verifica se il volume è stato creato anche in linstor
sudo linstor resource list
sudo linstor volume list
creazione di un container test per verifica del volume
docker service create \
--name volume-test \
--constraint 'node.hostname==swarm-dmz-01' \
--mount type=volume,source=test-linstor,target=/data,volume-driver=linbit/linstor-docker-volume \
alpine sh -c 'if [ ! -f /data/prova.txt ]; then echo "creato da $(hostname) $(date)" > /data/prova.txt; fi; sleep 3600'
spostare il container da 1 a 3
docker service update \
--constraint-rm 'node.hostname==swarm-dmz-01' \
--constraint-add 'node.hostname==swarm-dmz-03' \
volume-test
verificare che il contenuto del file prova.txt sia lo stesso (stesso timestamp)
docker exec $(docker ps -q --filter name=volume-test) cat /data/prova.txt
esempio
olimpo@swarm-dmz-01:docker exec $(docker ps -q --filter name=volume-test) cat /data/prova.txt
scritto da 4530e9a2a7db Sat Aug 22 16:20:59 UTC 2026
e su swarm-03
olimpo@swarm-dmz-03:~$ docker exec $(docker ps -q --filter name=volume-test) cat /data/prova.txt
scritto da 4530e9a2a7db Sat Aug 22 16:20:59 UTC 2026
Creazione di un volume Docker
esempio creazione di un volume da 256 mega con 3 repliche
docker volume create \
--driver linbit/linstor-docker-volume \
--opt size=256M \
--opt replicas=3 \
--opt storagepool=linstor-pool \
--opt fs=ext4 \
traefik-data
verifica
sudo linstor resource list
sudo linstor volume list
small cheat sheet
lista comandi utili per verificare lo stato del cluster
stato risorse
sudo linstor resource list
storage pool disponibili con stato e altre informazioni (spazio, nodi se attivi o diskless ecc)
sudo linstor storage-pool list
stato sincronizzazione del nodo
sudo drbdadm status
lista nodi
sudo linstor node list
disconnessione volume ↔ nodo
da usare in caso di problemi di sincronizzazione
sudo drbdadm disconnect linstor_db:swarm-dmz-03
connessione nodo
sudo drbdadm connect linstor_db:swarm-dmz-03