Proxmox VE : Personnaliser Alpine Linux avec Cloud-Init

Dans l’article précédent « Proxmox VE : Créer un template Alpine Linux avec Cloud-Init », nous avons vu comment créer un template VM réutilisable. Une question revient souvent : comment ajouter des packages ou des configurations personnalisées tout en conservant les paramètres définis dans l’interface Proxmox ?

La réponse se trouve dans l’utilisation de vendor-data plutôt que user-data.

Le problème : cicustom user écrase tout

Beaucoup de tutoriels en ligne suggèrent d’utiliser --cicustom "user=local:snippets/config.yaml" pour ajouter des personnalisations Cloud-Init. Cette approche pose un problème majeur : elle remplace entièrement la configuration utilisateur générée par Proxmox.

Concrètement, si vous définissez :

qm set $VMID --ciuser monuser
qm set $VMID --cipassword $(openssl passwd -6 'monsecret')
qm set $VMID --sshkeys ~/.ssh/id_ed25519.pub
qm set $VMID --cicustom "user=local:snippets/config.yaml"

Les paramètres ciuser, cipassword et sshkeys seront ignorés. Seul le contenu de votre fichier YAML sera appliqué.

La solution : utiliser vendor-data

Proxmox ne génère pas de vendor-data par défaut. L’utilisation de --cicustom "vendor=..." permet donc d’ajouter des configurations personnalisées sans écraser celles de l’interface.

qm set $VMID --cicustom "vendor=local:snippets/vendor-data.yaml"

Cloud-Init fusionne automatiquement les configurations user-data (générée par Proxmox) et vendor-data (votre fichier personnalisé).

Préparer le stockage Snippets

Vérifiez que les snippets sont activés sur votre stockage :

pvesm status
pvesm set local --content images,rootdir,vztmpl,backup,iso,snippets

Les fichiers se placent dans /var/lib/vz/snippets/.

Exemples de configurations par distribution

Alpine Linux (OpenRC)

Alpine utilise OpenRC comme système d’init. Les commandes systemctl des tutoriels classiques ne fonctionnent pas.

Créez le fichier /var/lib/vz/snippets/alpine-vendor.yaml :

#cloud-config
package_update: true
package_upgrade: true

packages:
  - qemu-guest-agent

runcmd:
  - rc-update add qemu-guest-agent default
  - rc-service qemu-guest-agent start

Version avancée avec SSH post-quantique (Alpine 3.21+ avec OpenSSH 9.9) :

#cloud-config
package_update: true
package_upgrade: true

packages:
  - qemu-guest-agent

write_files:
  - path: /etc/ssh/sshd_config.d/kex-algorithms.conf
    content: |
      KexAlgorithms mlkem768x25519-sha256,curve25519-sha256
    permissions: "0644"
    owner: root:root

runcmd:
  - rc-update add qemu-guest-agent default
  - rc-service qemu-guest-agent start
  - rc-service sshd restart

Appliquez la configuration :

qm set $VMID --cicustom "vendor=local:snippets/alpine-vendor.yaml"

Debian / Ubuntu (systemd)

Créez le fichier /var/lib/vz/snippets/debian-vendor.yaml :

#cloud-config
package_update: true
package_upgrade: true

packages:
  - qemu-guest-agent

runcmd:
  - systemctl enable qemu-guest-agent
  - systemctl start qemu-guest-agent

Version avancée avec configuration SSH :

#cloud-config
package_update: true
package_upgrade: true

packages:
  - qemu-guest-agent

write_files:
  - path: /etc/ssh/sshd_config.d/99-custom.conf
    content: |
      PermitRootLogin prohibit-password
      PasswordAuthentication no
      PubkeyAuthentication yes
    permissions: "0644"
    owner: root:root

runcmd:
  - systemctl enable qemu-guest-agent
  - systemctl start qemu-guest-agent
  - systemctl restart sshd

Rocky Linux / AlmaLinux / RHEL (systemd + dnf)

Créez le fichier /var/lib/vz/snippets/rhel-vendor.yaml :

#cloud-config
package_update: true
package_upgrade: true

packages:
  - qemu-guest-agent

runcmd:
  - systemctl enable qemu-guest-agent
  - systemctl start qemu-guest-agent

Version avancée avec SELinux et Cockpit :

#cloud-config
package_update: true
package_upgrade: true

packages:
  - qemu-guest-agent
  - cockpit
  - policycoreutils-python-utils

write_files:
  - path: /etc/ssh/sshd_config.d/99-custom.conf
    content: |
      PermitRootLogin prohibit-password
      PasswordAuthentication no
      PubkeyAuthentication yes
    permissions: "0600"
    owner: root:root

runcmd:
  - systemctl enable qemu-guest-agent
  - systemctl start qemu-guest-agent
  - systemctl enable --now cockpit.socket
  - firewall-cmd --permanent --add-service=cockpit
  - firewall-cmd --reload
  - systemctl restart sshd

Script complet de déploiement

Voici un script paramétré pour créer une VM depuis un template avec vendor-data :

#!/bin/bash
set -e

TEMPLATE_ID=9000
VM_ID=200
VM_NAME="vm-test"
STORAGE="local-lvm"
VENDOR_FILE="alpine-vendor.yaml"
CI_USER="admin"
SSH_KEY_FILE="$HOME/.ssh/id_ed25519.pub"

qm clone $TEMPLATE_ID $VM_ID --name $VM_NAME --full
qm set $VM_ID --memory 2048 --cores 2
qm resize $VM_ID scsi0 +8G

qm set $VM_ID --ciuser $CI_USER
qm set $VM_ID --sshkeys $SSH_KEY_FILE
qm set $VM_ID --ipconfig0 ip=dhcp
qm set $VM_ID --cicustom "vendor=local:snippets/$VENDOR_FILE"

qm cloudinit update $VM_ID
qm start $VM_ID

echo "VM $VM_NAME ($VM_ID) démarrée avec succès"

Points clés à retenir

ParamètreComportement
--cicustom "user=..."Remplace la config Proxmox (ciuser, cipassword, sshkeys ignorés)
--cicustom "vendor=..."S’ajoute à la config Proxmox (paramètres conservés)
--cicustom "network=..."Remplace la config réseau Proxmox
--cicustom "meta=..."Remplace les métadonnées Proxmox

Vérification post-déploiement

Après le démarrage de la VM, vérifiez que Cloud-Init a bien appliqué les configurations :

cloud-init status --wait
cat /var/log/cloud-init-output.log

Pour vérifier le QEMU Guest Agent depuis Proxmox :

qm agent $VMID ping
qm agent $VMID network-get-interfaces

Aller plus loin

Cette approche Infrastructure as Code ouvre la voie à des déploiements automatisés plus complexes : installation de Docker ou Podman, configuration de serveurs web (Nginx, Apache), déploiement de bases de données (MariaDB, PostgreSQL), ou intégration avec Ansible pour la configuration post-déploiement.

Ressources