Durante diez días, sh0 existió solo en un MacBook en Abiyán. Compilaba, pasaba los tests, se ejecutaba localmente. Pero un PaaS que no puede desplegarse a sí mismo es un fracaso irónico. El 21 y 22 de marzo de 2026, construimos la infraestructura para llevar sh0 de cargo build a un servidor en ejecución en demo.sh0.app -- y aprendimos que la última milla es donde vive la mayoría del dolor.
Esta es la historia de perfiles de release, pesadillas de compilación cruzada, bugs de .gitignore que rompieron compilaciones de producción y una Content Security Policy que convirtió el dashboard en una pantalla blanca.
El perfil de release: Exprimiendo el binario
El perfil de release por defecto de Rust ya es rápido, pero queríamos pequeño. sh0 se distribuye como un solo binario que los usuarios descargan y ejecutan. Cada megabyte importa, especialmente para usuarios con conexiones de internet africanas.
toml[profile.release]
opt-level = 3 # Optimización máxima
lto = "fat" # Link-time optimization completa
codegen-units = 1 # Unidad de codegen única
strip = "symbols" # Eliminar símbolos de depuración
panic = "abort" # Sin maquinaria de unwindingEl resultado: un binario de 25 MB en macOS ARM64, 27 MB en Linux x64. Comprimido a 10-11 MB en .tar.gz.
El Dockerfile multi-etapa
Tres etapas: compilar el dashboard, compilar el binario Rust, empaquetar el runtime. El dashboard debe compilarse primero porque el binario Rust lo incrusta vía la macro include_dir!. En tiempo de compilación, la macro lee cada archivo en dashboard/build/ y lo integra en el binario como un recurso estático. Esto es lo que hace de sh0 un solo binario -- toda la interfaz web está dentro del ejecutable.
El script de instalación
Los usuarios instalan sh0 con un solo comando:
bashcurl -fsSL https://get.sh0.dev | bashEl script maneja detección de plataforma, descarga y verificación: detecta SO y arquitectura, descarga el .tar.gz correcto, verifica SHA256, extrae a /usr/local/bin/sh0, crea el directorio de datos y muestra una guía de inicio rápido.
Compilación cruzada: El dolor
sh0 necesita ejecutarse en cuatro combinaciones de plataforma. Encontramos problemas con credenciales Docker en macOS, un bug de GCC memcmp en aws-lc-sys, y finalmente recurrimos a compilar en la plataforma objetivo para v1.0.0.
GitHub Actions CI/CD
Dos flujos de trabajo: CI (cada push a main, cada PR) con cuatro trabajos paralelos (fmt, clippy, test, dashboard), y Release (en tags de versión) con una matriz de compilación para cuatro objetivos.
Los bugs de .gitignore que rompieron producción
data/ coincidía demasiado ampliamente (incluyendo src/lib/data/), y <em>*/</em>secret* era demasiado codicioso (excluyendo una página sobre gestión de secretos). Ambos bugs eran invisibles en desarrollo local porque git no des-rastrea archivos que fueron confirmados antes de añadir una regla de ignorar.
El primer despliegue: demo.sh0.app
Desplegamos sh0 en un servidor Hetzner con Ubuntu 24.04. El dashboard mostró una página blanca por un problema de CSP que bloqueaba Google Fonts. La corrección fue añadir https://fonts.gstatic.com y https://fonts.googleapis.com a las directivas CSP.
El aprovisionamiento automático
Añadimos instalación automática de dependencias: cuando un usuario ejecuta sh0 serve en un servidor nuevo sin Docker o Caddy, sh0 detecta su ausencia y los instala.
El estado de la infraestructura después del día 11
Diez días antes, sh0 era un cargo init. Ahora era un producto desplegado con un sitio web, un script de instalación, distribución de binarios, CI/CD y un servidor demo en vivo. El pipeline de release no era elegante -- subidas manuales de binarios, soluciones alternativas de compilación cruzada, bugs de .gitignore descubiertos en producción. Pero funcionaba. Y funcionando, en producción, supera a elegante en papel.
Siguiente en la serie: Construyendo para África: Mobile Money, precios locales y por qué importa -- por qué construimos sh0 desde Abiyán con pagos Mobile Money, soporte para 5 idiomas y precios diseñados para desarrolladores africanos.