Desplegar un proyecto
¿Qué es deploy?
nuzur-cli deploy convierte un modelo aprobado en un servidor en funcionamiento con un solo comando. Apúntalo a una máquina Linux tuya o deja que cree el servidor por ti en DigitalOcean o Hetzner. A partir de ahí genera el código de la aplicación desde la versión de tu proyecto, lo compila en el servidor, aprovisiona una base de datos, aplica tu esquema y conecta todo de vuelta a nuzur — para que puedas gestionar los datos desplegados desde el Gestor de Datos de inmediato.
El código generado queda en un directorio tuyo que puedes editar: súbelo a git, añade endpoints propios y vuelve a desplegar para publicarlos. Ver El código de tu app.
El servidor sigue siendo tuyo. nuzur nunca recibe tu clave SSH, la contraseña de tu base de datos ni las credenciales de tu proveedor de nube: todo se ejecuta desde tu máquina a través de tu propia conexión SSH, y la contraseña de la base de datos se genera en el servidor, de modo que el texto plano nunca viaja por la red.
Requisitos
- La CLI de nuzur instalada en tu máquina (descargar)
- Una cuenta de nuzur con acceso al proyecto que quieres desplegar
- Un servidor, que puede ser:
- uno que ya tengas — con Ubuntu o Debian (el bootstrap usa
apt), accesible por SSH comorooto como un usuario con sudo sin contraseña; o - uno que nuzur cree por ti en DigitalOcean o Hetzner, para lo cual necesitas la CLI de ese proveedor instalada y autenticada (ver Dónde desplegar)
- uno que ya tengas — con Ubuntu o Debian (el bootstrap usa
- Para un despliegue completo: una configuración de Go Code Gen para el proyecto. Ejecuta
nuzur-cli go-code-genuna vez (o pasa--config-file) para que la CLI sepa qué superficie de API, autenticación y opciones generar.
Inicio rápido
Despliega en un servidor que ya tengas:
nuzur-cli deploy --host 203.0.113.10 --project mi-proyecto
O deja que nuzur cree también el servidor — sin --host, solo una región:
nuzur-cli deploy --provider digitalocean --region nyc3 --project mi-proyecto
En ambos casos es un despliegue completo: la última versión de mi-proyecto, una base de datos MySQL autoalojada y la API generada servida por HTTP.
Para un certificado HTTPS real, apunta primero un dominio al servidor y pásalo:
nuzur-cli deploy --host 203.0.113.10 --project mi-proyecto --domain api.ejemplo.com
Al terminar, la CLI imprime el id del despliegue, el uuid del agente, la URL pública y un enlace al Gestor de Datos que abre directamente la base de datos desplegada.
Qué hace deploy realmente
Cada paso es idempotente: volver a ejecutar el mismo comando actualiza el despliegue existente en lugar de crear uno nuevo.
- Resuelve tu proyecto y versión (por defecto, la última) e inicia sesión si hace falta.
- Genera el código de la aplicación con la extensión Go Code Gen (incluido un Dockerfile) en un directorio persistente que es tuyo —
./nuzur-<identifier>por defecto. - Emite un token de aprovisionamiento de un solo uso (válido 15 minutos) para que el servidor se empareje sin inicio de sesión interactivo.
- Crea el servidor y espera a que SSH responda — solo en proveedores gestionados; con
--provider sshusa el host que le indicaste. - Copia el código generado al servidor por SSH.
- Prepara la máquina: instala Docker, el motor de base de datos y Caddy; crea la base de datos y un usuario de aplicación con privilegios mínimos; compila la imagen; escribe las unidades de systemd.
- Empareja el agente de nuzur en el servidor y registra la base de datos como una conexión con nombre.
- Aplica tu esquema a la base de datos vacía mediante la extensión SQL Push (local).
- Registra el despliegue localmente y lo reporta a nuzur, donde aparece en la pantalla de Despliegues con su estado en vivo.
Qué queda en el servidor
| Elemento | Ubicación |
|---|---|
| Servicio de la app | {identifier}-api.service (systemd, Restart=always) |
| Contenedor / imagen | {identifier}-api / nuzur/{identifier}:latest |
| Configuración de la app | /etc/nuzur/{identifier}/config/prod.yaml (permisos 0600) |
| Contraseña de la BD | /etc/nuzur/{identifier}/db_password — generada en el servidor, nunca enviada a nuzur |
| Puerta de entrada | Caddy, un fragmento por proyecto en /etc/caddy/conf.d/{identifier}.caddy |
| Agente | nuzur-agent.service — un único agente compartido por servidor, para todos sus proyectos |
| Copias de seguridad | Volcado nocturno en /var/backups/nuzur, retención de 14 días (solo bases de datos autoalojadas) |
| Cortafuegos | ufw permite SSH (22), más 80/443 y el puerto público del proyecto en un despliegue completo |
La base de datos escucha en 127.0.0.1 y el agente se conecta solo hacia fuera, así que ninguno queda expuesto a internet.
Dónde desplegar
--provider decide cómo se obtiene el servidor. Todo lo que viene después — el bootstrap, la base de datos, el agente, el esquema — es idéntico en todos los proveedores.
| Proveedor | Qué hace | Necesita |
|---|---|---|
ssh (por defecto) |
Usa una máquina Linux que ya tienes. También es la opción universal para cualquier proveedor sin adaptador. | --host |
digitalocean |
Crea un droplet por ti, añade un cortafuegos en la nube y lo elimina al destruir. | doctl, --region |
hetzner |
Crea un servidor de Hetzner Cloud por ti, añade un cortafuegos y lo elimina al destruir. | hcloud, --region |
AWS, GCP, Azure, Vultr, Linode y Scaleway se reconocen pero aún no están implementados — si nombras uno te avisa de que está planificado en lugar de fallar como desconocido. Mientras tanto, crea la VM tú mismo y despliega con --provider ssh --host <ip>.
Proveedores gestionados
nuzur nunca maneja las credenciales de tu nube. Los adaptadores delegan en la CLI del propio proveedor, así que la autenticación se queda donde ya la configuraste:
# DigitalOcean — instala doctl y luego:
doctl auth init
# Hetzner — instala hcloud y luego:
hcloud context create
Despliega sin --host y la CLI crea la VM, espera hasta 3 minutos a que SSH responda y ejecuta el mismo bootstrap que en cualquier otro servidor:
nuzur-cli deploy --provider hetzner --region nbg1 --project mi-proyecto --domain api.ejemplo.com
--region es obligatorio. El resto tiene valores por defecto que puedes sobrescribir:
| DigitalOcean | Hetzner | |
|---|---|---|
--region |
una región, p. ej. nyc3, fra1 |
una localización, p. ej. nbg1, fsn1, hel1, ash |
--size |
s-1vcpu-1gb |
cpx22 |
--image |
ubuntu-22-04-x64 |
ubuntu-22.04 |
La VM se llama nuzur-{identifier} y siempre entra como root.
Hetzner: ningún tipo de servidor está disponible en todas las localizaciones (las de EE. UU. usan otra línea), así que si
cpx22no existe donde despliegas, ejecutahcloud server-type listy pasa uno válido con--size.
Claves SSH
Pasa --ssh-key-name para usar una clave ya registrada en el proveedor. Si no, la CLI sube la parte pública de tu --ssh-key — o de tu ~/.ssh/id_ed25519 o ~/.ssh/id_rsa por defecto — con el nombre nuzur-deploy y la reutiliza en despliegues posteriores.
Cortafuegos del proveedor
Los despliegues gestionados también reciben un cortafuegos a nivel de proveedor (nuzur-fw-{id-de-instancia}) que refleja el ufw de la máquina: SSH, más 80/443 y el rango de puertos públicos para una app completa, o solo SSH con --db-only. Es defensa en profundidad y de mejor esfuerzo — el ufw de la máquina sigue siendo la barrera autoritativa, así que un fallo del cortafuegos avisa en lugar de tumbar el despliegue.
El código de tu app
Deploy genera la aplicación en un directorio persistente en tu máquina y compila desde ahí. Ese directorio es tuyo: léelo, edítalo, súbelo a git.
./nuzur-mi-proyecto/ # por defecto; cámbialo con --source-dir
La ruta se guarda con el despliegue, así que los despliegues posteriores la reutilizan sin que vuelvas a pasar el flag. Los despliegues con --db-only no generan app, así que no tienen directorio de código.
Los redespliegues conservan tus cambios
Volver a ejecutar deploy regenera en el mismo directorio — no lo borra. Los archivos generados (los que llevan la cabecera Code generated ... DO NOT EDIT) se actualizan según tu último modelo; cualquier archivo que hayas editado y no esté marcado como generado se queda exactamente como lo escribiste. Así, tanto los cambios de tu modelo como tu código propio sobreviven a un redespliegue.
Eso es lo que hace que el directorio sea seguro a largo plazo: nuzur-cli deploy sirve tanto para "publica mi cambio de esquema" como para "publica mi cambio de código".
Usa git
Es un directorio normal — ponlo bajo control de versiones:
cd nuzur-mi-proyecto
git init
git add . && git commit -m "app generada inicial"
A partir de ahí cada redespliegue aparece como un diff revisable: ves exactamente qué actualizó el generador y qué cambiaste tú. Al crearlo por primera vez, deploy escribe un .gitignore y un .dockerignore por ti, que mantienen los secretos (config/prod.yaml, .env, *.key, *.pem) y los artefactos de compilación fuera de git y fuera de la imagen. Ninguno de los dos se sobrescribe si ya existe — una vez están en tu directorio, son tuyos.
La configuración de producción del servidor (
config/prod.yaml, que contiene la contraseña de la base de datos) se genera en el servidor y aquí está en el.gitignore. Subir este directorio a git no sube tus secretos.
Endpoints propios
Despliega con --custom y el generador emite una capa propia junto al CRUD generado, que la regeneración nunca sobrescribe:
| Archivo | Para qué sirve |
|---|---|
app/rest.go |
Rutas REST propias — sin generación de código |
app/grpc.go |
Sobrescribe o amplía los endpoints gRPC generados |
app/idl/proto/custom.proto |
RPCs de gRPC nuevos — complétalo y ejecuta app/idl/proto/gen.sh |
Edita, vuelve a ejecutar el mismo comando de deploy y tus endpoints se publican con el resto de la app.
Modos de despliegue
Aplicación completa (por defecto)
Genera la API, la compila en el servidor, autoaloja la base de datos y pone Caddy delante.
Solo base de datos
nuzur-cli deploy --host 203.0.113.10 --project mi-proyecto --db-only
Instala el motor de base de datos, empareja el agente, registra la conexión y aplica el esquema — pero no genera ninguna app, ni Docker, ni Caddy. Gestionas la base de datos íntegramente desde nuzur (Gestor de Datos, SQL Push, consultas). Como no se genera código, funciona con cualquier proyecto sin necesidad de una configuración de Go Code Gen, y el cortafuegos solo abre SSH.
Usar una base de datos existente
nuzur-cli deploy --host 203.0.113.10 --project mi-proyecto \
--db-dsn "postgres://usuario:clave@db.ejemplo.com:5432/midb?sslmode=require"
La app y el agente se conectan a una base de datos que ya tienes (local o remota, MySQL o Postgres) en lugar de autoalojar una. El motor se infiere del DSN: una URL postgres:// o postgresql:// significa Postgres, y cualquier otra cosa se interpreta como un DSN de MySQL (usuario:clave@tcp(host:puerto)/db?params). El DSN debe incluir el nombre de la base de datos.
Con --db-dsn, deploy omite la instalación de la base de datos, la creación del usuario y el cron de copias de seguridad — de eso se encarga tu proveedor. La base de datos nunca se elimina al destruir el despliegue, ni siquiera con --purge.
Desplegar desde una conexión de equipo guardada
nuzur-cli deploy --host 203.0.113.10 --project mi-proyecto --connection <uuid-de-conexion>
Si la base de datos destino ya está configurada como conexión de equipo en nuzur, despliega contra ella por uuid en lugar de volver a escribir un DSN. La CLI resuelve las credenciales por ti — la contraseña se descifra desde KMS en el servidor, así que nunca queda en el historial de tu shell. --connection y --db-dsn son mutuamente excluyentes, y la conexión debe pertenecer al equipo del proyecto. Todo lo demás se comporta exactamente igual que con --db-dsn.
Guardar una base de datos como conexión de equipo
Una base de datos externa (--db-dsn) registra únicamente una conexión asociada a ti, así que tus compañeros no pueden abrirla en el Gestor de Datos. Tras un despliegue con --db-dsn en una terminal interactiva, la CLI te ofrece guardarla como conexión de equipo. En ejecuciones automatizadas, decídelo de antemano con --save-connection o --no-save-connection — por defecto no se guarda.
Guardarla requiere que seas administrador del equipo, y nunca hace fallar el despliegue: si no se puede guardar, deploy avisa y continúa. Ten en cuenta que la ruta de equipo se conecta a la base de datos directamente desde nuzur cloud, así que su host y puerto deben ser accesibles desde allí — tu propia ruta vía el agente funciona igualmente. Esto no se ofrece en despliegues autoalojados (una base de datos en 127.0.0.1 no es accesible desde la nube) ni con --connection (ya es una conexión de equipo).
Elegir un motor
--db mysql (por defecto) o --db postgres selecciona el motor autoalojado. Para Postgres, --db-schema apunta a un espacio de nombres (por defecto public); en MySQL la base de datos es el esquema, así que el flag se ignora.
Al rol de Postgres autoalojado se le concede
CREATEDBpara que el diff de esquemas de nuzur pueda crear bases de datos temporales al aplicar cambios. Sigue siendo un rol sin superusuario y solo accesible desde localhost.
HTTPS y dominios
Pasa --domain api.ejemplo.com (con el DNS ya apuntando al servidor) y Caddy aprovisiona automáticamente un certificado real de Let's Encrypt, sirviendo el sitio en el 443.
Sin dominio, el proyecto recibe su propio puerto público asignado automáticamente en la IP del servidor por HTTP plano — empezando en 8443 e incrementando, para que varios proyectos convivan en una misma máquina sin dominios. Sirve para pruebas; usa un dominio para cualquier cosa seria.
Flags
nuzur-cli deploy
| Flag | Por defecto | Descripción |
|---|---|---|
--provider |
ssh |
Dónde desplegar: ssh, digitalocean o hetzner |
--host |
IP o hostname del servidor destino (obligatorio para ssh) |
|
--region |
Región/localización donde crear la VM (obligatorio en proveedores gestionados) | |
--size |
según proveedor | Tamaño/tipo de instancia |
--image |
Ubuntu 22.04 | Imagen del sistema operativo |
--ssh-key-name |
sube tu clave | Nombre/id de una clave SSH ya registrada en el proveedor |
--project, -p |
Nombre o UUID del proyecto | |
--version |
la última | Identificador o UUID de la versión del proyecto |
--user |
root |
Usuario SSH |
--ssh-key |
ssh-agent / ~/.ssh/config |
Ruta a una clave privada SSH |
--port |
22 |
Puerto SSH |
--sudo |
desactivado | Ejecuta el bootstrap con sudo (automático para usuarios no root) |
--domain |
Dominio para HTTPS automático vía Caddy | |
--identifier |
de la configuración del generador, o el nombre del proyecto | Nombra la BD, el servicio y la configuración en el servidor |
--db |
mysql |
Motor autoalojado: mysql o postgres |
--db-only |
desactivado | Solo base de datos y agente — sin app, Docker ni Caddy |
--db-dsn |
Usa una base de datos existente en lugar de autoalojar una | |
--connection |
Despliega contra una base de datos guardada como conexión de equipo (excluyente con --db-dsn) |
|
--save-connection / --no-save-connection |
pregunta en una TTY; si no, no | Guarda una base de datos externa (--db-dsn) como conexión de equipo |
--db-schema |
public |
Esquema de Postgres a usar (se ignora en MySQL) |
--api |
última configuración del proyecto | Superficie de API: rest, grpc o both |
--auth |
última configuración del proyecto | Middleware de autenticación: disabled, jwt o keycloak |
--custom |
desactivado | Genera la capa de aplicación personalizada para endpoints propios |
--source-dir |
./nuzur-<identifier> |
Directorio del código de la app — donde deploy genera y tú editas |
--config-file |
última usada | Ruta a una configuración JSON de Go Code Gen |
--cli-install-cmd |
releases de GitHub | Cambia cómo se instala la CLI de nuzur en el servidor |
--schema-push-extension |
sql-push-local |
Extensión usada para aplicar el esquema automáticamente |
--web-url |
nuzur cloud | URL base de la web de nuzur para el enlace al Gestor de Datos |
Elige --api según el consumidor: REST para clientes JS, web y navegador; gRPC para clientes Go y de backend.
nuzur-cli destroy <deployment-id>
| Flag | Descripción |
|---|---|
--purge |
También elimina la base de datos y el usuario de la app (irreversible; por defecto se conservan los datos) |
--keep-vm |
En un despliegue con proveedor gestionado, conserva la VM en lugar de eliminarla |
--skip-server |
Solo revoca el agente y borra el estado local; no toca el servidor |
--user / --port / --ssh-key |
Sobrescribe los datos SSH registrados del despliegue |
--sudo |
Ejecuta la limpieza con sudo (automático para usuarios no root) |
Después de desplegar
Gestiona tus datos
La CLI imprime un enlace directo al Gestor de Datos que abre la base de datos desplegada con la conexión preseleccionada. El despliegue también aparece en Despliegues en la aplicación web de nuzur, mostrando su configuración (host, motor, modo, puertos, URLs, versión del proyecto, versión de la CLI) y su estado en vivo, consultado bajo demanda al agente: si el servicio de la app está activo, el estado del contenedor y su número de reinicios, y si la base de datos es accesible. Si el agente está desconectado, la configuración se sigue mostrando y el estado aparece como desconocido.
Publica cambios
Vuelve a ejecutar el mismo comando deploy para desplegar una nueva versión — de tu modelo, de tu código o de ambos. Regenera el código de tu app en su sitio (actualizando los archivos generados y conservando tus cambios), reutiliza el agente y la conexión del servidor, recompila la imagen, reinicia el servicio y aplica el diff del esquema. Las contraseñas, las claves de firma JWT y los puertos se conservan por proyecto, así que un redespliegue nunca los cambia por debajo de una app en funcionamiento.
También puedes enviar cambios de esquema desde nuzur en cualquier momento con SQL Push (local) o una solicitud de cambio — el agente los aplica a la base de datos desplegada.
Lista tus despliegues
nuzur-cli deploy list
Los registros de despliegue viven en tu máquina, en ~/.config/nuzur/deployments/<id>.json, y son los que lee destroy. Si despliegas desde otro portátil, esa máquina no conocerá el despliegue anterior — la pantalla de Despliegues de nuzur es la vista compartida.
Varios proyectos en un servidor
Un servidor puede alojar varios proyectos. Cada uno tiene su propia base de datos, directorio de configuración, servicio de systemd, contenedor, sitio de Caddy y puerto público — todos namespaced por identificador — mientras la máquina mantiene un único agente compartido.
Desplegar un segundo proyecto reutiliza ese agente en lugar de emparejar uno nuevo. destroy elimina solo los artefactos de ese proyecto; el agente compartido sobrevive hasta que destruyes el último proyecto del servidor.
Desplegar un proyecto distinto bajo un identificador que ya se usa en ese host se rechaza, porque colisionarían en el nombre de base de datos derivado. Usa un --identifier distinto.
Destruir un despliegue
nuzur-cli destroy mi-proyecto-a1b2c3d4
Esto elimina el servicio, el contenedor, la imagen, la configuración, el fragmento de Caddy y el cron de copias del proyecto; quita su conexión del agente; marca el despliegue como destruido en nuzur (se conserva como histórico); y borra el registro local. Tu base de datos se conserva salvo que pases --purge, y una base de datos externa (--db-dsn) nunca se elimina.
En un despliegue con proveedor gestionado, destroy también elimina la VM que creó — pero solo cuando es el último proyecto de esa máquina, así que destruir uno de varios proyectos nunca deja a los demás sin servidor. Pasa --keep-vm para conservarla. Una máquina propia (BYO-SSH) nunca se toca; es tuya.
Si el servidor no está accesible, la limpieza avisa y aun así limpia el lado de nuzur — vuelve a ejecutarlo más tarde, o usa --skip-server. Si falla la eliminación de la VM, la CLI te indica el id de la instancia para que la borres y detengas el cobro.
Notas de seguridad
- Tu clave SSH y las credenciales de tu nube nunca salen de tu máquina: deploy se ejecuta en el cliente, y los proveedores gestionados delegan en tu propia CLI ya autenticada en lugar de pedirle a nuzur que guarde un token.
- La contraseña de la base de datos se genera en el servidor y se guarda con permisos
0600; nuzur solo ve metadatos de la conexión, nunca la contraseña. - El token de aprovisionamiento usado para el emparejamiento sin interacción es de un solo uso y caduca en 15 minutos, y está limitado a tu usuario y proyecto.
- La base de datos escucha en
127.0.0.1; el agente es solo saliente;ufwabre únicamente SSH y los puertos de la puerta de entrada. - La aplicación se ejecuta con un usuario de base de datos de privilegios mínimos.