Skip to main content

Sistema de backup pull para Proxmox VE

Sistema de backup basado en snapshots LVM para maquinas virtuales en Proxmox VE. Utiliza una arquitectura pull donde el servidor de storage inicia las conexiones SSH hacia Proxmox y tira de los datos. El servidor Proxmox nunca tiene acceso de escritura al storage.

Motivacion

El modelo tradicional push (Proxmox envia al storage) tiene un problema de seguridad critico: si el servidor Proxmox es comprometido, el atacante tiene credenciales SSH con acceso de escritura al storage y puede destruir todos los backups.

Con el modelo pull:

  • Proxmox no conoce las credenciales del storage
  • Proxmox no puede escribir ni borrar en el storage
  • Solo el storage decide cuando y que se respalda
  • Un Proxmox comprometido no puede destruir los backups existentes

Arquitectura

A partir de v2.0.0 el flujo es por VM en vez de por disco individual. Para VMs con multiples discos, todos los snapshots se crean atomicamente antes de transferir cualquier dato, garantizando consistencia temporal.

Storage Server                          Proxmox Server

     |                                       |
     |-- ssh "list" ------------------------>|  (inventario de VMs)
     |<-- CSV: vmid,status,vg,lv,size -------|
     |                                       |
     |  Para cada VM:                        |
     |-- ssh "snapshotsnapshot-vm vg<vmid> lv 10 1"<pct>" ---->|  (snapshots atomicos de TODOS los discos)
     |<-- manifest: snap_name,vg,lv,size ----|
     |                                       |
     |  Para cada disco en manifest:         |
     |-- ssh "stream-disk <vg> <snap>" ----->|  (creastream snapshot LVM)comprimido)
     |<------------ dd|pigz stream ----------|
     (stream comprimido)
     |-- guarda en incoming/                 |
     |-- verifica pigz -t                    |
     |                                       |
     |  Si TODOS los discos OK:              |
     |-- mv atomico incoming/ -> snapshots/  |
     |-- prune copias antiguas               |
     |                                       |
     |  Si ALGUN disco falla:                |
     |-- elimina TODO incoming/ de esta VM   |
     |-- backup previo en snapshots/ intacto |
     |                                       |
     |  SIEMPRE:                             |
     |-- ssh "cleanupcleanup-vm vg<vmid>" snap-name" ----------->|  (elimina snapshot)TODOS |--los mv incoming/ -> snapshots/          |  (movimiento atomico)
     |-- prune copias antiguas              |snapshots)

Consistencia multi-disco (v2.0.0)

El problema que resuelve v2.0.0: en versiones anteriores, los snapshots se creaban uno a uno (snapshot disk0 → transfer disk0 → cleanup disk0 → snapshot disk1 → transfer disk1). Para VMs con multiples discos, los snapshots capturaban diferentes instantes, produciendo backups inconsistentes.

La solucion:

    snapshot-vm crea TODOS los snapshots en sucesion rapida (milisegundos entre ellos)

    Si algun snapshot falla, rollback automatico de los ya creados

    Los streams se transfieren secuencialmente pero desde snapshots del mismo instante

    cleanup-vm elimina todos los snapshots al final

    Semantica all-or-nothing

      Un backup parcial de VM es inutil (disk0 de hoy y disk1 de ayer) snapshots/ siempre contiene conjuntos completos Si ANY disco falla: se borra todo el incoming de esa VM, el backup anterior queda intacto Prune solo ejecuta tras exito total

      Componentes

      Componente
      Version Ubicacion Funcion proxmox-backup-helper.sh 2.0.0 Proxmox: /usr/local/bin/ o repo clonado Manejador SSH restringido con 812 comandos proxmox-pull-backup.sh 2.0.0 Storage: /root/backups/pull/ o repo clonado Orquestador de backups (flujo por VM) proxmox-pull-restore.py 1.0.0 Storage: /root/backups/pull/mismo directorio Herramienta de restauracion (Python 3.6+) backup-access-shell.sh 1.0.0 Storage: /usr/local/bin/ Shell de solo lectura para restore install-pull-backup.sh 1.0.0 Storage: /root/backups/pull/mismo directorio Instalador automatizado

      Requisitos previos

      • Proxmox: Version 7.x, 8.x o 8.9.x con almacenamiento LVM
      • Storage: Servidor Linux con espacio suficiente
      • Ambos servidores: pigz instalado (apt-get install pigz)
      • Storage: Python 3.6+ (solo stdlib, sin dependencias externas)
      • Red: Acceso SSH desde storage hacia Proxmox

      Instalacion

      Paso 1: Instalar pigz en ambos servidores

      apt-get install -y pigz
      
      

      Paso 2: Subir los scripts al storage

      # Desde la maquina local o mediante rsync
      rsync -avz proxmox-backup/pull/ storage:/root/backups/pull/
      chmod +x /root/backups/pull/*.sh
      
      

      Paso 3: Ejecutar el instalador

      Desde el servidor de storage:

      cd /root/backups/pull/
      
      ./install-pull-backup.sh \
        --proxmox-host prox06.example.com \
        --proxmox-port 22 \
        --proxmox-name prox06 \
        --storage-dir /srv/storage/backups_kvm
      

      El instalador ejecuta 8 pasos automaticos:

      1. Genera clave SSH ed25519 en /root/backups/pull/keys/

      2. Copia el helper a Proxmox en /usr/local/bin/

      3. Configura authorized_keys con restriccion command=

      4. Crea directorios incoming/ y snapshots/

      5. Auto-detecta discos LVM en Proxmox

      6. Anade el host al inventario proxmox_hosts.cfg

      7. Instala backup-access-shell.sh en storage

      8. Ejecuta tests de conectividad

      Paso 4: Crear la configuracion principal

      cp /root/backups/pull/config/pull-backup.cfg.example \
         /root/backups/pull/config/pull-backup.cfg
      
      

      Editar los valores segun el entorno:

      # Directorios
      incoming_dir="/srv/storage/backups_kvm/incoming"
      final_dir="/srv/storage/backups_kvm/snapshots"
      
      # Retencion
      numcopias=3
      
      # Snapshots (v2.0.0)
      snapshot_pct=15          # Porcentaje del LV para snapshot (default 15)
      default_vm_scope="all"   # "all" = descubre y respalda todas las VMs; "none" = requiere filtro explicito
      
      # Rendimiento
      min_speed_mbs=150    # MB/s minimos esperados
      min_free_gb=1024     # GB libres requeridos
      ssh_timeout=30       # Timeout SSH en segundos
      
      # Notificaciones (solo en errores)
      correo="admin@example.com"
      
      

      Paso 5: Revisar el inventario de discoshosts

      cat /root/backups/pull/hosts/disks_prox06.txtproxmox_hosts.cfg
      
      

      ComentarFormato conCSV:

      # name,host,port,key_file,disks_file
      avanza01,ns3248916.ip-91-134-71.eu,22,/root/backups/pull/keys/id_pull_backup,
      
      

      El campo #disks_file loses discosopcional quedesde nov2.0.0. Si se quierandeja respaldar.vacio y default_vm_scope="all", el orquestador descubre automaticamente todas las VMs activas via el comando list.

      Paso 6: Configurar cron

      # Backup diario a las 3:10:00 AMUTC
      echo "0 310 * * * /root/backups/pull/proxmox-pull-backup.sh"sh >> /root/backups/pull/logs/cron.log 2>&1" | crontab -
      
      

      Uso diario

      Ejecutar backup manual

      # Dry-run (no ejecuta nada, muestra lo que haria)
      ./proxmox-pull-backup.sh --host prox06 --verbose --dry-run
      
      # Backup real de todas las VMs de un host
      ./proxmox-pull-backup.sh --host prox06 --verbose
      
      # Backup de una VM especifica
      ./proxmox-pull-backup.sh --host prox06 --vm 104 --verbose
      
      # Backup de varias VMs (separadas por coma o repetido)
      ./proxmox-pull-backup.sh --host prox06 --vm 104,203 --verbose
      
      # Backup de todos los hosts configurados
      ./proxmox-pull-backup.sh --verbose
      
      

      Listar backups disponibles

      python3 proxmox-pull-restore.py --config config/pull-backup.cfg list
      
      # Filtrar por host
      python3 proxmox-pull-restore.py --config config/pull-backup.cfg list --host prox06
      
      # Filtrar por VM
      python3 proxmox-pull-restore.py --config config/pull-backup.cfg list --vm 316
      
      

      Restaurar una VM

      Escenario A: la VM existe y esta parada (sobreescribe los discos):

      python3 proxmox-pull-restore.py --config config/pull-backup.cfg \
        restore --host prox06 --vm 316 --data-only
      
      

      Escenario B: la VM no existe (crea LV, config y restaura datos):

      python3 proxmox-pull-restore.py --config config/pull-backup.cfg \
        restore --host prox06 --vm 316 --from-scratch
      
      

      Restaurar desde una fecha concreta:

      python3 proxmox-pull-restore.py --config config/pull-backup.cfg \
        restore --host prox06 --vm 316 --date 2026-02-08
      
      

      Restaurar varias VMs en paralelo:

      python3 proxmox-pull-restore.py --config config/pull-backup.cfg \
        restore --host prox06 --vm 316 900 --workers 2
      
      

      IMPORTANTE: el argumento --config va ANTES del subcomando (list, restore).

      Modelo de seguridad

      Lado Proxmox

      El helper (proxmox-backup-helper.sh) se ejecuta exclusivamente via la restriccion command= en authorized_keys. Esto significa que cualquier conexion SSH con esa clave ejecuta el helper, ignorando el comando solicitado por el cliente.

      El helper valida cada argumento con expresiones regulares estrictas:

      • Volume groups: solo alfanumericos, guiones y guiones bajos
      • Logical volumes: solo patron vm-\d+-disk-\d+
      • Snapshot names: solo patron snap-\d+-disk\d+
      • VMIDs: solo numeros entre 100 y 999999
      • Snapshot porcentaje: solo numeros entre 1 y 100

      Comandos no reconocidos se rechazan con error. Todas las operaciones se registran en syslog.

      Lado Storage

      El storage inicia todas las conexiones. Proxmox nunca se conecta al storage para los backups. El unico acceso inverso (Proxmox hacia storage) es para restore, y se canaliza a traves de backup-access-shell.sh que solo permite operaciones de lectura (pigz -dc, ls, test, cat, du) y valida rutas con readlink -f contra symlinks maliciosos.

      Estructura de ficheros

      En el servidor de storage

      /root/backups/pull/
        proxmox-pull-backup.sh        # Orquestador v2.0.0
        proxmox-pull-restore.py       # Herramienta de restore
        backup-access-shell.sh        # Shell read-only
        install-pull-backup.sh        # Instalador
        config/
          pull-backup.cfg              # Configuracion principal
          pull-backup.cfg.example      # Template (versionado)
        hosts/
          proxmox_hosts.cfg            # Inventario de hosts
          disks_prox06.txt             # Discos(Opcional) filtro de prox06
          disks_prox07.txt             # Discos de prox07discos
        keys/
          id_pull_backup               # Clave privada SSH
          id_pull_backup.pub           # Clave publica SSH
        logs/
          pull-backup-YYYY-MM-DD.log   # Log diario
          cron.log                     # Log de cron
      
      

      En el servidor Proxmox

      /usr/local/bin/proxmox-backup-helper.sh    # Helper restringido v2.0.0
      /root/.ssh/authorized_keys                 # Clave con command=
      
      

      Directorio de backups

      /srv/storage/backups_kvm/
        incoming/                                # Transferencias en curso
        snapshots/
          kvm-316-104-disk0.2026-02-08-03-26-10-00-12.gz   # Disco 0 comprimido
          kvm-316.104-disk1.2026-02-26-10-00-12.gz   # Disco 1 (mismo timestamp)
          kvm-104.conf.2026-02-08-03-26-10-00-12       # Config de la VM
      
      

      Nomenclatura de ficheros

      kvm-{vmid}-disk{N}.{YYYY-MM-DD-HH-MM-SS}.gz    # Discos
      kvm-{vmid}.conf.{YYYY-MM-DD-HH-MM-SS}           # Configuraciones
      
      

      Comandos del helper

      El helper v2.0.0 acepta 812 comandos, todos validados con regex:

      Comando Argumentos Funcion
      list (ninguno) Lista todasVMs las VMsactivas con discos LVM
      list-all (ninguno) Lista TODAS las VMs (incluidas paradas) list-snapshots (ninguno) Lista snapshots activos (deteccion de huerfanos) snapshot-vm vmid porcentaje [force] v2.0.0: Crea snapshots atomicos de TODOS los discos de una VM. Devuelve manifest por stdout stream-disk vg snap_name v2.0.0: Envia stream comprimido de un snapshot existente cleanup-vm vmid v2.0.0: Elimina TODOS los snapshots de una VM snapshot vg lv porcentaje [force] (Deprecated) Crea snapshot individual y envia stream comprimido cleanup vg snap_name Elimina un snapshot LVM individual config vmid Devuelve el fichero de configuracion de la VM restore vg lv Recibe stream comprimido y lo escribe al disco create-disk vg nombre size_gb Crea un nuevo volumen logico restore-config vmid Recibe y escribe la config de la VM

      Formato del manifest (snapshot-vm)

      # snap_name,vg,lv,size_gb
      snap-104-disk0,pve,vm-104-disk-0,150
      snap-104-disk1,pve,vm-104-disk-1,125
      
      

      Opciones del orquestador

      proxmox-pull-backup.sh [--config <path>] [--host <name>] [--vm <vmid>[,<vmid>...]] [--dry-run] [--force] [--verbose]
      
        --config <path>            Fichero de configuracion (default: ./config/pull-backup.cfg)
        --host <name>              Backup solo este host (default: todos los hosts)
        --vm <vmid>[,<vmid>...]    Backup solo estas VMs (separadas por coma, repetible)
        --dry-run                  Muestra lo que haria sin ejecutar
        --force                    Auto-limpieza de snapshots huerfanos en Proxmox
        --verbose                  Salida detallada
      

      Funcionalidades avanzadas

      Descubrimiento dinamico (v2.0.0)

      Con default_vm_scope="all" en la config, el orquestador descubre automaticamente todas las VMs activas usando el comando list del helper. Los ficheros disks_*.txt pasan a ser opcionales (filtro de exclusion, no inventario obligatorio).

      Timeout dinamico

      El timeout de cada transferencia se calcula automaticamente segun el tamano del disco:

      timeout = (tamano_disco_gb * 1024) / min_speed_mbs
      
      

      Con un minimo de 300 segundos. Para un disco de 620GB150GB a 150 MB/s el timeout es ~7017 minutos.

      Deteccion de snapshots huerfanos

      Antes de procesar cada host, el orquestador ejecuta list-snapshots y compara con los snapshots esperados. Los huerfanos (de ejecuciones previas fallidas) se limpian automaticamente si force_orphan_cleanup=1.

      Estimacion de tamano

      Si existe un backup previo del mismo disco, se usa su tamano como estimacion. Si no, se estima el 50% del tamano raw del disco. Esto se usa para la verificacion de espacio libre.

      Verificacion de integridad

      Tras cada transferencia, se ejecuta pigz -t sobre el fichero comprimido para verificar integridad. Si falla, se descarta el fichero y se reintenta.

      Reintentos

      Si un backup falla, se reintenta una vez tras 10 minutos de espera. Solo se reporta error si el segundo intento tambien falla. Si falla el reintento, la VM completa se marca como fallida (all-or-nothing).

      Pruning automatico

      Tras completar los backups, se mantienen solo las ultimas numcopias copias de cada disco. Las configs asociadas a backups eliminados tambien se limpian.

      Rendimiento validado

      Produccion (avanza01 + stor01avanzait, febrero 2026)

      DatosKVM reales104 del— staging2 discos, snapshots atomicos v2.0.0:

      Disco Tamano raw Comprimido Tiempo transfer Velocidad Verificacion pigz -t disk0 150 GB 91 GB 828s (14 min) 111 MB/s ~16 min disk1 125 GB 80 GB 769s (13 min) 106 MB/s ~17 min

      Total: 171 GB comprimidos, 3657s (~61 min) incluyendo snapshots, transfers, verificacion y cleanup.

      Staging (avanza02 + stor01avanzait, febrero 2026):

      Operacion Disco Tiempo Velocidad Resultado
      Backup 5GB 11s 6 MB/s 73MB comprimido
      Restore data-only 5GB 10s 7 MB/s OK
      Restore from-scratch 5GB 9s 7 MB/s LV + config + datos
      Deteccion huerfanos -- instantaneo -- Auto-limpieza
      Pruning 4 a 3 copias -- <1s -- Retencion exacta

      Despliegues actuales

      Storage Host Proxmox VMs Cron Version stor01avanzait avanza01 KVM 104 (2 discos), KVM 203 (2 discos) 10:00 UTC diario v2.0.0

      Troubleshooting

      El backup genera un fichero .gz invalido

      Causa: Los comandos lvcreate/lvremove escriben mensajes a stdout que contaminan el stream gzip.

      Solucion: Verificar que el helper redirige stdout de lvm a stderr (>&2). Actualizar el helper a v1.1.0 o superior.v2.0.0.

      Error "integer expression expected" en el installer

      Causa: grep -c via SSH devuelve multiples lineas (0\n0), bash no puede comparar.

      Solucion: Bug menor conocido, no afecta a la instalacion. El paso 3 completa correctamente.

      El installer falla en el test de restriccion

      Causa: set -euo pipefail captura el exit code 1 del helper cuando rechaza un comando no autorizado.

      Solucion: Bug menor conocido. La restriccion funciona correctamente. Verificar manualmente con:

      ssh -i keys/id_pull_backup proxmox "ls /"
      # Debe devolver: ERROR: Unknown command: ls
      
      

      El restore dice "pigz: unrecognized format"

      Causa: El fichero .gz esta corrupto (ver primer problema).

      Solucion: Rehacer el backup con el helper corregido.v2.0.0.

      Timeout en backups de discos grandes

      Causa: El timeout dinamico depende de min_speed_mbs en la config.

      Solucion: Reducir min_speed_mbs si la red es lenta. Por ejemplo, para 50 MB/s reales:

      min_speed_mbs=40   # Dejar margen del 20%
      
      

      Snapshot overflow (snap_percent=100)

      Causa: El disco recibe mas escrituras de las esperadas durante la transferencia, llenando el snapshot COW.

      Solucion: Aumentar snapshot_pct en la config (default: 15). Para VMs con alta actividad de I/O, usar 20-25.

      Repositorio

      El codigo fuente esta en el repositorio utilidades bajo proxmox-backup/pull/.

      Documentacion completa: proxmox-backup/README.md y proxmox-backup/docs/SPEC-pull-backup-architecture.md.

      Historial de versiones

      Version Fecha Cambio principal 1.0.0 2026-02-08 Primera version funcional 1.1.0 2026-02-08 Fix stdout pollution en helper, validacion staging 1.4.0 2026-02-09 Multi-VM filter, dynamic discovery 2.0.0 2026-02-26 Snapshots atomicos multi-disco, flujo per-VM, all-or-nothing, snapshot-vm/stream-disk/cleanup-vm

      Ultima actualizacion

      • Fecha: 2026-02-0826
      • Version: 1.1.2.0.0
      • Autor: Abkrim