- Contenu
- Présentation personnelle et projet de stage
- Projet Refonte de l'infrastructure
- Jour 1 (30 mars 2026) :
- Jour 2 (31 mars 2026) :
- Jour 3 (1 avril 2026) :
- Jour 4 (2 avril 2026) :
- Jour 5 (3 avril 2026) :
- Jour 6 (7 avril 2026) :
- Jour 7 (8 avril 2026) :
- Jour 8 (9 avril 2026) :
- Jour 9 (10 avril 2026) :
- Jour 10 (20 avril 2026) :
- Jour 11 (21 avril 2026) :
- Jour 12 (22 avril 2026) :
- Jour 13 (23 avril 2026) :
- Jour 14 (24 avril 2026) :
- Jour 15 (27 avril 2026) :
- Jour 16 (28 avril 2026) :
- Jour 17 (29 avril 2026) :
- Jour 18 (30 avril 2026) :
- Jour 19 (4 mai 2026) :
- Jour 20 (5 mai 2026) :
- Jour 21 (6 mai 2026) :
- Jour 22 (7 mai 2026) :
- Jour 23 (11 mai 2026) :
- Jour 24 (12 mai 2026) :
- Jour 25 (13 mai 2026) :
- Jour 27 (18 mai 2026) :
- Jour 28 (19 mai 2026) :
- Jour 29 (20 mai 2026) :
- Jour 30 (21 mai 2026) :
- Jour 31 (22 mai 2026) :
- Jour 32 (26 mai 2026) :
- Jour 33 (27 mai 2026) :
- Jour 34 (28 mai 2026) :
- Jour 35 (29 mai 2026) :
- Jour 36 (1 juin 2026) :
- Jour 37 (2 juin 2026) :
- Jour 38 (3 juin 2026) :
- Jour 39 (4 juin 2026) :
- Jour 40 (5 juin 2026) :
Présentation personnelle et projet de stage¶
Je suis Yoan Gabriel
Je suis un étudiant en Licence Pro ADSILLH (Administration et Développement de Systèmes Informatiques à base de Logiciels Libres et Hybrides).
Je suis en stage du 30 Mars 2026 au 31 Juillet 2026 avec comme coéquipier Nicolas Schmauch.
Projet Refonte de l'infrastructure¶
Jour 1 (30 mars 2026) :¶
Nous avons un projet de refonte d’architecture qui va se baser sur un hyperviseur qui va stocker les différents services dans différentes VM.
| SCHEMA |
|---|
|
Nous avons tout d’abord dû choisir un hyperviseur parmi les différentes solutions open source :
| Caractéristique | Incus (Mode VM) | KVM/QEMU + libvirt | Canonical LXD (Mode VM) | Xen Project |
|---|---|---|---|---|
| Moteur | QEMU / KVM | QEMU / KVM | QEMU / KVM | Xen Hypervisor |
| Installation Debian 13 | Native apt install incus |
Native apt install libvirt-daemon |
Via Snap snap install lxd |
Native apt install xen-hypervisor |
| RAM | Très faible (~50 Mo) | Faible (~100 Mo) | Moyen (~200 Mo) | Élevé (minimum 1024 Mo) |
| Déploiement OS | Images Cloud et Manuel | Manuel (Fichiers ISO) | Images Cloud et Manuel | Manuel (ISO ou debootstrap) |
| Sauvegardes / Snapshots | Intégrés (incus snapshot / export) | Manuels (virsh snapshot-create) | Intégrés (lxc snapshot) | Complexes (via LVM) |
| Score global / 5 | 5 / 5 | 4 / 5 | 3 / 5 | 1 / 5 |
sources :
https://linuxcontainers.org
https://libvirt.org
https://canonical.com/lxd
https://xenproject.org/resources
https://wiki.debian.org/Incus
https://wiki.debian.org/KVM
https://wiki.debian.org/QEMU
https://wiki.debian.org/libvirt
https://wiki.debian.org/Xen/InstallBootConfig
https://wiki.xenproject.org/wiki/Tuning_Xen_for_Performance
https://dev.to/rosgluk/linux-virtualization-solutions-a-complete-comparison-guide-196g
https://www.siberoloji.com/how-to-install-and-configure-lxd-on-debian-12
Nous avons ensuite calculé les ressources nécessaires pour savoir si le serveur a besoin d’une amélioration de ses ressources ou non :
| Service | RAM minimum | RAM recommandée | CPU minimum | CPU recommandé |
| Apache | 512 MB | 1–4 GB | 1 core | 2+ cores |
| Nginx | 512 MB | 1 GB | 1 core | 2+ cores |
| Laravel | 1 GB | 2+ GB | 1 core | 2+ cores |
| Redmine | 512 MB | 1–2 GB | 1 core | 2 cores |
| Forgejo | 512 MB | 2 GB | 1 core | 2+ cores |
| Nextcloud | 512 MB | 2 GB | 1 core | 2 cores |
| total | 3,5 GB | 9-13+ GB | 6 cores | 12+ cores |
sources :
https://httpd.apache.org/docs/2.4/misc/perf-tuning.html
https://docs.nginx.com/nginx-agent/technical-specifications
https://kinsta.com/fr/blog/installer-nginx
https://www.hostragons.com/fr/blog/exigences-dhebergement-pour-les-applications-laravel
https://redmine1click.com/requirements
https://blog.stephane-robert.info/docs/services/devops/forgejo/installation
https://docs.nextcloud.com/server/stable/admin_manual/installation/system_requirements.html
https://dolys.fr/forums/topic/tuto-dinstallation-et-doptimisation-de-nextcloud-avec-nextcloud-office-collabora-online-sur-yunohost
Nous avons comparé les différents proxys pour savoir lequel répond au mieux à notre besoin :
| Critère clé | Caddy | Nginx Proxy Manager (NPM) | Traefik |
|---|---|---|---|
| Consommation Mémoire (RAM) | Très léger | Lourd | Léger |
| Automatisation SSL | Natif, Zéro config | Bouton dans l'interface | Demande une config YAML |
| Adaptation à un réseau de VM | Fichier texte très lisible | Routage IP via interface | Usine à gaz sans Docker |
| Facilité de prise en main | Syntaxe minimaliste | Interface web complète | Courbe d'apprentissage rude |
| Score global / 5 | 4 / 5 | 3 / 5 | 1 / 5 |
sources :
https://kx.cloudingenium.com/en/caddy-reverse-proxy-automatic-https-zero-config-ssl-guide/
https://caddyserver.com/docs/quick-starts/reverse-proxy
https://caddyserver.com/docs/
https://stackoverflow.com/questions/63077100/how-much-memory-and-cpu-nginx-and-nodejs-in-each-container-needs
https://nginxproxymanager.com/
https://community.traefik.io/t/traefik-performance-lags-behind-nginx-and-caddy/28919
https://doc.traefik.io/traefik/routing/overview/
https://www.reddit.com/r/selfhosted/comments/1odh46j/nginx_vs_caddy_vs_traefik_benchmark_results/
Jour 2 (31 mars 2026) :¶
J'ai rédigé un mail avec Nicolas pour Alexander concernant les choix de solution :
Bonjour Alexander, Nous sommes les nouveaux stagiaires ADSILLH. Pour le projet du nouveau serveur, on a fait des comparatifs (Yoan et moi), pour la nouvelle architecture et on propose : Hyperviseur : Incus (Mode VM), car il offre la consommation RAM la plus faible, s'installe nativement sur Debian et la gestion des snapshots et plus simple qu'avec KVM. Reverse Proxy : Caddy, car très léger et automatise à 100% la gestion des certificats SSL avec Let's Encrypt. Il est plus adapté que Traefik ou Nginx Proxy Manager pour des VM fixes. Voici nos comparatifs : https://projets.cohabit.fr/redmine/projects/accueil/wiki/Nicolas_Schmauch_2 Schmauch Nicolas
Jour 3 (1 avril 2026) :¶
Alexander nous a posé des questions en retour :
Du coup, les questions qui restent en suspens sont : Il faut que cela gère le raid logiciel (1 ou 5). Quelle technologie sont supportées pour les containers ? Et les Snapshots ou image des VM sont-elles exportables dans un format qui est géré par la plupart des hyperviseurs en cas de soucis ?
Nous avons répondu à ces questions :
Incus est compatible avec le raid logiciel (1 ou 5), il ne gère pas le matériel directement, il faut juste lui indiquer d'utiliser le volume créé. Incus utilise des conteneurs système (LXC) et il gère Docker soit en l'installant à l'intérieur d'une VM/conteneur (mode nesting), soit en lançant des images Docker (images OCI). Les VM Incus sont basées sur QEMU et sont exportées au format QCOW2 ou Raw. Donc c'est compatible avec la plupart des hyperviseurs comme Proxmox, KVM, VirtualBox, VMware, etc. Conteneur + dir = Dossier VM QEMU + dir = raw VM QEMU + lvm = qcow2
J'ai regardé comment fonctionne Incus et testé les différentes commandes. Je me suis fait une liste de commandes de base pour Incus :
| Commande | Description |
| incus admin init | Initialisation interactive |
| incus admin init --minimal | Initialisation rapide minimale |
| incus image list images: | Liste les images disponibles |
| incus launch images:debian/12 <nom> | Crée et démarre un conteneur |
| incus launch images:debian/12 <nom> --vm | Crée et démarre une VM |
| incus copy <source> <destination> | Copie un conteneur |
| incus list | Liste toutes les instances |
| incus info <nom> | Détails d'une instance |
| incus start/stop/restart <nom> | Démarre / Arrête / Redémarre |
| incus delete <nom> | Supprime une instance |
| incus delete <nom> --force | Supprime même si active |
| incus launch ... --config limits.cpu=1 --config limits.memory=192MiB | Lance avec limites |
| incus config show <nom> | Affiche la config |
| incus config set <nom> limits.memory=128MiB | Modifie une config |
| incus config device override <nom> root size=30GiB | Redimensionne le disque VM |
| incus exec <nom> -- bash | Shell interactif |
| incus exec <nom> -- <commande> | Exécuter une commande |
| incus file pull <nom>/chemin/fichier . | Récupère un fichier |
| incus file push <fichier> <nom>/chemin/ | Envoie un fichier |
| incus snapshot create <nom> <snap-name> | Crée un snapshot |
| incus snapshot list <nom> | Liste les snapshots |
| incus snapshot restore <nom> <snap-name> | Restaure un snapshot |
| incus snapshot delete <nom> <snap-name> | Supprime un snapshot |
| incus info <nom> | Affiche aussi les snapshots |
J’ai commencé à tester Caddy (reverse proxy), ainsi que son installation et sa première configuration :
Instalation de caddy :
sudo apt update sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list sudo apt update sudo apt install caddy
Configuration de base de Caddy pour avoir du HTTPS :
sudo vim /etc/caddy/Caddyfile
ip ou FQDN {
# Chemin du site
root * /var/www/site
# Activer les pages statiques
file_server
}
Si on veut un site dynamique en PHP, on ajoute cette ligne dans la config :
ip ou FQDN {
...
# Utiliser PHP-FPM
php_fastcgi unix//run/php/php-fpm.sock
}
Si on veut ajouter des logs de Caddy, toujours dans la conf :
ip ou FQDN {
...
log {
output file /var/log/caddy/access.log { # Chemin du log
roll_size 1gb # Taille maximale avant renouvellement
roll_keep 5 # Nombre de fichiers log conservés
roll_keep_for 720h # # Durée de conservation
}
}
}
Nous avons une mission de wifi réseaux :
| SCHEMA |
|---|
|
Flasher l’OS OpenWrt stable sur le routeur OpenWrt One :
On récupère la dernière version du firmware :
https://firmware-selector.openwrt.org/?version=25.12.2&target=mediatek%2Ffilogic&id=openwrt_one
On vient mettre le switch sur NOR derrière le routeur, puis on se connecte à l’interface web (http://192.168.1.1)
Après, on va dans System > Backup / Firmware et, tout en bas, il y a Flash new firmware image. On y insère l’image téléchargée.4
Ensuite, flasher la nouvelle version et décocher "keep settings" pour le reset.
Après cela, attendre que l’opération se termine, puis redémarrer en mode NAND pour pouvoir le configurer.
Jour 4 (2 avril 2026) :¶
On a commencé à installer le serveur sous Debian 13.
À un moment, il nous demande de partitionner le disque :
| Partition | Taille | Point de montage | Rôle |
| 1 | 512 Mo | /boot/efi | Pour le démarre en UEFI |
| 2 | 1 Go | /boot | Pour le boot Debian |
| 3 | 8 Go | swap | Sécurité si la RAM sature (à voir car jsp si c'est vraiment utile car on a 32go) |
| 4 | 50 Go | / (Racine) | Le Debian de l'hyperviseur, Incus et logs |
| 5 | ~940 Go | Aucun | Espace brut (Ne pas formater car on initialisera avec Incus en ZFS) |
On a continué la mission du Wi-Fi :
Configuration du routeur OpenWrt One pour se connecter au réseau Wi-Fi du téléphone
Jour 5 (3 avril 2026) :¶
On a flash l'OS PostMarketOS sur le PinePhone dans la mémoire interne (eMMC) :
Pour flasher un OS, on utilise bmaptool ou balenaEtcher
Il faut d'abord flasher Jumpdrive sur une carte micro SD en utilisant :
pine64-pinephone.img.xz (version normale)
ou
pine64-pinephone-charging.img.xz (version si la batterie est complètement vide)
On insère ensuite cette carte micro SD dans le PinePhone.
On démarre le PinePhone sur Jumpdrive et on le branche au PC.
Pour flasher Mobian ou PostMarketOS sur le stockage eMMC du PinePhone :
Mobian pour le PinePhone classique : sunxi
Mobian pour le PinePhone Pro : rockchip
PostMarketOS pour le PinePhone classique : PINE64 PinePhone
PostMarketOS pour le PinePhone Pro : PINE64 PinePhone Pro
Une fois le flash terminé, on débranche et éteint le PinePhone, puis on retire la carte micro SD.
Il faut ensuite flasher Tow-Boot sur la carte micro SD (c'est le fichier avec "install" dans le nom qu'il faut flasher)
pour le PinePhone classique : pine64-pinephoneA64-xxxx.xx-xxx.tar.xz
pour le PinePhone Pro : pine64-pinephonePro-xxxx.xx-xxx.tar.xz
On insère à nouveau cette carte micro SD dans le PinePhone.
On démarre sur Tow-Boot avec le PinePhone pour l'installer dans la mémoire eMMC.
On retire enfin la carte micro SD du téléphone, on le rallume, et on a réussi à démarrer sur PostMarketOS.
Pour mettre le PinePhone en hotspot, il faut le faire graphiquement avec le bouton et activer les données cellulaires et aussi "Mobile Data On".
On récupère le hotspot avec le routeur qui prend le relai pour émettre le réseau.
En ssh sur le routeur openwrt one pour installer le relay bridge :apk updateapk add relayd luci-proto-relayreboot
On accède au menu Network, puis Wireless et on sélectionne Radio 0 pour scanner et rejoindre le réseau du PinePhone.
On est allé dans Network, puis Interfaces et on a édité l'interface Wi-Fi du PinePhone pour sélectionner le réseau sont dans l'onglet Device.
On a ajouté un pont relais depuis Network, puis Interfaces en sélectionnant Add et Relay bridge.
On a configuré le pont relais via l'option Relay between networks en associant l'interface du PinePhone et le réseau LAN.
On a créé un point d'accès depuis Network, puis Wireless et Radio 1 en cliquant sur Add.
On est revenu dans les paramètres du nouveau réseau Wi-Fi pour assigner le périphérique (Device) au réseau LAN.
Pour configurer le routeur, on s'est basé sur cette vidéo
Pour la mission du serveur, on a update le tableau et on a installé Debian 13 en fonction :| Partition | Taille | Point de montage |
| 1 | 512 Mo | /boot/efi (pour grub) |
| 2 | 2 Go | /boot |
| 3 | 2 Go | swap |
| 4 | 50 Go | / (Racine) |
| 5 | ~940 Go | Aucun |
| Paramètre | Valeur à mettre |
| Nom | efi |
| Utiliser comme | Partition système EFI |
| Indicateur d'amorçage | présent |
| Taille | 512 Mo |
| Paramètre | Valeur à mettre |
| Nom | boot |
| Utiliser comme | ext4 journalisé |
| Point de montage | /boot |
| Options de montage | defaults |
| Étiquette | aucune |
| Blocs réservés | 5% |
| Utilisation habituelle | standard |
| Indicateur d'amorçage | absent |
| Taille | 2 Go |
| Paramètre | Valeur à mettre |
| Nom | swap |
| Utiliser comme | espace d'échange (swap) |
| Point de montage | (aucun) |
| Options de montage | (aucun) |
| Étiquette | aucune |
| Indicateur d'amorçage | absent |
| Taille | 2 Go |
| Paramètre | Valeur à mettre |
| Nom | root |
| Utiliser comme | ext4 journalisé |
| Point de montage | / |
| Options de montage | défauts |
| Étiquette | aucune |
| Blocs réservés | 5% |
| Utilisation habituelle | standard |
| Indicateur d'amorçage | absent |
| Taille | 50 Go |
Pour le reste du disque (~940 Go) on n'a rien touché
On a aussi créé quatre comptes (pierre, alexander, yoan et nicolas).
On a transféré les clés SSH avec cette commande : ssh-copy-id idcompte@ipserveur
Jour 6 (7 avril 2026) :¶
Cette fois-ci, on veut transférer la connexion du PinePhone au routeur via l'adaptateur USB > RJ45 :
Pour router les paquets entre les interfaces :
sudo sysctl -w net.ipv4.ip_forward=1
Pour le rendre permanent :
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
Pour activer la 4G :
sudo nmtui
Puis allez dans "Activer une connexion" et choisir la connexion mobile (4G).
Pour vérifier que la 4G fonctionne :
ping 8.8.8.8
Pour voir l'interface filaire :
ip a
Pour créer le partage de connexion avec l'interface filaire :
sudo nmcli connection add type ethernet ifname enx00e07c4601d6 ipv4.method shared con-name PartageUsb
Pour l'activer :
sudo nmcli connection up PartageUsb
Pour le routeur, on a ajusté la configuration :
le téléphone est désormais connecté au routeur via l'adaptateur.
Le routeur a été modifié pour s’adapter à ce changement :
dans Network > Interfaces on edit le repeat_wifi (le bridge) pour choisir dans Relay between networks "Lan" et "Wan".
Mise en place des utilisateurs dans le groupe sudo et désactivation du mode veille.
Passer en root :
su -
Désactiver le mode veille :
systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
Ajouter les utilisateurs au groupe sudo (leur donne les droits d'administration) :
usermod -aG sudo yoan
usermod -aG sudo nicolas
usermod -aG sudo pierre
usermod -aG sudo alexander
Redémarrer pour appliquer les changements :
reboot
Mise en place du RAID 1 logiciel sur le serveur toutatis avec mdadm (sda = miroir de sdb).
Installation des outils pour le raid :
su -
apt install mdadm gdisk rsync
Copier la table de partitions de sdb vers sda, puis typer les partitions en Linux RAID :
sfdisk -d /dev/sdb | sfdisk /dev/sda
sgdisk --typecode=2:A19D880F-05FC-4D3B-A006-743F0F84911E /dev/sda
sgdisk --typecode=3:A19D880F-05FC-4D3B-A006-743F0F84911E /dev/sda
sgdisk --typecode=4:A19D880F-05FC-4D3B-A006-743F0F84911E /dev/sda
Créer les arrays RAID :
mdadm --create /dev/md2 --level=1 --raid-devices=2 --metadata=1.0 /dev/sda2 missing
mdadm --create /dev/md3 --level=1 --raid-devices=2 /dev/sda3 missing
mdadm --create /dev/md4 --level=1 --raid-devices=2 /dev/sda4 missing
Créer les systèmes de fichiers :
mkfs.ext4 /dev/md2
mkswap /dev/md3
mkfs.ext4 /dev/md4
Monter et copier le système :
mkdir -p /mnt/newroot
mount /dev/md4 /mnt/newroot
mkdir /mnt/newroot/boot
mount /dev/md2 /mnt/newroot/boot
rsync -aAXv --exclude={"/proc/","/sys/","/dev/","/run/","/mnt/","/tmp/"} / /mnt/newroot/
Récupérer les UUID des arrays et mettre à jour fstab :
blkid /dev/md2 /dev/md3 /dev/md4
vi /mnt/newroot/etc/fstab
Sauvegarder la config mdadm :
mdadm --detail --scan | tee /mnt/newroot/etc/mdadm/mdadm.conf
Chroot, mise à jour initramfs et installation de GRUB :
for dir in /dev /dev/pts /proc /sys /run; do mount --bind $dir /mnt/newroot$dir; done
mount /dev/sda1 /mnt/newroot/boot/efi
chroot /mnt/newroot
update-initramfs -u -k all
grub-install /dev/sda
update-grub
exit
reboot
Après reboot on ajouter sdb au RAID pour compléter le miroir :
mdadm /dev/md2 --add /dev/sda2
mdadm /dev/md3 --add /dev/sda3
mdadm /dev/md4 --add /dev/sda4
grub-install /dev/sdb
watch cat /proc/mdstat
RAID complet quand tous les arrays affichent [UU].
Installation de Incus :
sudo apt install incus
Jour 7 (8 avril 2026) :¶
Pour Incus :
installer Incus :
sudo apt install incus
Pour configurer et initialiser Incus
sudo incus admin init
Would you like to use clustering? (yes/no) [default=no]: no Do you want to configure a new storage pool? (yes/no) [default=yes]: yes Name of the new storage pool [default=default]: default Where should this storage pool store its data? [default=/var/lib/incus/storage-pools/default]: Would you like to create a new local network bridge? (yes/no) [default=yes]: yes What should the new bridge be called? [default=incusbr0]: What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: auto What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: auto We detected that you are running inside an unprivileged container. This means that unless you manually configured your host otherwise, you will not have enough uids and gids to allocate to your containers. Your container's own allocation can be reused to avoid the problem. Doing so makes your nested containers slightly less safe as they could in theory attack their parent container and gain more privileges than they otherwise would. Would you like to have your containers share their parent's allocation? (yes/no) [default=yes]: yes Would you like the server to be available over the network? (yes/no) [default=no]: no Would you like stale cached images to be updated automatically? (yes/no) [default=yes]: yes Would you like a YAML "init" preseed to be printed? (yes/no) [default=no]: no
Pour Redmine :
Pour créer le conteneur :
incus launch images:debian/13 redmine
pour ouvrir le terminal du conteneur :
incus exec redmine bash
pour installer docker :
apt update && apt install -y docker.io docker-compose
pour créer la config du docker pour Redmine et PostgreSQL :
mkdir -p /opt/redmine
cd /opt/redmine
vi docker-compose.yml
services:
db:
image: postgres:15
restart: always
environment:
POSTGRES_USER: redmine
POSTGRES_PASSWORD: "Fablab"
POSTGRES_DB: redmine_db
volumes:
- ./pgdata:/var/lib/postgresql/data
redmine:
image: redmine:latest
restart: always
ports:
- "80:3000"
environment:
REDMINE_DB_POSTGRES: db
REDMINE_DB_USERNAME: redmine
REDMINE_DB_PASSWORD: "Fablab"
REDMINE_DB_DATABASE: redmine_db
volumes:
- ./redmine_files:/usr/src/redmine/files
depends_on:
- db
pour lancer le docker :
docker compose up -d
exit
fixer l'ip :
incus config device override redmine eth0 ipv4.address=10.218.184.3
pour appliquer :
incus restart redmine
accessible à :
http://redmine.192.168.23.3.nip.io/
Pour NextCloud :
Installation de la vm nextcloud :
sudo incus launch images:debian/13 nextcloud --vm
On installe Apache, MariaDB, PHP et tous les modules requis par Nextcloud :
sudo apt install apache2 mariadb-server php libapache2-mod-php php-mysql php-xml php-mbstring php-zip php-gd php-curl php-intl php-bcmath php-gmp php-imagick php-redis unzip wget -y
On ouvre la console MariaDB pour créer la base de données et l'utilisateur :
sudo mysql -u root
CREATE DATABASE nextcloud;
USE nextcloud;
CREATE USER 'nc_user'@'localhost' IDENTIFIED BY 'MotDePasse';
GRANT ALL PRIVILEGES ON nextcloud.* TO 'nc_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
On télécharge la dernière version de nextcloud et on l'extrait et on la place dans le dossier d'Apache :
wget https://download.nextcloud.com/server/releases/latest.zip
unzip latest.zip
sudo mv nextcloud /var/www/html/
On donne au serveur web Apache les droits sur le dossier Nextcloud pour qu'il puisse le lire et le modifier :
sudo chown -R www-data:www-data /var/www/html/nextcloud/
sudo chmod -R 755 /var/www/html/nextcloud/
On restart Apache :
sudo systemctl restart apache2
fixer l'ip :
incus config device override nextcloud eth0 ipv4.address=10.218.184.2
pour appliquer :
incus restart nextcloud
accessible à :
http://nextcloud.192.168.23.3.nip.io/nextcloud/index.php
Pour site web Laravel :
pour créer la VM sous debian13
sudo incus launch images:debian/13 laravel-web --vm
fixer l'ip :
sudo incus config device override laravel-web eth0 ipv4.address=10.218.184.5
pour appliquer :
sudo incus restart laravel-web
pour acceder au shell de la VM :
sudo incus shell laravel-web
installer les fonctionnalités pour Laravel :
apt update && apt upgrade -y
apt install -y nginx mariadb-server composer git unzip curl \
php-fpm php-mysql php-xml php-mbstring php-curl php-zip php-bcmath php-intl
pour config le serveur :
vi /etc/nginx/sites-available/laravel
source
server {
listen 80;
listen [::]:80;
server_name example.com;
root /srv/example.com/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_hide_header X-Powered-By;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
pour activer le site et redémarrer Nginx :
ln -s /etc/nginx/sites-available/laravel /etc/nginx/sites-enabled/
systemctl restart nginx
accessible à :
http://laravel.192.168.23.3.nip.io/
Pour Git Forgejo :
Installation de la vm git :
sudo incus launch images:debian/13 git --vm
Installer les paquets requis Git et SQLite3 :
sudo apt install git git-lfs wget tar sqlite3 postgresql postgresql-client -y
Pour isoler et sécuriser le service créer un utilisateur système dédié nommé forgejo :
sudo adduser \
--system \
--shell /bin/bash \
--gecos 'Forgejo Git Service' \
--group \
--disabled-password \
--home /var/lib/forgejo \
forgejo
Récupérer la version de Forgejo puis placer le binaire exécutable dans le dossier système approprié :
wget -O forgejo https://codeberg.org/forgejo/forgejo/releases/download/v14.0.3/forgejo-14.0.3-linux-amd64
sudo chmod +x forgejo
sudo mv forgejo /usr/local/bin/forgejo
Créer les dossiers où Forgejo stockera ses dépôts, ses configurations et ses logs, et donne la propriété à l'utilisateur forgejo :
sudo mkdir -p /var/lib/forgejo/{custom,data,log}
sudo chown -R forgejo:forgejo /var/lib/forgejo/
sudo chmod -R 750 /var/lib/forgejo/
sudo mkdir /etc/forgejo
sudo chown root:forgejo /etc/forgejo
sudo chmod 770 /etc/forgejo
Pour automatiser le démarrage de Forgejo :
sudo vi /etc/systemd/system/forgejo.service
[Unit] Description=Forgejo (Self-Hosted Git Service) After=network.target [Service] RestartSec=2s Type=simple User=forgejo Group=forgejo WorkingDirectory=/var/lib/forgejo ExecStart=/usr/local/bin/forgejo web --config /etc/forgejo/app.ini Restart=always Environment=USER=forgejo HOME=/var/lib/forgejo GITEA_WORK_DIR=/var/lib/forgejo [Install] WantedBy=multi-user.target
Rechargez systemd puis activer et démarrer le service Forgejo :
sudo systemctl daemon-reload
sudo systemctl enable --now forgejo
sudo systemctl status forgejo
fixer l'ip :
sudo incus config device override git eth0 ipv4.address=10.218.184.4
pour appliquer :
sudo incus restart git
accessible à :
http://git.192.168.23.3.nip.io/
Pour Vaultwarden :
sudo incus launch images:debian/13 vaultwarden
sudo incus shell vaultwarden
apt update && apt install -y docker.io docker-compose
mkdir -p /opt/vaultwarden
cd /opt/vaultwarden
vi docker-compose.yml
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: always
environment:
- WEBSOCKET_ENABLED=true
- ADMIN_TOKEN="Fablab"
ports:
- "80:80"
volumes:
- ./vw-data:/data
docker compose up -d
fixer l'ip :
sudo incus config device override vaultwarden eth0 ipv4.address=10.218.184.6
pour appliquer :
sudo incus restart vaultwarden
accessible à :
http://vaultwarden.192.168.23.3.nip.io/
Mais Vaultwarden n'accepte pas les pages en HTTP, donc il faudra demain mettre la page en HTTPS.
Pour Caddy :
apt install caddy
sudo vi /etc/caddy/Caddyfile
http://nextcloud.192.168.23.3.nip.io {
reverse_proxy 10.218.184.2:80
}
http://redmine.192.168.23.3.nip.io {
reverse_proxy 10.218.184.3:80
}
http://git.192.168.23.3.nip.io {
reverse_proxy 10.218.184.4:3000
}
http://laravel.192.168.23.3.nip.io {
reverse_proxy 10.218.184.5:80
}
http://vaultwarden.192.168.23.3.nip.io {
reverse_proxy 10.218.184.6:80
}
sudo systemctl restart caddy
On a mit a jour le tableau et le schema car il y a vaultwarden :
| Service | RAM minimum | RAM recommandée | CPU minimum | CPU recommandé |
| Apache | 512 MB | 1–4 GB | 1 core | 2+ cores |
| Nginx | 512 MB | 1 GB | 1 core | 2+ cores |
| Laravel | 1 GB | 2+ GB | 1 core | 2+ cores |
| Redmine | 512 MB | 1–2 GB | 1 core | 2 cores |
| Forgejo | 512 MB | 2 GB | 1 core | 2+ cores |
| Nextcloud | 512 MB | 2 GB | 1 core | 2 cores |
| vaultwarden | 512 MB | 1 GB | 1 core | 2 cores |
| total | 4 GB | 10-14+ GB | 7 cores | 14+ cores |
| SCHEMA |
|---|
|
Jour 8 (9 avril 2026) :¶
Pour projet Routeur PinePhone WireGuard :
https://www.reddit.com/r/WireGuard/comments/18pee5i/wireguard_working_as_server_and_client_of_another/
| Photo schema |
|---|
|
Pour le projet serveur, on a maintenant accès en SSH et on fait la migration vers le nouveau serveur dans les VMs/conteneurs d'Incus.
tar -czvf ~/migration-complete.tar.gz \
/opt/laravel/laravel \
/etc/apache2/sites-enabled/sql_laravel.sql \
/var/www/html/index.html \
/var/www/html/portfolios \
/var/www/html/trap
scp -P 55555 nicolas@projects.cohabit.fr:~/migration-complete.tar.gz ~/
mkdir -p ~/temp-migration
tar -xzvf ~/migration-complete.tar.gz -C ~/temp-migration
sudo incus exec laravel-web -- rm -rf /var/www/laravel
sudo incus file push -r ~/temp-migration/opt/laravel/laravel laravel-web/var/www/
sudo incus file push ~/temp-migration/var/www/html/index.html laravel-web/var/www/laravel/public/
sudo incus file push -r ~/temp-migration/var/www/html/portfolios laravel-web/var/www/laravel/public/
sudo incus file push -r ~/temp-migration/var/www/html/trap laravel-web/var/www/laravel/public/
sudo incus file push ~/temp-migration/etc/apache2/sites-enabled/sql_laravel.sql laravel-web/root/
sudo incus shell laravel-web
mysql -u root -e "CREATE DATABASE laravel_db;"
mysql -u root -e "CREATE USER 'laravel'@'localhost' IDENTIFIED BY 'MotDePasseSQL!';"
mysql -u root -e "GRANT ALL PRIVILEGES ON laravel_db.* TO 'laravel'@'localhost'; FLUSH PRIVILEGES;"
mysql -u root laravel_db < /root/sql_laravel.sql
vi /var/www/laravel/.env
chown -R www-data:www-data /var/www/laravel
chmod -R 775 /var/www/laravel/storage /var/www/laravel/bootstrap/cache
sed -i 's/index index.php;/index index.html index.php;/g' /etc/nginx/sites-available/laravel
rm /etc/nginx/sites-enabled/default
vi /etc/nginx/sites-available/laravel
server {
listen 80;
server_name _;
root /var/www/laravel/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index index.html index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
systemctl restart nginx
php artisan key:generate
php artisan config:clear
php artisan cache:clear
Jour 9 (10 avril 2026) :¶
On a récupéré le dessin du logo de connexion SSH depuis le serveur cohabit :
cat /etc/motd
Copier le contenu affiché, puis se rendre sur le serveur toutatis :
vi /etc/motd
Coller le contenu précédemment copié.
Récupérer la base de donnés git :
serveur cohabit :
sudo -u postgres pg_dump gitea > /tmp/forgejo-db.sql
serveur toutatis
scp -P 55555 yoan@projets.cohabit.fr:/tmp/forgejo-db.sql ~/
sudo incus file push ~/forgejo-db.sql git/tmp/forgejo-db.sql
vm git :
sudo -u postgres psql
CREATE USER gitea WITH PASSWORD 'MDP';
CREATE DATABASE gitea OWNER gitea;
\q
PGPASSWORD=MDP psql -U gitea -h 127.0.0.1 -d gitea < /tmp/forgejo-db.sql
En interface graphique :
Base de données :
| Champ | Valeur |
| Type | PostgreSQL |
| Hôte | 127.0.0.1:5432 |
| Nom d'utilisateur | gitea |
| Mot de passe | MDP |
| Nom de base de données | gitea |
Configuration générale :
| Champ | Valeur |
| Titre du site | Forgejo Fablab Cohabit |
| Emplacement racine des dépôts | /var/lib/forgejo/data/gitea-repositories |
| Répertoire racine Git LFS | /var/lib/forgejo/data/lfs |
| Exécuter avec le compte | forgejo |
| Port SSH | 22222 |
| Port HTTP | 10001 |
Compte administrateur (en bas de page)
| Champ | Valeur |
| Nom d'utilisateur | un nom admin |
| ton email | |
| Mot de passe | un mot de passe fort |
Jour 10 (20 avril 2026) :¶
On a agrendit le / car c'est la ou on stok tout et on avait 900GO sans partition
| Partition | Taille | Point de montage | Rôle |
| 1 | 512 Mo | /boot/efi | Pour le démarre en UEFI |
| 2 | 1 Go | /boot | Pour le boot Debian |
| 3 | 2 Go | swap | Sécurité si la RAM sature (à voir car jsp si c'est vraiment utile car on a 32go) |
| 4 | 996.5 Go | / (Racine) | Le Debian de l'hyperviseur, Incus et logs |
1. Voir l'espace disque actuel
df -h /
1. Voir la structure des disques et du RAID
lsblk -f
1. Voir les partitions exactes
sudo fdisk -l /dev/sda
sudo fdisk -l /dev/sdb
2. Agrandir la partition sda4
sudo fdisk /dev/sda
Dans fdisk, taper dans l'ordre :
d # Supprimer une partition
4 # Numéro de partition 4
n # Créer une nouvelle partition
4 # Numéro de partition 4
8814592 # Secteur de début
# Entrée vide = utiliser tout l'espace restant
N # Ne PAS supprimer la signature linux_raid_member
t # Changer le type
4 # Partition 4
linux-raid # Type RAID Linux
w # Écrire et quitter
3. Agrandir la partition sdb4
sudo fdisk /dev/sdb
Dans fdisk, taper dans l'ordre :
d # Supprimer une partition
4 # Numéro de partition 4
n # Créer une nouvelle partition
4 # Numéro de partition 4
8814592 # Secteur de début
# Entrée vide = utiliser tout l'espace restant
N # Ne PAS supprimer la signature linux_raid_member
t # Changer le type
4 # Partition 4
linux-raid # Type RAID Linux
w # Écrire et quitter
4. Vérifier que les deux partitions sont bien en "RAID Linux"
sudo fdisk -l /dev/sda | grep sda4
sudo fdisk -l /dev/sdb | grep sdb4
Les deux doivent afficher "RAID Linux" et ~927 Go
5. Agrandir le volume RAID mdadm
sudo mdadm --grow /dev/md4 --size=max
6. Surveiller la resynchronisation
watch cat /proc/mdstat
La synchronisation est terminée quand on voit :
md4 : active raid1 sda4[0] sdb4[2]
972320768 blocks super 1.2 [2/2] [UU]
7. Étendre le système de fichiers ext4
sudo resize2fs /dev/md4
8. Vérifier le résultat final
df -h /
____________________________________________________________________________________________________________________
Agrandir l'espace de la vm git
sudo incus config device override git root size=100GiB
Entré dans la VM git
sudo incus exec git -- bash
Installer gdisk dans la VM
apt install -y gdisk
Agrandir la partition
sgdisk -e /dev/sda
exit
Redémarrer la VM
sudo incus restart git
Agrandir le filesystem
sudo incus exec git -- bash
resize2fs /dev/sda2
Vérifier
df -h /
____________________________________________________________________________________________________________________
Transféré les données de l'ancien git vers le nouveau
Depuis l'ancien serveur
sudo zip -r ~/forgejo-repos.zip /var/lib/forgejo/data
Depuis le nouveau serveur
scp -P 55555 yoan@projets.cohabit.fr:~/forgejo-repos.zip ~/
sudo incus file push ~/forgejo-repos.zip git/root/forgejo-repos.zip
sudo incus exec git -- bash
Dans la VM git
Ne pas remplacer les fichier du dossier indexers et queues
unzip /root/forgejo-repos.zip -d /
chown -R forgejo:forgejo /var/lib/forgejo/data/
systemctl restart forgejo
systemctl status forgejo
Jour 11 (21 avril 2026) :¶
Mtre en place un serveur zotero
sudo incus launch images:debian/13 zotero
sudo incus shell zotero
apt update && apt install -y docker.io docker-compose
docker run -d \
--name=zotero \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Etc/UTC \
-p 3000:3000 \
-p 3001:3001 \
-v /path/to/config:/config \
--shm-size="1gb" \
--restart unless-stopped \
lscr.io/linuxserver/zotero:latest
fixer l'ip :
sudo incus config device override zotero eth0 ipv4.address=10.218.184.7
Pour appliquer :
sudo incus restart zotero
Rajouter zotero dans caddy
sudo vi /etc/caddy/Caddyfile
zotero.192.168.23.3.nip.io {
tls internal
reverse_proxy 10.218.184.7:3000
}
sudo systemctl restart caddy
Accéder à Zotero
http://zotero.192.168.23.3.nip.io
___________________________________________________________________________________________
Restauration des données Redmine
Migration donnée
sudo tar -czvf ~/migration-var-redmine.tar.gz /var/www/html/redmine
sudo tar -czvf ~/migration-opt-redmine.tar.gz /opt/redmine
sudo tar -czvf ~/migration-etc-apache2.tar.gz /etc/apache2/
Migration de la base de données
echo '127.0.0.1:5432:laravel:laravel:p5U\!aUe8!' > ~/.pgpass
chmod 600 ~/.pgpass
pg_dump -U laravel -h 127.0.0.1 -p 5432 -Fc --file=/tmp/laravel_db.dump laravel
Récupération des données sur le nouveau serveur
scp -P 55555 nicolas@projets.cohabit.fr:~/migration-etc-apache2.tar.gz ~/
scp -P 55555 nicolas@projets.cohabit.fr:~/migration-opt-redmine.tar.gz ~/
scp -P 55555 nicolas@projets.cohabit.fr:~/migration-var-redmine.tar.gz ~/
sudo incus shell redmine
Jour 12 (22 avril 2026) :¶
J'ai tester de faire fontionner redmine mais sans succes
Voici le comparatif des version entre l'ancien et le nouveau serveur pour laravel
| Composant | Serveur cohabit | Serveur laravel-web | Commande pour vérifier |
| Apache2 | 2.4.66 | 2.4.66 | apachectl -v |
| PHP | 8.4.15 | 8.4.16 | php -v |
| PostgreSQL | 17.4 | 17.9 | psql --version |
| Laravel | 9.52.10 | 9.52.10 | php artisan --version |
| Modules PHP (php -m) | [PHP Modules] calendar Core ctype date exif FFI fileinfo filter ftp gettext hash iconv json ldap libxml openssl pcntl pcre PDO pdo_pgsql pgsql Phar posix random readline Reflection session shmop sockets sodium SPL standard sysvmsg sysvsem sysvshm tokenizer Zend OPcache zlib [Zend Modules] Zend OPcache | [PHP Modules] calendar Core ctype date exif FFI fileinfo filter ftp gettext hash iconv json ldap libxml openssl pcntl pcre PDO pdo_pgsql pgsql Phar posix random readline Reflection session shmop sockets sodium SPL standard sysvmsg sysvsem sysvshm tokenizer Zend OPcache zlib [Zend Modules] Zend OPcache | php -m |
Jour 13 (23 avril 2026) :¶
Analyse de la Documentation de la suite numérique
Migration de Redmine sur le serveur incus (en gardant la même version)
Initialisation de la vm :
sudo incus init images:debian/13 redmine --vm
sudo incus config device override redmine root size=100GiB
sudo incus config device override redmine eth0 ipv4.address=10.218.184.3
sudo incus start redmine
sudo incus file push -r ~/redmine_db.dump redmine2/root/
sudo incus file push -r ~/migration-opt-redmine.tar.gz redmine2/root/
sudo incus shell redmine
Installation des dépendances système
apt update && apt upgrade -y
apt install -y ruby ruby-dev ruby-psych postgresql postgresql-contrib \
postgresql-server-dev-all libxslt1-dev libyaml-dev libxml2-dev \
libpq-dev libcurl4-openssl-dev zlib1g-dev apache2 apache2-dev \
gcc g++ make patch imagemagick git
Création de la base de données PostgreSQL
su - postgres
createuser redmine
createdb redmine -O redmine
set +H
psql -c "ALTER USER redmine WITH PASSWORD 'MDP';"
set -H
exit
Configuration de pg_hba.conf
vi /etc/postgresql/17/main/pg_hba.conf
local all all md5
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
systemctl restart postgresql
Restauration de la base de donnée
cp /root/redmine_db.dump /tmp/redmine_db.dump
su - postgres
dropdb redmine
createdb redmine -O redmine
pg_restore --no-owner -x -d redmine /tmp/redmine_db.dump
exit
Donner les droits à l'utilisateur redmine :
su - postgres
psql -d redmine
sql
GRANT ALL ON SCHEMA public TO redmine;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO redmine;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO redmine;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO redmine;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO redmine;
ALTER DATABASE redmine OWNER TO redmine;
\q
exit
Déploiement des fichiers Redmine
tar -xzvf migration-opt-redmine.tar.gz -C /
Installation des gems Ruby
cd /opt/redmine/redmine-5.0.5/
vi Gemfile
Avant : ruby ">= 2.5.0", "< 3.2.0"
Après : ruby ">= 2.5.0", "< 3.4.0"
Avant : gem "pg", "~> 1.2.2", :platforms => [:mri, :mingw, :x64_mingw]
Après : gem "pg", "~> 1.5.0", :platforms => [:mri, :mingw, :x64_mingw]
gem install bundler
bundle config set --local without 'development test mysql sqlite'
bundle update pg
bundle install
Initialisation de Redmine
bundle exec rake generate_secret_token RAILS_ENV=production
bundle exec rake db:migrate RAILS_ENV=production
Permissions des fichiers
chown -R www-data:www-data /opt/redmine/redmine-5.0.5
chmod -R 755 /opt/redmine/redmine-5.0.5
chmod -R 777 /opt/redmine/redmine-5.0.5/tmp \
/opt/redmine/redmine-5.0.5/log \
/opt/redmine/redmine-5.0.5/files \
/opt/redmine/redmine-5.0.5/public/plugin_assets
Installation de Passenger via APT
apt install -y dirmngr gnupg apt-transport-https ca-certificates
curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key.txt | \
gpg --dearmor | tee /etc/apt/trusted.gpg.d/phusion.gpg > /dev/null
echo "deb https://oss-binaries.phusionpassenger.com/apt/passenger bookworm main" > \
/etc/apt/sources.list.d/passenger.list
apt update
apt install -y libapache2-mod-passenger
a2enmod passenger
systemctl restart apache2
Configuration Apache
vi /etc/apache2/sites-available/redmine.conf
<VirtualHost *:80>
DocumentRoot /opt/redmine/redmine-5.0.5/public
PassengerRuby /usr/bin/ruby3.3
<Directory /opt/redmine/redmine-5.0.5/public>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/redmine_error.log
CustomLog ${APACHE_LOG_DIR}/redmine_access.log combined
</VirtualHost>
a2ensite redmine
rm /etc/apache2/sites-available/default-ssl.conf
rm /etc/apache2/sites-available/000-default.conf
systemctl restart apache2
exit
Configuration de caddy
vi /etc/caddy/Caddyfile
redmine.192.168.23.3.nip.io {
tls internal
reverse_proxy 10.218.184.3:80
}
systemctl restart caddy
Accéder au site :
https://redmine.192.168.23.3.nip.io/
Jour 14 (24 avril 2026) :¶
On a tenté de mettre à jour Redmine 5.0.5 en 6.1.2 mais sans succés
Jour 15 (27 avril 2026) :¶
Nous avons upgradé redmine 5.0.5 vers redmine 5.1.12 et on a aussi un autre conteneur redmine pour tester la version 6.1.2, mais elle n'est pas totalement compatible.
On a deux conteneurs incus que l'on a renommé ainsi :
redmine-6-1-2
redmine-5-1-12
et on a créé des snapshots pour chacune :
pour redmine-6-1-2 elle s'appelle redmine-6-1-2
et pour redmine-5-1-12 elles s'appellent redmine-5-1-12 et aussi redmine-5-0-5
Pour le conteneur de redmine-6-1-2, on a donc mis à jour de 5.0.5 vers 6.1.2, mais les thèmes ne sont pas compatibles (icônes devenues noires et énormes), donc on les a retirés pour laisser le thème par défaut et pour les plugins, on a téléchargé des versions compatibles.
Pour le conteneur de redmine-5-1-12, on a donc mis à jour de 5.0.5 vers 5.1.12 (aucun problème de compatibilité) mais on a aussi laissé dessus la version 5.0.5 avec sa snapshot au cas de problème.
Upgrade redmine 5.0.5 vers 5.1.12
Télécharger Redmine
cd /opt/redmine
wget https://www.redmine.org/releases/redmine-5.1.12.tar.gz
tar -xzf redmine-5.1.12.tar.gz
Config base de données
cp redmine-5.0.5/config/database.yml redmine-5.1.12/config/
Config générale
cp redmine-5.0.5/config/configuration.yml redmine-5.1.12/config/
Fichiers des projets
cp -r redmine-5.0.5/files/* redmine-5.1.12/files/
Plugins
cp -r redmine-5.0.5/plugins/* redmine-5.1.12/plugins/
Thèmes
cp -r redmine-5.0.5/public/themes/* redmine-5.1.12/public/themes/
Modifier L'utilisateur pour le nouveau Redmine
sudo -u postgres psql -d redmine
Changer le propriétaire de toutes les tables
DO $$
DECLARE obj RECORD;
BEGIN
FOR obj IN SELECT tablename FROM pg_tables WHERE schemaname = 'public' LOOP
EXECUTE 'ALTER TABLE public.' || quote_ident(obj.tablename) || ' OWNER TO redmine';
END LOOP;
END $$;
Changer le propriétaire de toutes les séquences
DO $$
DECLARE obj RECORD;
BEGIN
FOR obj IN SELECT sequence_name FROM information_schema.sequences WHERE sequence_schema = 'public' LOOP
EXECUTE 'ALTER SEQUENCE public.' || quote_ident(obj.sequence_name) || ' OWNER TO redmine';
END LOOP;
END $$;
GRANT ALL ON SCHEMA public TO redmine;
\q
Installer Redmine
cd /opt/redmine/redmine-5.1.12
bundle config set --local without 'development test'
bundle install
bundle exec rake db:migrate RAILS_ENV=production
Régénérer le token secret
bundle exec rake generate_secret_token RAILS_ENV=production
Migration des plugins
bundle exec rake redmine:plugins:migrate RAILS_ENV=production
Recompiler les assets
bundle exec rake assets:clobber RAILS_ENV=production
bundle exec rake assets:precompile RAILS_ENV=production
Appliquer les permissions
chown -R www-data:www-data /opt/redmine/redmine-5.1.12
vi /etc/apache2/sites-enable/redmine.conf
<VirtualHost *:80>
DocumentRoot /opt/redmine/redmine-5.1.12/public
PassengerRuby /usr/bin/ruby3.3
<Directory /opt/redmine/redmine-5.1.12/public>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/redmine_error.log
CustomLog ${APACHE_LOG_DIR}/redmine_access.log combined
</VirtualHost>
Redémarrer le service
systemctl restart apache2
Upgrade redmine 5.0.5 vers 6.1.2
C'est à peu près pareil qu'au-dessus, mais à la place d'écrire 5.1.12, c'est 6.1.2, mais il y a un problème avec cette version, car les thèmes et les plugins ne sont pas compatibles donc pour ces parties-là, nous avons fait :
Pour les thèmes :
Nous avons déplacé le dossier /public/themes/ à la racine du dossier Redmine parce que dans les versions 6.X, c'est comme ça donc /themes/ et on a regardé le résultat, mais les icônes sont surdimensionnées et toute noires donc comme nous ne savons pas régler cela, on a laissé le thème par défaut de Redmine.
Pour les plugins :
On a pu trouver les dernières versions compatibles pour les plugins redmine_wiki_extensions et redmine_agile mais pas pour redmine_embedded_video donc on l'a laissé, mais il ne fonctionne pas (nous avons l'impression que les plugins ne change rien au Redmine comme s'ils n'étaient pas chargés alors qu'ils le sont bien).
Migration des plugins
bundle exec rake redmine:plugins:migrate RAILS_ENV=production
Nous avons aussi envoyé un message à Alexander et Pierre :
Voici une mise à jour sur l'avancement de nos déploiements. Nous avons mis en place Vaultwarden et Zotero en conteneurs, alors que Git-Forgejo, Nextcloud, Redmine et Laravel sont installés sur des VMs. Si vous préférez qu'un de ces services soit sur une VM plutôt qu'un conteneur ou inversement, dites-le-nous et nous ferons la modification. Nous avions également quelques questions pour la suite : Pour Laravel, vous pouvez consulter la liste des services installés sur le conteneur et comparer leurs versions avec celles du serveur Cohabit actuel sur ce lien : https://projets.cohabit.fr/redmine/projects/accueil/wiki/Nicolas_Schmauch_2#Jour-18-Mercredi-22-avril-2026. Souhaitez-vous que nous procédions à leur mise à jour ? Pour Redmine, nous avons déjà installé trois versions différentes pour faire des tests. Il ne nous reste plus qu'à choisir laquelle conserver. Laquelle préférez-vous garder ? - La version actuelle du serveur Cohabit (5.0.5). - Une version mise à jour compatible (5.1.12). - Une version plus récente, mais moins compatible (6.1.2). Vous trouverez plus de détails sur ces versions de Redmine ici : https://projets.cohabit.fr/redmine/projects/accueil/wiki/Nicolas_Schmauch_2#Jour-21-Lundi-27-avril-2026. Enfin, nous avons découvert Watchtower, un outil qui permet de maintenir automatiquement à jour les conteneurs Docker. Le projet officiel ayant été archivé (https://github.com/containrrr/watchtower), nous avons trouvé un fork qui est toujours maintenu (https://github.com/nicholas-fedor/watchtower/ https://watchtower.nickfedor.com/v1.16.1/). Si cette automatisation vous intéresse, nous pouvons vous la mettre en place.
Jour 16 (28 avril 2026) :¶
On nous a répondu :
Salut, automatiser la montée de version je ne suis pas sûr mais à voir pourquoi pas, en revanche surveiller et prévenir/alerter ça oui à 100% à minima. Pour redmine le meilleur conseil que j'ai à donner c'est de ne jamais faire la montée de version sur le serveur actuel ou vous allez avoir des envies bien sombres derrière. La version pleinement compatible semble déjà un bon compromis pour le moment, à la limite si vous trouvez d'autres thèmes sympa on peut regarder pour les mettre ou modifier aux couleurs du fablab En revanche je vais faire l'embetant, je pourrais avoir en plus un petit graph de l'infra vm/kube avec les différents points d'entrée ? (Ou plus tard si vous voulez, si vous comptez encore modifier des choses après ces premiers tests :) )
Donc, nous avons demandé :
Nous pensons à automatiser la montée de version, surtout pour Vaultwarden, car souvent les applications et extensions Bitwarden refusent la connexion à un serveur Vaultwarden qui n'est pas à jour. Donc pourquoi pas mettre watchtower juste pour vaultwarden.
Il nous a dit :
Ça fait des Snapshot automatiquement avant la montée de version ? Si oui ça serait parfait du coup et possible de l'appliquer aux autres
Comme Watchtower ne permet pas de faire de snapshot, nous avons donc convenu de faire un script :
Non, Watchtower ne fait pas de snapshot automatique avant la mise à jour. Le plus propre pour nous, ce serait un script côté Incus et quand une nouvelle version est disponible, il crée un snapshot (la nuit), lance la mise à jour (la nuit), puis envoie une alerte (les snapshots sont gérés avec incus).On a fait un nouveau schéma du serveur pour répondre à Alexander.
| Schéma serveur |
|---|
|
Pour la recherche de theme compatible avec la version de Redmine-6.1.2, nous avons vu que le theme utilisé pour la version de Redmine-5.0.5 etait PurpleMine2 et on a trouvé Opale (qui un fork de PurpleMine2, car il est devenu "archived").
Le problème, c'est que le thème Opale n'a pas de "dark mode" donc il faut le modifier pour pouvoir en avoir un et aussi changer les couleurs du thème pour correspondre à l'ancien (le vert du fablab).
Commande pour changer le thème :
RAILS_ENV=production bundle exec rails console
Setting.ui_theme = 'opale-1.6.7'
exit
Pour appliquer :
bundle exec rake assets:precompile RAILS_ENV=production
systemctl restart apache2
https://github.com/fraoustin/redmine_dark
https://www.redmine.org/plugins/redmine_dark
https://github.com/gagnieray/opale
https://www.redmine.org/projects/redmine/wiki/Theme_List
Jour 17 (29 avril 2026) :¶
On a mis à jour le schéma du serveur pour répondre à la demande d'Alexander :
| Schéma serveur |
|---|
|
Nous avons fait des ajustements sur le thème Opale pour le rendre sombre et vert comme l'ancien thème sur le Redmine-5.0.5 et nous avons aussi commencé à faire un script pour maintenir le serveur à jour tout en créant avant les mises à jour, des snapshots pour chaque service (puis plus tard des alertes, mais pas encore).
Jour 18 (30 avril 2026) :¶
Début de script pour les snapshot :
#!/bin/bash
# ============================================================
# incus-auto-update.sh
# Snapshot + Mise à jour automatique des instances Incus
# Conteneurs (CT) : Docker Compose pull & up
# Machines Virtuelles (VM) : apt update & full-upgrade
# ============================================================
# ─── CONFIGURATION ──────────────────────────────────────────
# Durée de rétention des snapshots avant suppression automatique
SNAPSHOT_EXPIRY="7d"
# Répertoire de logs
LOG_DIR="/var/log/incus-updates"
LOG_FILE="${LOG_DIR}/update-$(date +%Y-%m-%d).log"
# ─── INSTANCES ──────────────────────────────────────────────
# Conteneurs (CT) avec Docker — chemin vers le docker-compose.yml
declare -A CT_COMPOSE_PATHS=(
["vaultwarden"]="/opt/vaultwarden"
["zotero"]="/opt/zotero"
)
# Machines Virtuelles (VM) — mise à jour apt
VMS=("git" "laravel-web" "nextcloud" "redmine-6-1-2")
# ─── FONCTIONS ──────────────────────────────────────────────
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
send_notification() {
local message="$1"
local payload
curl -s -X POST -H "Content-Type: application/json" \
-d "$payload" "$WEBHOOK_URL" > /dev/null 2>&1
}
create_snapshot() {
local instance="$1"
local snap_name="$2"
log " Création du snapshot '$snap_name' pour '$instance'..."
if incus snapshot create "$instance" "$snap_name" --expiry "$SNAPSHOT_EXPIRY" >> "$LOG_FILE" 2>&1; then
log " Snapshot '$snap_name' créé (expiration : $SNAPSHOT_EXPIRY)"
return 0
else
log " Échec de création du snapshot pour '$instance'"
return 1
fi
}
update_container_docker() {
local instance="$1"
local compose_path="$2"
local snap_name="$3"
log " [$instance] Vérification des mises à jour Docker..."
# Récupère les images (pull)
incus exec "$instance" -- bash -c "cd '$compose_path' && docker compose pull" >> "$LOG_FILE" 2>&1
PULL_STATUS=$?
if [ $PULL_STATUS -ne 0 ]; then
log " [$instance] Impossible de pull les images Docker. Vérifiez la connexion."
send_notification " **[$instance]** Échec du pull Docker. Mise à jour annulée. Snapshot disponible : \`$snap_name\`"
return 1
fi
# Recrée les conteneurs si une nouvelle image est disponible
incus exec "$instance" -- bash -c "cd '$compose_path' && docker compose up -d --remove-orphans" >> "$LOG_FILE" 2>&1
UP_STATUS=$?
if [ $UP_STATUS -eq 0 ]; then
log " [$instance] Conteneurs Docker mis à jour avec succès."
# Nettoyage des anciennes images inutilisées
incus exec "$instance" -- bash -c "docker image prune -f" >> "$LOG_FILE" 2>&1
send_notification " **[$instance]** Mise à jour Docker réussie. Snapshot de secours : \`$snap_name\`"
else
log " [$instance] Échec de la mise à jour Docker."
send_notification " **[$instance]** Échec mise à jour Docker.\nRestauration possible avec :\n\`incus restore $instance $snap_name\`"
fi
return $UP_STATUS
}
update_vm_apt() {
local instance="$1"
local snap_name="$2"
log " [$instance] Lancement de apt update & full-upgrade..."
incus exec "$instance" -- bash -c \
"apt-get update -qq && apt-get full-upgrade -y && apt-get autoremove -y && apt-get clean" \
>> "$LOG_FILE" 2>&1
APT_STATUS=$?
if [ $APT_STATUS -eq 0 ]; then
log " [$instance] Paquets mis à jour avec succès."
send_notification " **[$instance]** apt full-upgrade réussi. Snapshot de secours : \`$snap_name\`"
else
log " [$instance] Échec de apt full-upgrade."
send_notification " **[$instance]** Échec apt full-upgrade.\nRestauration possible avec :\n\`incus restore $instance $snap_name\`"
fi
return $APT_STATUS
}
# ─── MAIN ───────────────────────────────────────────────────
mkdir -p "$LOG_DIR"
DATE=$(date +%Y-%m-%d_%H-%M)
log "======================================================"
log " Démarrage des mises à jour automatiques Incus"
log "======================================================"
send_notification " **Incus Auto-Update** démarré ($(date '+%d/%m/%Y %H:%M'))"
# ── Conteneurs Docker (CT) ──────────────────────────────────
for INSTANCE in "${!CT_COMPOSE_PATHS[@]}"; do
SNAP_NAME="pre-update-${DATE}"
COMPOSE_PATH="${CT_COMPOSE_PATHS[$INSTANCE]}"
log ""
log "── CT : $INSTANCE ──────────────────────────────────"
if create_snapshot "$INSTANCE" "$SNAP_NAME"; then
update_container_docker "$INSTANCE" "$COMPOSE_PATH" "$SNAP_NAME"
else
send_notification " **[$INSTANCE]** Snapshot impossible. Mise à jour annulée par sécurité."
fi
done
# ── Machines Virtuelles (VM) ────────────────────────────────
for INSTANCE in "${VMS[@]}"; do
SNAP_NAME="pre-update-${DATE}"
log ""
log "── VM : $INSTANCE ──────────────────────────────────"
if create_snapshot "$INSTANCE" "$SNAP_NAME"; then
update_vm_apt "$INSTANCE" "$SNAP_NAME"
else
send_notification " **[$INSTANCE]** Snapshot impossible. Mise à jour annulée par sécurité."
fi
done
log ""
log "======================================================"
log " Traitement terminé."
log "======================================================"
send_notification "**Incus Auto-Update** terminé. Logs : \`$LOG_FILE\`"
# Nettoyage des logs de plus de 30 jours
find "$LOG_DIR" -name "*.log" -mtime +30 -delete
Jour 19 (4 mai 2026) :¶
Nous avons essayé de modifier le thème en dark mode pour Redmine 6.1.2, mais en analysant plus en détaille, il y a des menus qui ont disparu et d'autre incompatibilité donc nous avons mis à jour le Redmine vers la 5.1.12 qui est compatible (l'ancienne version 5.0.5).
Nous travaillons maintenant sur le script qui permettra de faire des snapshots et mettre à jour les instance d'incus :
Pour Nextcloud :
Pour le mettre à jour :
sudo -u www-data php /var/www/html/nextcloud/updater/updater.phar
Pour pouvoir le mettre à jour, il faut l'installer depuis l'interface graphique sur la page web, mais pour cela, il faut donner les droits et créer la base avec un user :
chown -R www-data:www-data /var/www/html/nextcloud/
chmod -R 750 /var/www/html/nextcloud/config/
chmod 640 /var/www/html/nextcloud/config/config.php
sudo mysql -u root -p
CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'Fablab';
GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Pour connaitre sa version :
sudo -u www-data php /var/www/html/nextcloud/occ status
ou
cat /var/www/html/nextcloud/version.php
Pour Zotero :
Pour le mettre à jour :
docker pull lscr.io/linuxserver/zotero:latest
docker stop zotero
docker rm zotero
docker run -d --name=zotero -e PUID=1000 -e PGID=1000 -e TZ=Etc/UTC -p 3000:3000 -p 3001:3001 -v /opt/zotero:/config --shm-size="1gb" --restart unless-stopped lscr.io/linuxserver/zotero:latest
Pour connaitre sa version :
docker inspect -f '{{ index .Config.Labels "build_version" }}' zotero
Pour Vaultwarden :
Pour le mettre à jour :
cd /opt/vaultwarden && docker compose pull && docker compose up -d
Pour connaitre sa version :
docker exec -it vaultwarden /vaultwarden --version
Pour Laravel et Redmine :
Nous ne mettons que a jour les paquets Apt pour éviter de casser les deux services :
apt update && apt -y full-upgrade && apt autoremove -y && apt clean
Pour Git-Forgejo :
Pour le mettre à jour :
cd /opt/forgejo && docker compose pull && docker compose up -d
Pour connaitre sa version :
docker exec -it forgejo forgejo --version
Admin :
ID : Fablab
MdP : FablabForgejo
Et nous avons donc mis à jour le script des snapshots et maj :
#!/bin/bash
# ============================================================
# incus-auto-update.sh
# Snapshot automatique + mise à jour de toutes les instances
# Planifier via crontab : 0 3 * * * /usr/local/bin/incus-auto-update.sh
# ============================================================
DATE=$(date +%Y-%m-%d_%H-%M)
LOG="/var/log/incus-updates.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG"
}
update_instance() {
local INSTANCE=$1
local CMD=$2
local SNAP_NAME="pre-update-${DATE}"
log "=== Traitement de $INSTANCE ==="
# 1. Snapshot avant mise à jour
log "Création du snapshot $SNAP_NAME..."
if incus snapshot create "$INSTANCE" "$SNAP_NAME"; then
log "Snapshot créé avec succès."
# 2. Mise à jour
log "Lancement de la mise à jour..."
if incus exec "$INSTANCE" -- bash -c "$CMD"; then
log "Mise à jour réussie pour $INSTANCE."
else
log "Échec de la mise à jour pour $INSTANCE."
log "Pour restaurer : incus restore $INSTANCE $SNAP_NAME"
fi
else
log "Échec de création du snapshot pour $INSTANCE. Mise à jour annulée."
fi
}
# ============================================================
# Définition des instances et de leurs commandes de mise à jour
# ============================================================
# VM laravel
#update_instance "laravel" "apt-get update && apt-get -y full-upgrade && apt-get autoremove -y && apt-get clean"
# VM Redmine
#update_instance "redmine" "apt-get update && apt-get -y full-upgrade && apt-get autoremove -y && apt-get clean"
# VM nextcloud
#update_instance "nextcloud" "apt-get update && apt-get -y full-upgrade && apt-get autoremove -y && apt-get clean && sudo -u www-data php /var/www/html/nextcloud/updater/updater.phar"
# Conteneur git-forgejo
#update_instance "git" "cd /opt/forgejo && docker compose pull && docker compose up -d"
# Conteneur vaultwarden
#update_instance "vaultwarden" "cd /opt/vaultwarden && docker compose pull && docker compose up -d"
# Conteneur zotero
#update_instance "zotero" 'docker pull lscr.io/linuxserver/zotero:latest && if [ "$(docker inspect --format "{{.Id}}" lscr.io/linuxserver/zotero:latest)" != "$(docker inspect --format "{{.Image}}" zotero)" ]; then docker stop zotero && docker rm zotero && docker run -d --name=zotero -e PUID=1000 -e PGID=1000 -e TZ=Etc/UTC -p 3000:3000 -p 3001:3001 -v /opt/zotero:/config --shm-size="1gb" --restart unless-stopped lscr.io/linuxserver/zotero:latest; else echo "Zotero est deja a jour"; fi'
log "=== Toutes les instances ont été traitées. ==="
Jour 20 (5 mai 2026) :¶
Nous avons recréé git-forgejo mais en conteneur (car c'est plus simple pour le maintenir à jour) :
sudo incus shell git
apt update && apt install -y docker.io docker-compose
mkdir -p /opt/forgejo && cd /opt/forgejo
vi docker-compose.yml
services:
server:
image: codeberg.org/forgejo/forgejo:15
container_name: forgejo
environment:
- USER_UID=1000
- USER_GID=1000
- FORGEJO__database__DB_TYPE=postgres
- FORGEJO__database__HOST=db:5432
- FORGEJO__database__NAME=forgejo
- FORGEJO__database__USER=forgejo
- FORGEJO__database__PASSWD=Zo0EiPha
restart: always
volumes:
- ./forgejo-data:/data
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "2222:2222"
depends_on:
- db
db:
image: postgres:14
restart: always
environment:
- POSTGRES_USER=forgejo
- POSTGRES_PASSWORD=Zo0EiPha
- POSTGRES_DB=forgejo
volumes:
- ./postgres-data:/var/lib/postgresql/data
docker compose up -d
docker compose stop server
exit
sudo incus file push forgejo-db.sql git/root/.
sudo incus push forgejo-repos.zip git/root/.
sudo incus push forgejo.zip git/root/.
sudo incus shell git
cp /root/forgejo-db.sql .
cat forgejo-db.sql | docker compose exec -T db psql -U forgejo -d forgejo
cd
unzip forgejo.zip
unzip forgejo-repos.zip
mv var/lib/forgejo/data/* /opt/forgejo/forgejo-data/
cd /opt/forgejo/
chown -R 1000:1000 forgejo-data
chown -R 1000:1000 forgejo-data/git/.ssh
docker compose start server
docker compose logs -f server
docker exec -it forgejo forgejo --version
En interface graphique :
Base de données :| Champ | Valeur |
| Type | PostgreSQL |
| Hôte | bd:5432 |
| Nom d'utilisateur | forgejo |
| Mot de passe | Zo0EiPha |
| Nom de base de données | forgejo |
| Champ | Valeur |
| Titre du site | Forgejo Fablab Cohabit |
| Emplacement racine des dépôts | /data/git/gitea-repositories |
| Répertoire racine Git LFS | /data/git/lfs |
| Exécuter avec le compte | forgejo |
| Port SSH | 2222 |
| Port HTTP | 3000 |
| Champ | Valeur |
| Nom d'utilisateur | Fablab |
| admin@mail.com | |
| Mot de passe | FablabForgejo |
Les maj pour passenger ne passé, car les clés ne sont plus signées donc nous avons fait cela :
curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key-2025.txt \
| gpg --dearmor \
| sudo tee /etc/apt/trusted.gpg.d/phusion.gpg > /dev/null
sudo apt-get update
Nous avons mis à jour le script pour les maj auto avec leurs pre-snapshots :
#!/bin/bash
# ============================================================
# incus-auto-update.sh
# Snapshot automatique + mise à jour de toutes les instances
# Planifier via crontab : 0 1 * * 6 /usr/local/bin/incus-auto-update.sh
# ============================================================
LOG="/var/log/incus-updates.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG"
}
# Nettoie une version pour être valide dans un nom de snapshot Incus
# (supprime tout caractère autre que lettres, chiffres, point, tiret, underscore)
sanitize_version() {
echo "$1" | sed 's/[^a-zA-Z0-9._-]/-/g'
}
# ============================================================
# Récupère la version d'une image Docker dans une instance
# Usage : get_docker_version <instance> <image_name>
# ============================================================
get_docker_version() {
local INSTANCE=$1
local IMAGE=$2
local VERSION
VERSION=$(incus exec "$INSTANCE" -- docker inspect --format '{{index .Config.Labels "org.opencontainers.image.version"}}' "$IMAGE" 2>/dev/null)
if [ -z "$VERSION" ]; then
VERSION=$(incus exec "$INSTANCE" -- docker inspect --format '{{.Config.Image}}' "$IMAGE" 2>/dev/null | awk -F: '{print $2}')
fi
if [ -z "$VERSION" ]; then
VERSION=$(incus exec "$INSTANCE" -- docker inspect --format '{{slice .Id 0 12}}' "$IMAGE" 2>/dev/null)
fi
sanitize_version "${VERSION:-unknown}"
}
# ============================================================
# Récupère la version de Forgejo via commande interne
# Usage : get_forgejo_version <instance>
# ============================================================
get_forgejo_version() {
local INSTANCE=$1
local VERSION
VERSION=$(incus exec "$INSTANCE" -- docker exec forgejo forgejo --version 2>/dev/null \
| grep -oP '(?<=version )\S+')
sanitize_version "${VERSION:-unknown}"
}
# ============================================================
# Récupère la version de Nextcloud via occ
# Usage : get_nextcloud_version <instance>
# ============================================================
get_nextcloud_version() {
local INSTANCE=$1
local VERSION
VERSION=$(incus exec "$INSTANCE" -- sudo -u www-data php /var/www/html/nextcloud/occ status 2>/dev/null \
| grep "version:" \
| awk '{print $3}')
sanitize_version "${VERSION:-unknown}"
}
# ============================================================
# Fonction principale de mise à jour
# Usage : update_instance <instance> <snap_name> <commande_update>
# ============================================================
update_instance() {
local INSTANCE=$1
local SNAP_NAME=$2
local CMD=$3
log "=== Traitement de $INSTANCE (snapshot : $SNAP_NAME) ==="
# 1. Snapshot avant mise à jour
log "Création du snapshot $SNAP_NAME..."
if incus snapshot create "$INSTANCE" "$SNAP_NAME"; then
log "Snapshot créé avec succès."
# 2. Mise à jour
log "Lancement de la mise à jour..."
if incus exec "$INSTANCE" -- bash -c "$CMD"; then
log "Mise à jour réussie pour $INSTANCE."
else
log "Échec de la mise à jour pour $INSTANCE."
log "Pour restaurer : incus restore $INSTANCE $SNAP_NAME"
fi
else
log "Échec de création du snapshot pour $INSTANCE. Mise à jour annulée."
fi
}
# ============================================================
# Définition des instances et de leurs commandes de mise à jour
# ============================================================
# VM laravel — version fixe
update_instance "laravel" "pre-update_laravel-9.52.10" \
"apt-get update && apt-get -y full-upgrade && apt-get autoremove -y && apt-get clean"
# VM redmine — version fixe
update_instance "redmine" "pre-update_redmine-5.1.12" \
"apt-get update && apt-get -y full-upgrade && apt-get autoremove -y && apt-get clean"
# VM nextcloud — version récupérée via occ
VERSION=$(get_nextcloud_version "nextcloud")
update_instance "nextcloud" "pre-update_nextcloud-${VERSION}" \
"apt-get update && apt-get -y full-upgrade && apt-get autoremove -y && apt-get clean && sudo -u www-data php /var/www/html/nextcloud/updater/updater.phar"
# Conteneur git-forgejo — version via commande interne
VERSION=$(get_forgejo_version "git")
update_instance "git" "pre-update_forgejo-${VERSION}" \
"cd /opt/forgejo && docker compose pull && docker compose up -d"
# Conteneur vaultwarden — version de l'image Docker
VERSION=$(get_docker_version "vaultwarden" "vaultwarden/server")
update_instance "vaultwarden" "pre-update_vaultwarden-${VERSION}" \
"cd /opt/vaultwarden && docker compose pull && docker compose up -d"
# Conteneur zotero — version de l'image Docker
VERSION=$(get_docker_version "zotero" "lscr.io/linuxserver/zotero")
update_instance "zotero" "pre-update_zotero-${VERSION}" \
'docker pull lscr.io/linuxserver/zotero:latest && if [ "$(docker inspect --format "{{.Id}}" lscr.io/linuxserver/zotero:latest)" != "$(docker inspect --format "{{.Image}}" zotero)" ]; then docker stop zotero && docker rm zotero && docker run -d --name=zotero -e PUID=1000 -e PGID=1000 -e TZ=Etc/UTC -p 3000:3000 -p 3001:3001 -v /opt/zotero:/config --shm-size="1gb" --restart unless-stopped lscr.io/linuxserver/zotero:latest; else echo "Zotero est deja a jour"; fi'
log "=== Toutes les instances ont été traitées. ==="
Nous avons aussi créé un script pour les snapshots hebdomadaire :
#!/bin/bash
# /usr/local/bin/incus-snapshot.sh
# Crée un snapshot horodaté sur toutes les VM/CT Incus
# Planifier via crontab : 0 0 * * 6 /usr/local/bin/incus-snapshot.sh
INSTANCES=(git laravel nextcloud redmine vaultwarden zotero)
DATE=$(date +"%Y%m%d-%H%M%S")
LOG="/var/log/incus-snapshot.log"
echo "[$(date)] === Début des snapshots ===" >> "$LOG"
for INSTANCE in "${INSTANCES[@]}"; do
SNAPSHOT_NAME="hebdo-${DATE}"
if incus snapshot create "$INSTANCE" "$SNAPSHOT_NAME --expiry=3w" >> "$LOG" 2>&1; then
echo "[$(date)] $INSTANCE -> $SNAPSHOT_NAME" >> "$LOG"
else
echo "[$(date)] ERREUR sur $INSTANCE" >> "$LOG"
fi
done
echo "[$(date)] === Fin des snapshots ===" >> "$LOG"
Commande crontab :
crontab -e
0 0 * * 6 /usr/local/bin/incus-snapshot.sh
0 1 * * 6 /usr/local/bin/incus-auto-update.sh
Jour 21 (6 mai 2026) :¶
Pour mettre facilement a jour le conteneur zotero, nous avons créé un fichier docker-compose.yml (comme avec vaultwarden et git-forgejo) :
services:
zotero:
image: lscr.io/linuxserver/zotero:latest
container_name: zotero
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
volumes:
- /opt/zotero:/config
ports:
- 3000:3000
- 3001:3001
shm_size: "1gb"
restart: unless-stopped
Puis, nous avons adapté le script des updates pour retirer les snapshots car avec le script des snapshots hebdomadaire, nous avons déjà des snapshots avant les maj parce que le script des snapshots hebdomadaire sera lancé avant le script des majs (avec crontab) :
#!/bin/bash
# ============================================================
# incus-update.sh
# Mise à jour automatique des instances Incus
# Planifier via crontab : 0 1 * * 6 /usr/local/bin/incus-update.sh
# ============================================================
LOG="/var/log/incus-updates.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG"
}
# ============================================================
# Mise à jour d'une instance Docker Compose
# Logique : pull → comparer image locale vs image du conteneur actif
# si différent → up -d
# ============================================================
update_docker_instance() {
local INSTANCE=$1
local IMAGE=$2
local COMPOSE_DIR=$3
local CONTAINER_NAME=$4
log "=== Traitement de $INSTANCE ==="
log "Vérification des mises à jour disponibles..."
incus exec "$INSTANCE" -- bash -c "cd $COMPOSE_DIR && docker compose pull" >> "$LOG" 2>&1
local IMAGE_DIGEST
IMAGE_DIGEST=$(incus exec "$INSTANCE" -- docker inspect --format '{{.Id}}' "$IMAGE" 2>/dev/null)
local CONTAINER_DIGEST
CONTAINER_DIGEST=$(incus exec "$INSTANCE" -- docker inspect --format '{{.Image}}' "$CONTAINER_NAME" 2>/dev/null)
if [ "$IMAGE_DIGEST" = "$CONTAINER_DIGEST" ]; then
log "Aucune mise à jour disponible pour $INSTANCE. Rien à faire."
return 0
fi
log "Nouvelle version détectée et installée ! Redémarrage de $INSTANCE..."
if incus exec "$INSTANCE" -- bash -c "cd $COMPOSE_DIR && docker compose up -d --remove-orphans"; then
log "Mise à jour réussie pour $INSTANCE."
else
log "Échec de la mise à jour pour $INSTANCE."
fi
}
# ============================================================
# Mise à jour d'une instance système (APT)
# ============================================================
update_apt_instance() {
local INSTANCE=$1
log "=== Traitement de $INSTANCE ==="
local UPGRADABLE
UPGRADABLE=$(incus exec "$INSTANCE" -- bash -c "apt-get update -qq && apt-get -s upgrade 2>/dev/null | grep -c '^Inst'" 2>/dev/null)
if [ "$UPGRADABLE" -eq 0 ] 2>/dev/null; then
log "Aucune mise à jour disponible pour $INSTANCE. Rien à faire."
return 0
fi
log "$UPGRADABLE paquet(s) à mettre à jour pour $INSTANCE."
if incus exec "$INSTANCE" -- bash -c "apt-get -y full-upgrade && apt-get autoremove -y && apt-get clean"; then
log "Mise à jour réussie pour $INSTANCE."
else
log "Échec de la mise à jour pour $INSTANCE."
fi
}
# ============================================================
# Définition des instances
# ============================================================
# VM laravel
update_apt_instance "laravel"
# VM redmine
update_apt_instance "redmine"
# VM nextcloud (APT + Nextcloud)
log "=== Traitement de nextcloud ==="
UPGRADABLE=$(incus exec "nextcloud" -- bash -c "apt-get update -qq && apt-get -s upgrade 2>/dev/null | grep -c '^Inst'" 2>/dev/null)
if [ "$UPGRADABLE" -eq 0 ] 2>/dev/null; then
log "Aucune mise à jour disponible pour nextcloud. Rien à faire."
else
log "$UPGRADABLE paquet(s) à mettre à jour pour nextcloud."
if incus exec "nextcloud" -- bash -c "apt-get -y full-upgrade && apt-get autoremove -y && apt-get clean && sudo -u www-data php /var/www/html/nextcloud/updater/updater.phar"; then
log "Mise à jour réussie pour nextcloud."
else
log "Échec de la mise à jour pour nextcloud."
fi
fi
# Conteneur git-forgejo
update_docker_instance "git" "codeberg.org/forgejo/forgejo:15" "/opt/forgejo" "forgejo"
# Conteneur vaultwarden
update_docker_instance "vaultwarden" "vaultwarden/server" "/opt/vaultwarden" "vaultwarden"
# Conteneur zotero
update_docker_instance "zotero" "lscr.io/linuxserver/zotero:latest" "/opt/zotero" "zotero"
log "=== Toutes les instances ont été traitées. ==="
Jour 22 (7 mai 2026) :¶
Pour pouvoir envoyer un mail des résultats des majs des instances incus, il nous faut postfix et nous avons récupéré la config du serveur cohabit donc dans /etc/postfix/ mais le problème, c'est que le pare-feu du réseau bloque :
sudo systemctl status postfix --no-pager >> log.log cat log.log ... mai 07 10:18:34 toutatis postfix/smtp[736103]: connect to mta-in02.u-bordeaux.fr[147.210.215.18]:25: Connection timed out
Nous avons modifié le script des updates pour faire l'envoie du mail :
#!/bin/bash
# ============================================================
# incus-update.sh
# Mise à jour automatique des instances Incus
# Planifier via crontab : 0 1 * * 6 /usr/local/bin/incus-update.sh
# ============================================================
LOG="/var/log/incus-updates.log"
SESSION_LOG=$(mktemp /tmp/incus-update-session.XXXXXX)
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG" | tee -a "$SESSION_LOG" > /dev/null
}
# ============================================================
# Mise à jour d'une instance Docker Compose
# Logique : pull → comparer image locale vs image du conteneur actif
# si différent → up -d
# ============================================================
update_docker_instance() {
local INSTANCE=$1
local IMAGE=$2
local COMPOSE_DIR=$3
local CONTAINER_NAME=$4
log "=== Traitement de $INSTANCE ==="
log "Vérification des mises à jour disponibles..."
incus exec "$INSTANCE" -- bash -c "cd $COMPOSE_DIR && docker compose pull" >> "$LOG" 2>&1
local IMAGE_DIGEST
IMAGE_DIGEST=$(incus exec "$INSTANCE" -- docker inspect --format '{{.Id}}' "$IMAGE" 2>/dev/null)
local CONTAINER_DIGEST
CONTAINER_DIGEST=$(incus exec "$INSTANCE" -- docker inspect --format '{{.Image}}' "$CONTAINER_NAME" 2>/dev/null)
if [ "$IMAGE_DIGEST" = "$CONTAINER_DIGEST" ]; then
log "Aucune mise à jour disponible pour $INSTANCE. Rien à faire."
return 0
fi
log "Nouvelle version détectée et installée ! Redémarrage de $INSTANCE..."
if incus exec "$INSTANCE" -- bash -c "cd $COMPOSE_DIR && docker compose up -d --remove-orphans"; then
log "Mise à jour réussie pour $INSTANCE."
else
log "Échec de la mise à jour pour $INSTANCE."
fi
}
# ============================================================
# Mise à jour d'une instance système (APT)
# ============================================================
update_apt_instance() {
local INSTANCE=$1
log "=== Traitement de $INSTANCE ==="
local UPGRADABLE
UPGRADABLE=$(incus exec "$INSTANCE" -- bash -c "apt-get update -qq && apt-get -s upgrade 2>/dev/null | grep -c '^Inst'" 2>/dev/null)
if [ "$UPGRADABLE" -eq 0 ] 2>/dev/null; then
log "Aucune mise à jour disponible pour $INSTANCE. Rien à faire."
return 0
fi
log "$UPGRADABLE paquet(s) à mettre à jour pour $INSTANCE."
if incus exec "$INSTANCE" -- bash -c "apt-get -y full-upgrade && apt-get autoremove -y && apt-get clean"; then
log "Mise à jour réussie pour $INSTANCE."
else
log "Échec de la mise à jour pour $INSTANCE."
fi
}
# ============================================================
# Définition des instances
# ============================================================
# VM laravel
update_apt_instance "laravel"
# VM redmine
update_apt_instance "redmine"
# VM nextcloud (APT + Nextcloud)
log "=== Traitement de nextcloud ==="
UPGRADABLE=$(incus exec "nextcloud" -- bash -c "apt-get update -qq && apt-get -s upgrade 2>/dev/null | grep -c '^Inst'" 2>/dev/null)
if [ "$UPGRADABLE" -eq 0 ] 2>/dev/null; then
log "Aucune mise à jour disponible pour nextcloud. Rien à faire."
else
log "$UPGRADABLE paquet(s) à mettre à jour pour nextcloud."
if incus exec "nextcloud" -- bash -c "apt-get -y full-upgrade && apt-get autoremove -y && apt-get clean && sudo -u www-data php /var/www/html/nextcloud/updater/updater.phar"; then
log "Mise à jour réussie pour nextcloud."
else
log "Échec de la mise à jour pour nextcloud."
fi
fi
# Conteneur git-forgejo
update_docker_instance "git" "codeberg.org/forgejo/forgejo:15" "/opt/forgejo" "forgejo"
# Conteneur vaultwarden
update_docker_instance "vaultwarden" "vaultwarden/server" "/opt/vaultwarden" "vaultwarden"
# Conteneur zotero
update_docker_instance "zotero" "lscr.io/linuxserver/zotero:latest" "/opt/zotero" "zotero"
log "=== Toutes les instances ont été traitées. ==="
# Envoi du rapport de la session par mail
MAIL_DEST="pierre.grange-praderas@u-bordeaux.fr"
mail -s "[toutatis] Rapport mise à jour $(date '+%Y-%m-%d')" "$MAIL_DEST" < "$SESSION_LOG"
# Nettoyage du fichier temporaire
rm -f "$SESSION_LOG"
Nous avons aussi modifié le script des snapshots, car le --expiry n'existe pas sur les versions 6.0.x LTS de incus et nous avons aussi modifié le nom des snapshots pour plus de clarté (de hebdo-20260506-115242 à hebdo-2026-05-06_11:52:42) :
#!/bin/bash
# /usr/local/bin/incus-snapshot.sh
# Crée un snapshot horodaté sur toutes les VM/CT Incus
# Planifier via crontab : 0 0 * * 6 /usr/local/bin/incus-snapshot.sh
INSTANCES=(git laravel nextcloud redmine vaultwarden zotero)
DATE=$(date +"%d-%m-%Y_%H:%M:%S")
LOG="/var/log/incus-snapshot.log"
echo "[$(date)] === Début des snapshots ===" >> "$LOG"
for INSTANCE in "${INSTANCES[@]}"; do
SNAPSHOT_NAME="hebdo-${DATE}"
if echo "expires_at: $(date -u -d '+2 weeks' '+%Y-%m-%dT%H:%M:%SZ')" \
| incus snapshot create "$INSTANCE" "$SNAPSHOT_NAME" >> "$LOG" 2>&1; then
echo "[$(date)] $INSTANCE -> $SNAPSHOT_NAME" >> "$LOG"
else
echo "[$(date)] ERREUR sur $INSTANCE" >> "$LOG"
fi
done
echo "[$(date)] === Fin des snapshots ===" >> "$LOG"
Commande crontab :
crontab -e
0 0 * * 6 /usr/local/bin/incus-snapshot.sh
0 1 * * 6 /usr/local/bin/incus-update.sh
Jour 23 (11 mai 2026) :¶
Ajout de deux nouveaux services (twiki et dokuwiki) :
Pour TWiki :
sudo incus launch images:debian/13 twiki
sudo incus config device override twiki eth0 ipv4.address=10.218.184.9
sudo incus restart twiki
sudo incus shell twiki
apt update && apt upgrade
apt install apache2 rcs wget make gcc libcgi-session-perl libdigest-sha-perl libhtml-parser-perl liberror-perl php wget zip
systemctl restart apache2
wget -O TWiki-6.1.0.zip "https://sourceforge.net/projects/twiki/files/TWiki%20for%20all%20Platforms/TWiki-6.1.0/TWiki-6.1.0.zip/download"
unzip TWiki-6.1.0.zip
mv TWiki-6.1.0 /var/www/html/
Pour la configuration d'apache, TWiki ont un générateur de template : https://twiki.org/cgi-bin/view/TWiki/ApacheConfigGenerator?dir=%2Fvar%2Fwww%2Fhtml%2FTWiki-6.1.0&path=%2Fdo&puburl=%2Fpub&loginmanager=Template&phpinstalled=PHP4&secureattachments=no&errordocument=TWikiRegistration&execperl=mod_cgi&apachever24=on#TWikiConf
Mais pour que la page nous redirige automatiquement sur la page d'accueil du site, il faut ajouter cela :
RedirectMatch ^/$ /do/view/Main/WebHome
vi /etc/apache2/sites-enabled/twiki.conf
rm /etc/apache2/sites-enabled/000-default.conf
chown -R www-data:www-data /var/www/html/TWiki-6.1.0/
systemctl restart apache2
exit
Finaliser la configuration sur la page web
https://twiki.192.168.23.3.nip.io/do/configure
sudo vi /etc/caddy/Caddyfile
twiki.192.168.23.3.nip.io {
tls internal
reverse_proxy 10.218.184.9:80
}
TWiki est accessible sur : https://twiki.192.168.23.3.nip.io/
Pour DokuWiki :
sudo incus init images:debian/13 dokuwiki --vm
sudo incus config device override dokuwiki eth0 ipv4.address=10.218.184.8
sudo incus start dokuwiki
sudo incus shell dokuwiki
sudo apt update && sudo apt upgrade -y
sudo apt install apache2 php wget -y
sudo systemctl enable apache2
wget https://download.dokuwiki.org/out/dokuwiki-30e181c1af123e688bf57014595f64a4.tgz
tar xzvf dokuwiki-stable.tgz
sudo mkdir -p /var/www/html/dokuwiki
sudo mv dokuwiki-*/* /var/www/html/dokuwiki/
sudo chown -R www-data:www-data /var/www/html/dokuwiki
sudo chmod -R 755 /var/www/html/dokuwiki
sudo chmod -R 775 /var/www/html/dokuwiki/data
sudo chmod -R 775 /var/www/html/dokuwiki/conf
sudo vi /etc/apache2/sites-available/dokuwiki.conf
sudo rm /etc/apache2/sites-available/000-default.conf
<VirtualHost *:80>
DocumentRoot /var/www/html/dokuwiki
<Directory /var/www/html/dokuwiki>
Options FollowSymLinks
AllowOverride All
Require all granted
SetEnv HOME /var/www/html/dokuwiki
SetEnv HTTP_HOME /var/www/html/dokuwiki
</Directory>
ErrorLog ${APACHE_LOG_DIR}/dokuwiki-error.log
CustomLog ${APACHE_LOG_DIR}/dokuwiki-access.log combined
</VirtualHost>
sudo systemctl restart apache2
exit
Finaliser l'installation sur la page web
https://dokuwiki.192.168.23.3.nip.io/install.php
sudo vi /etc/caddy/Caddyfile
dokuwiki.192.168.23.3.nip.io {
tls internal
reverse_proxy 10.218.184.8:80
}
DokuWiki est accessible sur : https://dokuwiki.192.168.23.3.nip.io/
Nous avons aussi mis à jour les scripts incus-update.sh et incus-snapshot.sh pour ajouter twiki et dokuwiki :
vi /usr/local/bin/incus-update.sh
...
# VM twiki
update_apt_instance "twiki"
# VM dokuwiki
update_apt_instance "dokuwiki"
...
vi /usr/local/bin/incus-snapshot.sh
...
INSTANCES=(git laravel nextcloud redmine vaultwarden zotero twiki dokuwiki)
...
Jour 24 (12 mai 2026) :¶
Création d'une page web de test dans TWiki avec mkdir /var/www/html/TWiki-6.1.0/data/Test puis création d’un topic avec vi data/Test/test.txt et application des droits avec chown www-data:www-data data/Test/test.txt.
Création correcte du web Test en s’appuyant sur le web modèle _default.
cp -r /var/www/html/TWiki-6.1.0/data/_default /var/www/html/TWiki-6.1.0/data/Test
chown -R www-data:www-data /var/www/html/TWiki-6.1.0/data/Test
vi /var/www/html/TWiki-6.1.0/data/Test/PageTest.txt
chown www-data:www-data /var/www/html/TWiki-6.1.0/data/Test/PageTest.txt
Test de contenu TWiki dans PageTest.txt : titres, listes, tableaux, liens, bloc verbatim, variables TOC, WEB, TOPIC, WIKINAME, SCRIPTURL.
Correction de l’encodage en UTF-8 :
vi /var/www/html/TWiki-6.1.0/lib/LocalSite.cfg
réglage de {Site}{CharSet} sur utf-8
réglage de {Site}{Locale} sur fr_FR.UTF-8
Et aussi dans apache ajoute de AddDefaultCharset UTF-8 :
vi /etc/apache2/conf-available/charset.conf
systemctl restart apache2
Page disponible ici : https://twiki.192.168.23.3.nip.io/do/view/Test/PageTest
Et pour DokuWiki : https://dokuwiki.192.168.23.3.nip.io/doku.php
Jour 25 (13 mai 2026) :¶
On a envoyé le mail à la DSI pour débloquer le port pour le VPN WireGuard.
Sur le serveur cohabit, nous avons trouvé postfix, DMARC et DKIM :
/etc/postfix/* /etc/opendkim.conf /etc/default/opendkim /etc/opendkim/* /var/spool/postfix/opendkim/ /etc/opendmarc.conf /etc/default/opendmarc /etc/opendmarc/* /var/spool/postfix/opendmarc/
Et nous avons regardé si caddy ne géré pas déjà cela, et nous avons trouvé maddy :
https://maddy.email/
https://github.com/foxcpp/maddy/
https://maddy.email/builds/
Jour 27 (18 mai 2026) :¶
On a envoyé un message concernant les mails :
On a vu avec Pierre et on n'a trouvé que du Postfix avec DMARC et DKIM sur le serveur cohabit. Il nous a proposé de reprendre cette configuration, soit de voir avec Caddy. En cherchant, on a découvert Maddy (un serveur mail créé par la même équipe que Caddy). Qu'est-ce que vous préférez qu'on mette en place ?
On a essayé de faire fonctionner maddy sur docker, mais sans succès.
On a commencé la config avec cette vidéo tuto :
https://www.youtube.com/watch?v=uU0uXNHrPs4
Jour 28 (19 mai 2026) :¶
Création d'un fichier texte pour migrer les données pour le déplacement du serveur le vendredi 22
================= GIT =================
serveur cohabit :
sudo -u postgres pg_dump gitea > /tmp/forgejo-db.sql
sudo zip -r ~/forgejo-repos.zip /var/lib/forgejo/data
serveur toutatis :
scp -P 55555 yoan@projets.cohabit.fr:/tmp/forgejo-db.sql ~/
scp -P 55555 yoan@projets.cohabit.fr:~/forgejo-repos.zip ~/
sudo incus file push ~/forgejo-db.sql git/tmp/forgejo-db.sql
sudo incus file push ~/forgejo-repos.zip git/root/forgejo-repos.zip
sudo incus exec git -- bash
vm git :
cp /root/forgejo-db.sql .
cat forgejo-db.sql | docker compose exec -T db psql -U forgejo -d forgejo
cd
unzip forgejo.zip
unzip forgejo-repos.zip
mv var/lib/forgejo/data/* /opt/forgejo/forgejo-data/
cd /opt/forgejo/
chown -R 1000:1000 forgejo-data
chown -R 1000:1000 forgejo-data/git/.ssh
docker compose start server
docker compose logs -f server
docker exec -it forgejo forgejo --version
================= REDMINE =================
serveur cohabit :
sudo tar -czvf ~/migration-opt-redmine.tar.gz /opt/redmine
echo '127.0.0.1:5432:laravel:laravel:p5U\!aUe8!' > ~/.pgpass
chmod 600 ~/.pgpass
pg_dump -U redmine -h 127.0.0.1 -p 5432 -Fc --file=/tmp/redmine_db.dump redmine
serveur toutatis :
scp -P 55555 nicolas@projets.cohabit.fr:~/migration-opt-redmine.tar.gz ~/
scp -P 55555 nicolas@projets.cohabit.fr:/tmp/redmine_db.dump ~/
sudo incus shell redmine
vm redmine :
cp /root/redmine_db.dump /tmp/redmine_db.dump
su - postgres
dropdb redmine
createdb redmine -O redmine
pg_restore --no-owner -x -d redmine /tmp/redmine_db.dump
exit
su - postgres
psql -d redmine
sql
GRANT ALL ON SCHEMA public TO redmine;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO redmine;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO redmine;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO redmine;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO redmine;
ALTER DATABASE redmine OWNER TO redmine;
\q
exit
tar -xzvf migration-opt-redmine.tar.gz -C /
chown -R www-data:www-data /opt/redmine/redmine-5.0.5
chmod -R 755 /opt/redmine/redmine-5.0.5
chmod -R 777 /opt/redmine/redmine-5.0.5/tmp \
/opt/redmine/redmine-5.0.5/log \
/opt/redmine/redmine-5.0.5/files \
/opt/redmine/redmine-5.0.5/public/plugin_assets
================= LARAVEL =================
serveur cohabit :
sudo -u postgres pg_dump laravel > /tmp/sauvegarde_laravel.sql
tar -czvf ~/migration-complete.tar.gz \
/etc/apache2 \
/var/www/html
sur le serveur toutatis :
scp -P 55555 nicolas@projet.cohabit.fr:/tmp/sauvegarde_laravel.sql ~/.
scp -P 55555 nicolas@projets.cohabit.fr:~/migration-complete.tar.gz ~/
scp -P 55555 nicolas@projets.cohabit.fr:/opt/laravel/laravel ~/
sudo incus file push ~/sauvegarde_laravel.sql laravel-web/
sudo incus file push -r ~/migration-complete.tar.gz laravel-web/
sudo incus file push -r ~/laravel laravel-web/
sudo incus shell laravel
vm laravel :
sudo -u postgres psql laravel -f /sauvegarde_laravel.sql
tar -xzvf /migration-complete.tar.gz -C /temp-web
cp -r /temp-web/etc /.
cp -r /temp-web/var /.
cp -r /laravel /opt/laravel/.
chown -R www-data:www-data /var/www/html
chown -R www-data:www-data /opt/laravel/laravel
chmod -R 775 /opt/laravel/laravel/storage
chmod -R 775 /opt/laravel/laravel/bootstrap/cache
RDV prévu vendredi 22 mai pour mettre en place le nouveau serveur à la place de l'ancien.
Mise en place de Maddy sur le serveur toutatis (pas encore fini) :
wget -O maddy-0.9.4-x86_64-linux-musl.tar.zst https://github.com/foxcpp/maddy/releases/download/v0.9.4/maddy-0.9.4-x86_64-linux-musl.tar.zst
unzstd maddy-0.9.4-x86_64-linux-musl.tar.zst
tar xvf maddy-0.9.4-x86_64-linux-musl.tar
ls maddy-0.9.4-x86_64-linux-musl
sudo mkdir /etc/maddy
sudo cp maddy-0.9.4-x86_64-linux-musl/maddy.conf /etc/maddy
sudo cp maddy-0.9.4-x86_64-linux-musl/maddy /usr/bin
which maddy
sudo vi /etc/maddy/maddy.conf
... $(hostname) = mail.192.168.23.3.nip.io $(primary_domain) = 192.168.23.3.nip.io $(local_domains) = $(primary_domain) cohabit.fr tls file /var/lib/caddy/.local/share/caddy/certificates/local/mail.192.168.23.3.nip.io/mail.192.168.23.3.nip.io.crt /var/lib/caddy/.local/share/caddy/certificates/local/mail.192.168.23.3.nip.io/mail.192.168.23.3.nip.io.key ...
sudo vi /etc/caddy/Caddyfile
...
mail.192.168.23.3.nip.io {
tls internal
respond "Serveur Mail Cohabit" 200
}
...
sudo systemctl restart caddy
sudo apt install acl
sudo useradd -mrU -s /usr/sbin/nologin -c "maddy mail server" maddy
id maddy
sudo setfacl -R -m u:maddy:rX /var/lib/caddy/.local/share/caddy/certificates/local/mail.192.168.23.3.nip.io/mail.192.168.23.3.nip.io.crt /var/lib/caddy/.local/share/caddy/certificates/local/mail.192.168.23.3.nip.io/mail.192.168.23.3.nip.io.key
sudo getfacl /var/lib/caddy/.local/share/caddy/certificates/local/mail.192.168.23.3.nip.io/mail.192.168.23.3.nip.io.key
sudo chmod +x /usr/bin/maddy
sudo cp systemd/*.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo ls /var/lib/caddy/.local/share/caddy/certificates/local/mail.192.168.23.3.nip.io/
mail.192.168.23.3.nip.io.crt mail.192.168.23.3.nip.io.json mail.192.168.23.3.nip.io.key
sudo setfacl -m u:maddy:x /var/lib/caddy
sudo setfacl -m u:maddy:x /var/lib/caddy/.local
sudo setfacl -m u:maddy:x /var/lib/caddy/.local/share
sudo setfacl -m u:maddy:x /var/lib/caddy/.local/share/caddy
sudo setfacl -m u:maddy:x /var/lib/caddy/.local/share/caddy/certificates
sudo setfacl -m u:maddy:x /var/lib/caddy/.local/share/caddy/certificates/local
sudo setfacl -R -m u:maddy:rX /var/lib/caddy/.local/share/caddy/certificates/local/mail.192.168.23.3.nip.io/
sudo setfacl -d -R -m u:maddy:rX /var/lib/caddy/.local/share/caddy/certificates/local/mail.192.168.23.3.nip.io/
sudo systemctl edit maddy
... [Service] ReadOnlyPaths=/var/lib/caddy/.local/share/caddy/certificates/ ...
sudo systemctl stop postfix
sudo systemctl enable maddy
sudo systemctl start maddy
sudo systemctl status maddy
sudo maddy creds create projets@cohabit.fr
Jour 29 (20 mai 2026) :¶
Nous avons demandé à pierre ceci :
Bonjour, Sur le serveur Toutatis, est-ce qu'on met le SSH sur le port 55555 comme sur le serveur Cohabit ? Est-ce que nous devons désactiver l’accès SSH par mot de passe ? Parce que si nous faisons cela, vous devrez d'abord mettre votre clé publique sur le serveur. Et aussi pour vendredi, est-ce que c'est vous qui prenez le serveur pour l'emmener à Aquilenet ?
et il nous a répondu :
Oui port 55555 Pour le ssh, MDP interdit, et connexion en root interdite, recopiez mes clés depuis le serveur actuel Oui c'est moi qui amène le serveur vendredi
Donc, nous avons copié tous les fichiers du dossier /home/pgp/.ssh/ et aussi de /home/alexander/.ssh/ pour les mettre sur le nouveau serveur.
Pour la config SSH, nous avons juste recopié la config de l'ancien serveur /etc/ssh/sshd_config vers le nouveau serveur.
Pour l'envoi de mail avec Maddy :
La commande mail que l'on utilisait avec postfix, ne fonctionnait pas pour maddy donc nous avons utilisé swaks :
sudo apt install swaks
swaks --to "yoangabriel33@gmail.com" \
--from "projets@cohabit.fr" \
--server 127.0.0.1 --port 587 --tls \
--auth-user "projets@cohabit.fr" \
--auth-password "Fablab"
et dans les logs, on peut voir comme avec postfix que le réseau bloque sur le port 25 donc normalement sur autre réseau ou le port 25 n'est pas bloqué, ça devrait marcher :
sudo journalctl -u maddy -f
mai 20 12:14:26 toutatis maddy[3595504]: submission: incoming message {"msg_id":"92679cce","sender":"projets@cohabit.fr","src_host":"toutatis","src_ip":"127.0.0.1:34070","username":"projets@cohabit.fr"}
mai 20 12:14:26 toutatis maddy[3595504]: submission: RCPT ok {"msg_id":"92679cce","rcpt":"yoangabriel33@gmail.com"}
mai 20 12:14:26 toutatis maddy[3595504]: submission: accepted {"msg_id":"92679cce"}
mai 20 12:16:43 toutatis maddy[3595504]: cannot use MX {"domain":"gmail.com","io_op":"dial","msg_id":"92679cce-6a0d8982","reason":"dial tcp 64.233.166.27:25: connect: connection timed out","remote_addr":"64.233.166.27:25","remote_server":"gmail-smtp-in.l.google.com.","smtp_code":450,"smtp_enchcode":"4.4.2","smtp_msg":"Network I/O error"}
Nous avons donc adapté le script incus-update.sh pour l'envoi du mail :
...
# Envoi du rapport de la session par mail avec swaks
MAIL_DEST="pierre.grange-praderas@u-bordeaux.fr"
SUBJECT="[toutatis] Rapport mise à jour $(date '+%Y-%m-%d')"
cat "$SESSION_LOG" | swaks --to "$MAIL_DEST" \
--from "projets@cohabit.fr" \
--server 127.0.0.1 --port 587 --tls \
--auth-user "projets@cohabit.fr" \
--auth-password "Fablab" \
--header "Subject: $SUBJECT" \
--body - > /dev/null 2>&1
# Nettoyage du fichier temporaire
rm -f "$SESSION_LOG"
Jour 30 (21 mai 2026) :¶
Jour 31 (22 mai 2026) :¶
Jour 32 (26 mai 2026) :¶
Jour 33 (27 mai 2026) :¶
Jour 34 (28 mai 2026) :¶
Jour 35 (29 mai 2026) :¶
Jour 36 (1 juin 2026) :¶
Jour 37 (2 juin 2026) :¶
Jour 38 (3 juin 2026) :¶
Jour 39 (4 juin 2026) :¶
Jour 40 (5 juin 2026) :¶