Un notebook viejo de oficina reemplazó US$292 al mes de VMs en la nube


En agosto de 2026 un notebook ASUS de oficina del 2020 salió de un cajón donde llevaba un año sin hacer nada. Intel i5-10300H, 4 núcleos / 8 hilos, 32 GB de RAM, un NVMe de 1 TB. Debian 13 entró desde la imagen netinst el 30 de agosto. Dos días después una de sus VMs construía y desplegaba un sitio en producción en unos 100 segundos, un trabajo que en un VPS tomaba entre 7 y 12 minutos.

TL;DR

  • La misma capacidad cuesta ~US$292 al mes en DigitalOcean. El notebook, ~US$3 de luz.
  • Sin Proxmox. Debian + KVM pelado: menos superficie de ataque, una sola cadena de confianza.
  • Nada corre en el host. Toda carga vive en una VM; las no confiables, en gVisor dentro de la VM.
  • Las VMs se apagan solas cuando están ociosas y liberan 16 GB de RAM.

Cualquier asistente de IA te escribe un tutorial de KVM en diez segundos. Este post es la parte que no puede escribir: las decisiones, los números y lo que me mordió.

La cuenta

El notebook corre dos VMs: build (8 vCPU, 16 GB) y scraper (4 vCPU, 4 GB, con su propio Postgres). El droplet único más parecido en DigitalOcean, precios de agosto de 2026:

  • General Purpose 8 vCPU / 32 GB: US$272 al mes
  • Block storage: ~US$20 al mes
  • Si con 4 vCPU alcanza, Memory-Optimized tiene los mismos 32 GB por US$168
NubeNotebook
Mensual~US$292~US$2-3 de luz (estimado, ~20 W promedio)
Una vez0kit 32 GB + NVMe 1 TB, precio de mercado en Chile ~CLP 400-500 mil (~US$430-540)

El upgrade se paga solo en menos de dos meses.

  • Toda empresa tiene dos o tres notebooks así en un armario.
  • El aprovisionamiento es un script bash idempotente de 9 fases por SSH. Aprovisionaría el tuyo igual.
  • Bonus: un notebook es su propia UPS. La batería está limitada al 60% vía charge_control_end_threshold.

Por qué no Proxmox

Todo tutorial, y toda respuesta de IA, parte con “instala Proxmox”. El plan de julio también. Lo que quedó en la máquina es Debian 13 + KVM/libvirt + ZFS pelado, con Cockpit en la LAN como consola.

Razón personal. El repo enterprise pagado nunca me cerró. Debian jamás me va a cobrar, y el repo no-subscription de Proxmox es, por definición de ellos, el menos probado.

Razón técnica. La web UI es muy linda, pero mira lo que corre detrás:

  • pveproxy: un servidor HTTP en Perl en el puerto 8006
  • pvedaemon: corriendo como root
  • Apagarlos no está soportado. Lo más que puedes hacer es amarrarlos a localhost.
  • Libvirt, en cambio, escucha en un socket UNIX. Cero TCP.
  • Los CVE de escape de guest de QEMU pegan igual en los dos. Proxmox suma un plano de gestión encima.

Para ser justo, en la misma evaluación Proxmox ganó en operabilidad, 9 contra 6,5: templates, clones y snapshots son de primera clase allá y scripts acá. De todos modos prefiero manejar VMs por SSH antes que desde un browser.

Nada corre en el host

El script de aprovisionamiento tenía una fase que instalaba Docker con gVisor en el host. Corrió el 30 de agosto. Esa misma tarde la purgué: Docker, runsc, las fuentes apt, el bridge docker0.

Si tienes un hipervisor, correr cualquier cosa fuera de una VM es una locura.

El host es KVM, ZFS, Cockpit y sshd. Nada más.

Docker vive dentro de las VMs, y gVisor sigue aplicando ahí sin virtualización anidada. El scraper, la carga no confiable, corre como container runsc dentro de su VM:

  • filesystem de solo lectura
  • cap_drop: ALL
  • usuario no-root

Tres capas: gVisor → KVM → host.

gVisor me cobró dos veces:

  • DNS. El resolver embebido de Docker es inalcanzable bajo gVisor en redes custom de Compose. Solución: network_mode: bridge + DNS externo.
  • musl. El binding nativo de Vite costaba 50 segundos por build hasta que la imagen pasó de alpine a glibc.

VMs que se apagan solas

Las dos VMs son on-demand. Un cron del host corre cada 15 minutos y apaga una VM tras dos strikes de ociosidad. Ociosa significa las dos señales:

  • proceso QEMU bajo el 5% de un núcleo, y
  • menos de 512 KB de red en el ciclo.

Por qué las dos: con CPU sola mataría un scrape con rate-limit a mitad de corrida. CPU cero, megabytes moviéndose. Cualquier anomalía significa no apagar. El ruido de una VM ociosa es de 22-23 KB por ciclo; el margen sobra.

Resultado: de 24 GB usados a 8 GB con las dos abajo.

No hay otra forma de recuperar esa RAM. drop_caches en el guest no baja el RSS del host ni un byte; solo balloon o shutdown. El script de deploy prende la VM de build solo (+17 s). Una VM apagada es normal, no una falla.

Push-only

  • La VM de build sube con una llave restrict + rrsync -wo: solo escritura en un directorio. Probado: sin shell, sin lecturas, sin escape.
  • La salida de la VM es una allowlist de nftables.
  • Nunca baja nada de producción. Si prod está comprometida, traer un archivo de ahí infecta la máquina que guarda todas las demás llaves.

IP pública por US$4 (todavía no armado)

Detrás de un ISP residencial, el plan para cualquier cosa pública:

  1. El droplet más barato (US$4).
  2. Un túnel WireGuard desde el notebook hacia él.
  3. Un reverse proxy en el droplet hacia la LAN.

IP fija, TLS en el droplet, la IP de la casa nunca en DNS.

Límites honestos: no hay región de DigitalOcean en Chile, así que cada request salta a EE.UU.; el uptime es luz e internet residencial. Sirve para builds, scrapers, crons y staging. No para producción sensible a latencia. Todavía no armado; las dos VMs no lo necesitan.

Lo que me mordió

GotchaSolución
La cloud image de Debian 13 es solo EFI; con SeaBIOS entra en boot-loopvirt-install --boot uefi
La cloud image viene sin llaves de host SSHvirt-customize --run-command "ssh-keygen -A"
cloud-init nunca corrió con seed NoCloud (sigue sin explicación)virt-customize hace su pega
Secure Boot + ZFS: el módulo DKMS no carga sin firmarfirmar con la llave MOK de DKMS, inscribirla una vez con mokutil
La VM y la estación comparten IP pública; un login SSH fallido desde la VM gatilla fail2ban para los dosparar al primer Permission denied

Lo que la IA me dio, y lo que no

Claude Code me dio: los comandos, la unidad systemd de la batería, los flags de ZFS, la sintaxis de nftables, el cron.

Lo que no: nada en el host, la señal doble de ociosidad, la jaula de solo escritura, push-only.

Eso sale de tener algo que perder. Lo que miro todos los días es btop por SSH.