Un contenedor Docker con dnsmasq actúa como servidor DHCP en vmbr1 (ver Configuración de red de Proxmox). Cada vez que se crea o arranca una VM, un hookscript de Proxmox lee automáticamente el VMID y la MAC de la VM y registra la reserva DHCP sin intervención manual. Al borrar la VM, la reserva se elimina.
Esquema de IPs: 10.10.10.<VMID> — el VMID debe ser ≤ 254.
VM arranca (VMID=105)
↓
hookscript lee MAC de /etc/pve/qemu-server/105.conf
↓
calcula IP → 10.10.10.105
↓
escribe en /opt/stack/dnsmasq/hosts.d/reservas.conf:
BC:24:11:AA:BB:CC,10.10.10.105
↓
docker kill --signal=HUP dnsmasq-router → recarga sin reiniciar
| Fichero | Ubicación en host | Descripción |
|---|---|---|
docker-compose.yml | /opt/stack/ | Despliegue del contenedor (junto con NginxPM) |
dnsmasq.conf | /opt/stack/dnsmasq/config/ | Configuración del servidor DHCP |
reservas.conf | /opt/stack/dnsmasq/hosts.d/ | Reservas dinámicas VMID↔MAC↔IP |
hookscript.sh | /var/lib/vz/snippets/ | Script ejecutado por Proxmox en cada evento de VM |
Crear /opt/stack/dnsmasq/config/dnsmasq.conf:
# Interfaz donde escucha (red interna de VMs)
interface=vmbr1
bind-interfaces
# Rango DHCP dinámico (para IPs sin reserva)
dhcp-range=10.10.10.100,10.10.10.200,12h
# Fichero de leases
dhcp-leasefile=/var/lib/dnsmasq/leases
# Fichero de reservas estáticas (VMID→MAC→IP)
dhcp-hostsfile=/etc/dnsmasq/hosts.d/reservas.conf
# DNS upstream
server=8.8.8.8
server=8.8.4.4
# No leer /etc/hosts del contenedor
no-hosts
# Log
log-dhcp
Crear /opt/stack/dnsmasq/hosts.d/reservas.conf vacío:
touch /opt/stack/dnsmasq/hosts.d/reservas.conf
Crear /var/lib/vz/snippets/hookscript.sh:
#!/bin/bash
#
# Hookscript de Proxmox — registra/elimina reservas DHCP en dnsmasq
# Uso: asignado a cada VM con: qm set <VMID> --hookscript local:snippets/hookscript.sh
#
VMID=$1
PHASE=$2
RESERVAS_FILE="/opt/stack/dnsmasq/hosts.d/reservas.conf"
DNSMASQ_CONTAINER="dnsmasq-router"
IP="10.10.10.${VMID}"
# Lee la MAC de la interfaz net0 del config de la VM
get_mac() {
grep "^net0:" /etc/pve/qemu-server/${VMID}.conf 2>/dev/null \
| grep -oP '([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' \
| head -1
}
reload_dnsmasq() {
docker kill --signal=HUP "$DNSMASQ_CONTAINER" > /dev/null 2>&1
}
case "$PHASE" in
pre-start)
MAC=$(get_mac)
if [ -z "$MAC" ]; then
echo "hookscript: VMID ${VMID} no tiene net0, saltando."
exit 0
fi
# Eliminar entradas previas para esta IP o MAC (evita duplicados)
sed -i "/,${IP}$/d" "$RESERVAS_FILE"
sed -i "/^${MAC},/d" "$RESERVAS_FILE"
# Añadir nueva reserva
echo "${MAC},${IP}" >> "$RESERVAS_FILE"
echo "hookscript: registrada ${MAC} → ${IP}"
reload_dnsmasq
;;
post-stop)
MAC=$(get_mac)
if [ -z "$MAC" ]; then
exit 0
fi
# Eliminar reserva
sed -i "/^${MAC},/d" "$RESERVAS_FILE"
sed -i "/,${IP}$/d" "$RESERVAS_FILE"
echo "hookscript: eliminada reserva ${MAC} → ${IP}"
reload_dnsmasq
;;
esac
exit 0
Dar permisos de ejecución:
chmod +x /var/lib/vz/snippets/hookscript.sh
El hookscript se asigna automáticamente al crear el template con create-haos-template.sh (ver Máquinas Virtuales y Contenedores). Para asignarlo manualmente a una VM existente:
qm set <VMID> --hookscript local:snippets/hookscript.sh
| Evento | Acción |
|---|---|
pre-start | Lee MAC, calcula IP, deduplica y escribe reserva, recarga dnsmasq |
post-stop | Elimina reserva del fichero, recarga dnsmasq |