Una copia de seguridad que nunca se restauró es esperanza, no estrategia - y en GLPI la parte que más rompe la restauración no es el dump de la base, es glpicrypt.key. En el mantenimiento de entornos de clientes ya hemos visto una restauración "perfecta" de la base en la que nadie podía iniciar sesión por LDAP ni enviar correo: los datos volvieron, pero la clave que descifra las contraseñas de LDAP, SMTP y colector de correo se quedó atrás. Esta guía consolida la rutina de copia de seguridad, rotación, prueba de restauración y disaster recovery que ejecutamos en producción, centrada en los puntos donde las cosas realmente se rompen.
Qué debe entrar de verdad en la copia
GLPI tiene tres capas de estado, y las tres deben volver juntas y coherentes:
- Base de datos - tickets, activos, usuarios, reglas y configuraciones. Es el corazón, pero por sí sola no reconstruye el entorno.
- Directorio de archivos (
files/) - documentos adjuntos (files/_documents), imágenes, inventarios XML y datos de plugins. Si la base tiene la fila del documento pero el archivo no volvió, el usuario hace clic y recibe un error. - Configuración (
config/) - elconfig_db.php(host, base y credenciales) y, sobre todo,glpicrypt.key. Esa clave descifra las contraseñas guardadas en la base: bind de LDAP, colector de correo, SMTP de notificaciones y secretos de OAuth. Restaurar la base sin ella deja todas esas contraseñas como basura indescifrable.
Dentro de files/ no todo necesita copia. Directorios volátiles como _cache, _tmp, _sessions, _cron y _lock se regeneran solos y solo inflan la copia - exclúyelos. Lo que importa preservar es _documents, _pictures, _uploads, _inventories y _plugins.
Un dump consistente de la base
GLPI 10 y 11 usan InnoDB en todas las tablas, así que --single-transaction toma un snapshot coherente sin bloquear la aplicación - los usuarios siguen abriendo tickets durante la copia. --quick evita cargar tablas enormes en memoria y --default-character-set=utf8mb4 impide la corrupción de acentos y emojis. Nunca pases la contraseña en la línea de comandos (aparece en ps y en los logs); usa un archivo de credenciales restringido:
# ~/.my.cnf (chmod 600) - evita la contrasena en 'ps' y en los logs
[client]
host = 127.0.0.1
user = glpi
password = ...contrasena_de_glpi...
La copia nativa de GLPI (Administración > Mantenimiento) escribe un .sql en files/_dumps, pero en bases grandes suele superar los límites de tiempo y memoria de PHP. Para producción, un mysqldump programado es más fiable.
Script de copia con rotación
Un único script que resuelve base, archivos y config en el orden correcto y además limpia lo que supera los 30 días:
#!/usr/bin/env bash
set -euo pipefail
# Credenciales en ~/.my.cnf (chmod 600), nunca en la linea de comandos
DATA=$(date +%F)
DEST="/backup/glpi/${DATA}"
mkdir -p "${DEST}"
# Base de datos: snapshot consistente de InnoDB sin bloquear la aplicacion
mysqldump --single-transaction --quick --routines --triggers \
--default-character-set=utf8mb4 glpi \
| gzip -6 > "${DEST}/glpi_db.sql.gz"
# Archivos + config DESPUES del dump (un archivo de mas es inofensivo;
# una fila en la base apuntando a un archivo ausente, no)
tar -czf "${DEST}/glpi_files.tar.gz" \
--exclude='files/_cache' --exclude='files/_tmp' \
--exclude='files/_sessions' --exclude='files/_lock' \
-C /var/www/glpi files config
# Rotacion: conservar 30 dias
find /backup/glpi -maxdepth 1 -type d -mtime +30 -exec rm -rf {} +
echo "Backup OK: ${DEST}"
Fíjate en el orden: el dump va antes del tar. Un archivo que cae en disco después del dump es inofensivo en la restauración; lo contrario - una fila en la base apuntando a un archivo que aún no existía cuando corrió tar - se convierte en un adjunto roto. Prográmalo en cron:
# /etc/cron.d/glpi-backup - diario a las 02:15
15 2 * * * root /opt/scripts/backup-glpi.sh >> /var/log/glpi-backup.log 2>&1
Matriz de decisión: RPO vs RTO
Antes de cerrar frecuencia y retención, responde dos preguntas: cuántos datos puede permitirse perder el negocio (RPO) y en cuánto tiempo debe volver a estar en pie (RTO). La respuesta cambia según la criticidad:
| Perfil del entorno | RPO objetivo | RTO objetivo | Frecuencia de la base | Retención | Offsite |
|---|---|---|---|---|---|
| Preproducción / prueba | 24 h | días | diario | 7 días | opcional |
| Producción estándar | 24 h | 4 h | diario | 14-30 días | sí (nube) |
| Producción crítica (SLA) | 15 min | 1 h | diario + binlog o réplica | 30-90 días | sí, cifrado |
Son rangos honestos, no promesas. Un RPO de 15 minutos exige binlog de MariaDB/MySQL o una réplica - el dump diario por sí solo no lo entrega.
Prueba de restauración: el paso que casi nadie hace
Una copia que nunca se restauró es un número en un informe, no una garantía. Ensaya la restauración periódicamente en un entorno desechable - Docker es ideal - y nunca sobre la producción:
# Entorno DESECHABLE (Docker). Nunca ensayes sobre la produccion.
mysql -u root -e "CREATE DATABASE glpi_restore CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
# 1) Base de datos
zcat glpi_db.sql.gz | mysql --default-character-set=utf8mb4 -u root glpi_restore
# 2) Archivos + config (INCLUYE config/glpicrypt.key)
tar -xzf glpi_files.tar.gz -C /var/www/glpi
# 3) Codigo mas nuevo que el dump? aplica la migracion de esquema
php bin/console db:update --no-interaction
# 4) Limpia la cache y arranca para validar login/LDAP/correo
php bin/console cache:clear
Tras la restauración, confirma que el estado coincide con la realidad antes de confiar. Dos consultas rápidas ya detectan los problemas más comunes:
-- Version guardada en la base (debe coincidir con la del codigo antes de db:update)
SELECT value AS version_bd
FROM glpi_configs
WHERE context = 'core' AND name = 'version';
-- Cuantos documentos apuntan a un archivo fisico en files/_documents
SELECT COUNT(*) AS docs_con_archivo
FROM glpi_documents
WHERE filepath <> '';
La primera muestra la versión guardada en la base: si es menor que la del código, es db:update quien reconcilia el esquema. La segunda cuenta los documentos que esperan un archivo en disco - compárala con lo que realmente llegó en files/_documents. En Docker, los mismos pasos corren con docker exec -u www-data glpi php bin/console ....
Disaster recovery de verdad
Una copia es un comando; el disaster recovery es un plan. El mínimo sano es la regla 3-2-1: tres copias, en dos medios distintos, una de ellas offsite. Y aquí está el error común: meter el dump de la base y config/ (con glpicrypt.key) en el mismo .tar sin cifrar y enviarlo a un bucket. Quien tenga ese archivo posee toda la base y la clave que descifra las contraseñas de LDAP, SMTP y colector - es decir, credenciales de infraestructura del cliente en texto recuperable. La copia offsite debe ir cifrada (por ejemplo con age o gpg) y con acceso restringido.
Cierra el plan con un runbook escrito - dónde están las copias, cuál es el orden de restauración, quién lo activa - y con el RTO cronometrado en un ensayo real. En el mantenimiento, lo que separa un susto de una catástrofe nunca fue tener copia; fue haber restaurado esa copia antes, reloj en mano, y saber que vuelve en 40 minutos y no en "no lo sé".
Si tu GLPI es crítico y nunca cronometraste una restauración completa - base, archivos y la clave de cifrado juntos - NexTool diseña y opera la rutina de copia de seguridad y disaster recovery de tu entorno, con restauración ensayada y copia offsite cifrada. Habla con nosotros sobre soporte y mantenimiento de GLPI.
Este contenido se produjo con ayuda de inteligencia artificial y fue revisado por el equipo de Nextool Solutions.