🎯 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.mdFuncionales
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
--jsonque produzca JSON válido (verificable conjq .). - Código de salida significativo:
0OK ·1advertencias ·2crí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
--verifyque 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-runque 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:
--summarymuestra el recuento de errores por hora de las últimas 24 h.
No funcionales
| # | Requisito |
|---|---|
| NF1 | Todos los scripts empiezan con #!/usr/bin/env bash y set -euo pipefail |
| NF2 | shellcheck bin/* lib/* pasa sin ningún aviso |
| NF3 | Toda expansión de variable va entre comillas dobles |
| NF4 | Cada script tiene --help y sale con código 0 |
| NF5 | Ningún secreto escrito en el código: todo vía config o variables de entorno |
| NF6 | trap de limpieza en todo script que cree temporales |
| NF7 | Los mensajes de error van a stderr; los datos, a stdout |
| NF8 | Funciona 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 llamadatest-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)
opsbox monitor— bucle que ejecutehealthcada 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).- Backup remoto — sube el backup a S3/R2 con
rcloneoaws s3 cp, verificando el checksum. opsbox ssl— comprueba y renueva certificados concertbot, avisando con antelación.- Autocompletado de Bash — un archivo en
/etc/bash_completion.d/para completar subcomandos y opciones con TAB. - Tests de verdad — usa bats-core para escribir tests unitarios de tus funciones de
lib/.
Errores típicos (y qué significan)
| Síntoma | Causa casi segura |
|---|---|
| Funciona a mano, falla en cron | Cron tiene otro PATH y otro directorio de trabajo. Usa rutas absolutas |
| "unbound variable" al pasar una opción | set -u + variable sin valor por defecto. Usa ${VAR:-} |
| El script se rompe con un nombre de archivo raro | Falta 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 falla | No guardaste cuál era la versión anterior antes de cambiarla |
Siguiente: 02-api-de-tareas.md