Skip to content

🎯 Qué construyes: opsbox, un conjunto de scripts que usarías de verdad para administrar un servidor. No un ejercicio de juguete: al terminar tendrás herramientas que puedes instalar en un VPS y usar.

Prerrequisitos: capítulos T1, T2 y T3. Duración: ~1 semana a ratos.


El contexto

Trabajas solo en un proyecto que corre en un VPS. No hay equipo de infraestructura. Necesitas que las tareas repetitivas sean fiables, porque las vas a ejecutar medio dormido y con prisa.


Requisitos

Estructura obligatoria

opsbox/
├── bin/
│   ├── opsbox               # punto de entrada: opsbox <comando> [opciones]
│   ├── deploy.sh
│   ├── backup.sh
│   ├── restore.sh
│   ├── health.sh
│   └── logs.sh
├── lib/
│   ├── comun.sh             # log, error, fatal, confirmar…
│   └── config.sh            # carga y validación de configuración
├── tests/
│   └── test_comun.sh        # sí, se puede testear Bash
├── opsbox.conf.example
├── Makefile
└── README.md

Funcionales

F1 · opsbox health — Diagnóstico del sistema.

  • Disco, RAM, carga, servicios (configurables), puertos escuchando, certificados TLS que caducan en menos de 30 días.
  • Salida legible con colores solo si stdout es una terminal.
  • Opción --json que produzca JSON válido (verificable con jq .).
  • Código de salida significativo: 0 OK · 1 advertencias · 2 crítico.

F2 · opsbox backup — Copia de seguridad con rotación.

  • Volcado de PostgreSQL comprimido, más una carpeta de archivos configurable.
  • Escribe primero en un temporal y solo mueve al destino si la verificación pasa.
  • Rotación configurable por días.
  • Opción --verify que compruebe la integridad del último backup.

F3 · opsbox restore — Restauración.

  • Lista los backups disponibles con fecha y tamaño, y permite elegir.
  • ⚠️ Confirmación explícita obligatoria antes de sobrescribir nada: el usuario debe escribir el nombre de la base de datos, no solo pulsar "s".
  • Opción --dry-run que muestre qué haría sin hacerlo.

F4 · opsbox deploy <version> — Despliegue con reversión.

  • Lock para impedir dos despliegues simultáneos.
  • Health check tras desplegar, con reintentos.
  • Reversión automática a la versión anterior si el health check falla.
  • Registro de cada despliegue en un archivo de historial (fecha, versión, resultado, quién).

F5 · opsbox logs [servicio] — Explorador de logs.

  • Sigue logs en vivo, con filtro por nivel (--level error).
  • Busca en logs rotados y comprimidos, sin descomprimirlos.
  • Resumen: --summary muestra el recuento de errores por hora de las últimas 24 h.

No funcionales

#Requisito
NF1Todos los scripts empiezan con #!/usr/bin/env bash y set -euo pipefail
NF2shellcheck bin/* lib/* pasa sin ningún aviso
NF3Toda expansión de variable va entre comillas dobles
NF4Cada script tiene --help y sale con código 0
NF5Ningún secreto escrito en el código: todo vía config o variables de entorno
NF6trap de limpieza en todo script que cree temporales
NF7Los mensajes de error van a stderr; los datos, a stdout
NF8Funciona en Ubuntu 24.04 y en WSL2

Fases

Fase 1 · El esqueleto (día 1)lib/comun.sh con log, info, ok, advertencia, error, fatal y confirmar. El despachador bin/opsbox que enrute subcomandos y muestre ayuda. Configura ShellCheck.

Fase 2 · health (días 2-3) Es el más fácil y el que te enseña el patrón: una función por comprobación, cada una devolviendo su código, y un agregador que se queda con el peor.

Fase 3 · backup y restore (días 4-5) Aquí está el riesgo real: restore destruye datos. Escribe primero el --dry-run.

Fase 4 · deploy (día 6) Lock, health check con reintentos, reversión. Pruébalo forzando un fallo a propósito.

Fase 5 · Pulido (día 7) Tests, --help en todo, README, instalador (make install que enlace en /usr/local/bin).


Criterios de aceptación

□ opsbox --help lista todos los subcomandos
□ opsbox health devuelve 2 cuando fuerzas el disco por encima del umbral
□ opsbox health --json | jq . funciona sin errores
□ opsbox health > salida.txt no contiene códigos de color
□ opsbox backup crea el archivo y opsbox backup --verify lo valida
□ Si matas el backup a mitad (Ctrl+C), NO queda un archivo corrupto en el destino
□ opsbox restore --dry-run no toca nada
□ opsbox restore sin confirmación exacta se aborta
□ opsbox deploy revierte solo si el health check falla (pruébalo rompiendo la app a propósito)
□ Dos opsbox deploy simultáneos: el segundo falla con un mensaje claro
□ shellcheck bin/* lib/* → silencio absoluto
□ Todos los scripts sobreviven a rutas con espacios

💡 La prueba que más te va a enseñar: crea una carpeta llamada mi carpeta de backups (con espacios) y una base de datos llamada test-db. Si algún script se rompe, te falta un par de comillas. Es exactamente el bug que te encontrarás en producción.


Extensiones (opcionales)

  1. opsbox monitor — bucle que ejecute health cada N minutos y envíe una alerta a Telegram o Slack cuando el estado cambie (solo al cambiar, no en cada ciclo: nadie lee una alerta que llega cada minuto).
  2. Backup remoto — sube el backup a S3/R2 con rclone o aws s3 cp, verificando el checksum.
  3. opsbox ssl — comprueba y renueva certificados con certbot, avisando con antelación.
  4. Autocompletado de Bash — un archivo en /etc/bash_completion.d/ para completar subcomandos y opciones con TAB.
  5. Tests de verdad — usa bats-core para escribir tests unitarios de tus funciones de lib/.

Errores típicos (y qué significan)

SíntomaCausa casi segura
Funciona a mano, falla en cronCron tiene otro PATH y otro directorio de trabajo. Usa rutas absolutas
"unbound variable" al pasar una opciónset -u + variable sin valor por defecto. Usa ${VAR:-}
El script se rompe con un nombre de archivo raroFalta un "$var"
El backup pesa 0 bytes y nadie se enteróNo verificaste la salida. [[ -s "$f" ]] y gzip -t
La reversión también fallaNo guardaste cuál era la versión anterior antes de cambiarla

Siguiente: 02-api-de-tareas.md