Instalar CRM-LAB en un VPS con un agente de IA
Vas a llevar un servidor Ubuntu vacío hasta un CRM con HTTPS, la primera cuenta de administrador y copias cifradas cada seis horas. El agente ejecuta los comandos por SSH desde tu computadora; vos revisás y aprobás cada paso.
Narrado en español, con subtítulos. Los datos de ejemplo (IP, dominio, inmobiliaria) se reemplazan por los tuyos.
Comandos del video
Los mismos pasos del video, con cada comando listo para copiar.
1Antes de empezar
Necesitás:
- Un VPS nuevo con Ubuntu 24.04 o 26.04 LTS y acceso root por SSH. El piloto funciona en 1 vCPU, 4 GB de RAM y 50 GB de disco.
- Un dominio o subdominio para el CRM, por ejemplo
crm.tuinmobiliaria.com.ar, con acceso a su panel DNS. - Un agente de IA de programación instalado en tu computadora: Claude Code, Codex u otro que pueda ejecutar comandos en terminal con tu aprobación.
- Un correo reservado para la cuenta de administrador.
secrets/. Y no le permitas contratar servicios o generar cargos: cualquier gasto lo decidís vos.2Clave SSH y repositorio
Si todavía no tenés una clave SSH, generala en tu computadora. La parte privada nunca sale de tu equipo; al servidor solo se copia el archivo .pub.
ssh-keygen -t ed25519 -C "crm-lab"Cloná el repositorio. El agente va a leer los scripts desde acá antes de copiarlos al VPS.
git clone https://github.com/javierdicamillo/CRM-LAB.git
cd CRM-LAB3Abrir el agente con el contexto correcto
Iniciá el agente dentro de la carpeta del repositorio (por ejemplo, claude o codex) y pegale este mensaje inicial. Reemplazá la IP, el dominio y el nombre de tu inmobiliaria.
Vamos a instalar CRM-LAB en un VPS nuevo siguiendo infra/README.md y docs/09-entrega-crm-mvp.md de este repositorio.
Datos:
- IP del VPS: 203.0.113.10
- Dominio del CRM: crm.tuinmobiliaria.com.ar
- Inmobiliaria: "Tu Inmobiliaria"
- Mi clave pública: ~/.ssh/id_ed25519.pub
Reglas:
1. Antes de cada comando que modifique el servidor, explicame qué hace y esperá mi aprobación.
2. Leé cada script antes de ejecutarlo y avisame si algo no coincide con un Ubuntu nuevo.
3. Nunca imprimas ni leas en voz alta el contenido de /opt/crm-lab/secrets ni del .env.
4. No contrates servicios ni generes cargos.
5. No publiques el puerto 5432 ni cambies el Compose sin mostrarme el diff.
Empezá inspeccionando el servidor sin modificar nada.El resto del tutorial muestra los comandos que el agente debería proponerte. Te sirven para controlar lo que hace, o para seguir a mano si preferís.
4Inspeccionar el VPS
Primero, solo lectura: sistema, disco, memoria y servicios que ya estén escuchando.
ssh root@203.0.113.10 'cat /etc/os-release; df -h /; free -h; ss -tlnp'Al conectarte por primera vez, compará la huella de la clave del servidor con la que muestra la consola web de tu proveedor. Si hay otros servicios en los puertos 80 o 443, frená y revisá antes de seguir.
5Preparar el servidor y proteger el acceso
bootstrap-ubuntu.sh instala Docker, UFW, restic y actualizaciones automáticas de seguridad, y crea el usuario crmadmin con tu clave pública.
scp infra/scripts/bootstrap-ubuntu.sh infra/scripts/harden-ssh.sh ~/.ssh/id_ed25519.pub root@203.0.113.10:/root/
ssh root@203.0.113.10 'bash /root/bootstrap-ubuntu.sh /root/id_ed25519.pub'Abrí una conexión nueva como crmadmin y comprobá que sudo funciona sin contraseña:
ssh crmadmin@203.0.113.10 'sudo -n id'Solo si eso respondió uid=0(root), deshabilitá el acceso por contraseña y activá el firewall:
ssh crmadmin@203.0.113.10 'sudo bash /root/harden-ssh.sh'crmadmin.6Dominio y DNS
En el panel DNS de tu dominio creá:
| Tipo | Nombre | Valor |
|---|---|---|
| A | crm (o @) | IPv4 del VPS |
| CNAME | www.crm (o www) | el dominio del CRM |
Borrá cualquier registro AAAA que apunte a otro servidor y no toques MX ni TXT del correo. Verificá desde tu computadora:
dig +short crm.tuinmobiliaria.com.ar A
dig +short crm.tuinmobiliaria.com.ar AAAA7Levantar PostgreSQL y Caddy
Copiá al VPS los archivos de Compose, Caddy y los scripts de despliegue y respaldo:
ssh crmadmin@203.0.113.10 'mkdir -p ~/crm-lab-setup'
scp infra/crm-base/{compose.yaml,compose.app.yaml,Caddyfile,Caddyfile.app,crm-lab-backup.service,crm-lab-backup.timer} \
infra/scripts/{deploy-base.sh,prepare-app.sh,backup-app.sh,verify-app-backup.sh} \
crmadmin@203.0.113.10:~/crm-lab-setup/En el VPS, instalá la base. El script genera la contraseña de PostgreSQL en el servidor y fija las imágenes oficiales por digest:
sudo install -d -m 755 /opt/crm-lab
sudo cp ~/crm-lab-setup/{compose.yaml,compose.app.yaml,Caddyfile} /opt/crm-lab/
sudo bash ~/crm-lab-setup/deploy-base.shAl terminar, docker compose ps debe mostrar db como healthy. PostgreSQL usa una red interna y no publica puertos.
8Construir y preparar CRM-LAB
La imagen se construye en el propio VPS desde la raíz del repositorio. No hace falta un registry pago.
sudo apt-get install -y git
sudo git clone https://github.com/javierdicamillo/CRM-LAB.git /opt/crm-lab/source
cd /opt/crm-lab/source
sudo docker build -f CRM-LAB/Dockerfile -t crm-lab:0.2.0 .Los archivos de despliegue todavía referencian la etiqueta del piloto. Pedile al agente que la actualice y te muestre el diff:
sudo sed -i 's/crm-lab:0.1.0/crm-lab:0.2.0/' /opt/crm-lab/compose.app.yaml
sed -i 's/crm-lab:0.1.0/crm-lab:0.2.0/' ~/crm-lab-setup/prepare-app.sh
grep -n 'crm-lab:' /opt/crm-lab/compose.app.yaml ~/crm-lab-setup/prepare-app.shGenerá los secretos de la aplicación y creá las bases de producción y de prueba, con un rol crm_app sin privilegios de superusuario. No se imprime ningún secreto:
sudo bash ~/crm-lab-setup/prepare-app.sh crm.tuinmobiliaria.com.ar "Tu Inmobiliaria" admin@tuinmobiliaria.com.ar9Activar HTTPS y crear la primera cuenta
Reemplazá el Caddyfile temporal por el de la aplicación, validalo y levantá app y worker:
cd /opt/crm-lab
echo 'CRM_DOMAIN=crm.tuinmobiliaria.com.ar' | sudo tee -a .env
sudo cp Caddyfile Caddyfile.http-backup
sudo cp ~/crm-lab-setup/Caddyfile.app Caddyfile
sudo docker compose run --rm --no-deps caddy caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
sudo docker compose -f compose.yaml -f compose.app.yaml up -d --waitCaddy obtiene el certificado de Let's Encrypt en cuanto el DNS apunta al VPS. Comprobalo desde afuera:
curl -fsS https://crm.tuinmobiliaria.com.ar/healthz
curl -s -o /dev/null -w '%{http_code}\n' https://crm.tuinmobiliaria.com.ar/api/stateEl primero responde 200 y el segundo, 401 (sin sesión). Para activar la cuenta de administrador necesitás el token de arranque. Leelo vos, en tu propia terminal, no a través del agente:
ssh -t crmadmin@203.0.113.10 "sudo grep BOOTSTRAP_TOKEN /opt/crm-lab/secrets/app.env"Abrí https://crm.tuinmobiliaria.com.ar/#activar=TOKEN, completá nombre, el correo reservado y una contraseña de al menos 12 caracteres. El enlace deja de funcionar una vez creado el primer administrador.
10Programar copias y verificar
Instalá el respaldo cifrado con restic: dump de PostgreSQL, fotos, configuración y secretos, cada seis horas.
sudo install -m 750 ~/crm-lab-setup/backup-app.sh /usr/local/sbin/crm-lab-backup
sudo cp ~/crm-lab-setup/crm-lab-backup.{service,timer} /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now crm-lab-backup.timer
sudo systemctl start crm-lab-backup.service
sudo bash ~/crm-lab-setup/verify-app-backup.shLa verificación restaura la copia en una base temporal, comprueba los archivos y elimina solo sus temporales.
/opt/crm-lab/secrets/restic_password) en un lugar privado fuera del servidor.Lista final:
- Entrás por SSH como
crmadmincon clave; el acceso por contraseña está deshabilitado. https://tu dominio responde con certificado válido ywwwredirige al principal.- Iniciaste sesión, creaste una propiedad de prueba y la borraste.
systemctl list-timers crm-lab-backup.timermuestra la próxima copia.- La restauración de prueba terminó sin errores.