wandres.dev
ISOLATES · no son contenedores

V8 isolates: aislamiento ligero

El núcleo del modelo de Cloudflare: el isolate de V8 como unidad de aislamiento dentro de un proceso ya caliente. Definimos con precisión qué es un isolate frente a un context, seguimos cómo la frontera de confianza se muda del kernel al runtime del lenguaje, vemos cómo workerd hospeda a miles de inquilinos en un mismo proceso, y aceptamos el compromiso que todo ello impone: ejecutar solo lo que V8 ejecuta.

⏱ 17 min

Si el cold start es el impuesto de aislar en el sistema operativo, el isolate es la idea que lo elimina cambiando de qué está hecha la frontera. Un isolate no es una máquina pequeña ni un contenedor liviano: es la unidad de aislamiento del motor V8, el mismo que ejecuta JavaScript en Chrome y en Node. Cloudflare tomó esa pieza, pensada para separar pestañas del navegador, y la convirtió en la base para separar inquilinos en el edge.

🎯 Al terminar esta lección sabrás
  • Definir con precisión qué es un isolate de V8 y en qué se diferencia de un context.
  • Entender por qué la frontera de aislamiento pasa del kernel al runtime del lenguaje.
  • Ver cómo miles de inquilinos conviven en un único proceso ya caliente.
  • Comprender el compromiso: seguridad por memory-safety a cambio de ejecutar solo lo que V8 ejecuta.

Qué es exactamente un isolate

V8 es el motor que compila y ejecuta JavaScript y WebAssembly. Un isolate es su unidad de aislamiento: una instancia independiente del motor, de un solo hilo, con su propio heap, su propio recolector de basura y su propia copia de los objetos incorporados del lenguaje.

Dos isolates son universos separados: no comparten punteros, no comparten memoria y no pueden alcanzarse el uno al otro. Cuando en un isolate creas un objeto, ese objeto vive en un heap que ningún otro isolate puede direccionar.

Conviene no confundir dos niveles. El isolate es el universo aislado; dentro de él puede haber uno o varios context, que son ámbitos globales distintos que sí comparten el heap.

Chrome usa esa jerarquía para separar pestañas y marcos, herencia directa de su proyecto de aislamiento de sitios. Cloudflare simplifica el modelo: un Worker, un isolate. Tu código es el único habitante de su universo.

// Un Worker es un modulo. Su ambito global vive dentro de un isolate.
let contador = 0; // vive en el heap del isolate, no lo ve ningun otro Worker

export default {
  async fetch(request, env, ctx) {
    contador++; // persiste entre peticiones del MISMO isolate
    return new Response(`peticiones en este isolate: ${contador}`);
  },
};

Fíjate en que la variable vive entre peticiones, pero solo dentro de este isolate. Otro isolate del mismo Worker, en otra máquina del edge, tiene su propio contador empezando en cero.

No hay un valor global compartido entre todos: hay tantos como isolates activos haya en la red en ese instante.

La frontera se muda del kernel al lenguaje

Este es el giro conceptual. En el modelo de contenedores, lo que impide que el inquilino A lea la memoria del inquilino B es el kernel o el hipervisor: hardware y tablas de páginas.

En el modelo de isolates, lo que lo impide es que JavaScript y WebAssembly son lenguajes con memoria segura: no existe la aritmética de punteros, no puedes fabricar una dirección arbitraria ni leer fuera de las estructuras que el motor te entrega.

La garantía deja de venir del sistema operativo y pasa a venir del propio runtime del lenguaje.

Es un cambio de responsable, no una relajación: la carga de demostrar el aislamiento se traslada del kernel al motor, y ese motor es, no por casualidad, uno de los componentes de software más auditados del mundo.

flowchart TB
P[Un solo proceso ya caliente workerd] --> I1[Isolate tenant A]
P --> I2[Isolate tenant B]
P --> I3[Isolate tenant C]
P --> IN[Isolate tenant N]
I1 --> H1[Heap propio + GC]
I2 --> H2[Heap propio + GC]
I3 --> H3[Heap propio + GC]
style I1 fill:#a6e3a1,color:#11111b
style I2 fill:#89b4fa,color:#11111b
style I3 fill:#cba6f7,color:#11111b
style IN fill:#fab387,color:#11111b

Compara este diagrama con el del nivel anterior. Allí cada inquilino arrastraba su propio kernel invitado y su propio runtime en columnas paralelas.

Aquí hay un único proceso, ya arrancado y caliente, que hospeda a todos los inquilinos como isolates hermanos. La frontera entre ellos no es una VM: es el límite del heap que V8 garantiza por construcción del lenguaje.

Miles de inquilinos en un runtime

Que la frontera sea ligera cambia la economía por completo. Crear un isolate cuesta poco: reservar un heap y preparar su context, del orden de pocos milisegundos, y aún menos gracias a los snapshots de V8, que dejan los objetos incorporados precargados y listos para clonarse.

Un isolate ocioso no arrastra un sistema operativo: ocupa apenas unos megabytes de su heap. La consecuencia es directa:

Arranque en milisegundos

No hay microVM que iniciar ni imagen que montar. Se crea un heap, se carga el script y ya está. El arranque baja de cientos de milisegundos a unos pocos.

🏘️

Densidad altísima

Miles de isolates caben en un proceso porque cada uno cuesta memoria de heap, no un runtime entero. Un servidor hospeda a muchísimos más inquilinos.

🔒

Aislamiento por lenguaje

Sin aritmética de punteros no hay forma, dentro de las reglas del lenguaje, de leer el heap del vecino. La seguridad la impone V8, no el hipervisor.

♻️

Ciclo de vida barato

Crear y destruir isolates es tan barato que el runtime puede evictar los inactivos y recrearlos bajo demanda sin penalizar al usuario.

El runtime que orquesta todo esto es workerd, el código abierto que Cloudflare ejecuta en producción. Un proceso workerd es un anfitrión que crea, alimenta y destruye isolates de muchos clientes distintos, todos compartiendo el mismo motor V8 ya compilado en memoria.

El código máquina del propio motor, generado por su compilador JIT, se comparte entre inquilinos porque es idéntico para todos; lo que no se comparte jamás es el heap de datos de cada uno.

Es la diferencia entre compartir la imprenta y compartir el manuscrito: lo primero es eficiente, lo segundo sería una brecha de seguridad.

Y como cada isolate es tan barato, el runtime los trata como recursos desechables: crea uno cuando llega tráfico a un Worker inactivo y lo descarta cuando deja de hacer falta. La densidad no es solo cuántos caben, sino con qué agilidad nacen y mueren.

El compromiso que hay que aceptar

Ninguna arquitectura es gratis. Mover la frontera al lenguaje impone una condición: solo puedes ejecutar lo que V8 ejecuta, es decir, JavaScript y WebAssembly.

No corres un binario nativo arbitrario, no cargas una extensión en C que haga llamadas al sistema a su antojo, y cada isolate es de un solo hilo.

Ese es el peaje, y es aceptable para la enorme mayoría de las cargas web: renuncias a ejecutar cualquier cosa a cambio de un aislamiento que arranca en cero y escala a miles de inquilinos.

El hilo único, además, apenas es un límite para el trabajo web típico, que pasa la vida esperando red y base de datos, no saturando la CPU. Un isolate cede el hilo en cada await, así que uno solo basta para atender mucha concurrencia de entrada y salida.

💡
WebAssembly ensancha la puerta

La restricción a lo que V8 ejecuta no te encierra en JavaScript. Lenguajes como Rust, C o Go, compilados a WebAssembly, corren dentro del mismo isolate seguro y a velocidad casi nativa. Así llevas librerías maduras al edge —criptografía, códecs, parsers— sin renunciar al modelo de aislamiento por lenguaje.

El isolate redefine qué es la unidad de cómputo en la nube

La lección profunda no es que los isolates sean rápidos, sino que reubican la frontera de confianza. Durante toda la era de la virtualización, la nube asumió un axioma: para aislar código que no confías, necesitas hardware o un kernel que lo separe. El isolate niega ese axioma. Afirma que un lenguaje con memoria segura, ejecutado por un motor endurecido, puede ser una frontera de aislamiento legítima entre inquilinos que no se conocen. Al aceptar esa premisa, todo cambia de escala: la unidad de cómputo deja de ser una máquina, con su arranque y su copia de sistema operativo, y pasa a ser un heap con un context, que se crea en milisegundos y se destruye sin coste. Por eso la frase correcta es “isolates, no contenedores”, y no “contenedores más ligeros”: no es el mismo objeto optimizado, es un objeto de otra naturaleza. Un contenedor comparte un kernel pero mantiene la ilusión de una máquina propia; un isolate ni siquiera pretende ser una máquina, es un compartimento del lenguaje. Interiorizar esto es dejar de preguntar “cuánto tarda en arrancar mi máquina” y empezar a preguntar “cuánto trabajo real hace mi código”, que es justo la pregunta que el modelo de coste de Cloudflare premia. El resto del nivel se apoya entero sobre esta idea.

⚔️ Piensa en isolates, no en máquinas
  1. Explica con tus palabras la diferencia entre un isolate y un context de V8, y por qué Cloudflare usa un isolate por Worker.
  2. Toma el diagrama de este nivel y el del anterior: señala exactamente qué columna del modelo de contenedores desaparece con los isolates.
  3. Razona por qué una variable global de tu Worker persiste entre peticiones pero no puede usarse como fuente de verdad compartida entre inquilinos.
  4. Argumenta el compromiso: enumera dos cosas que ganas con el aislamiento por lenguaje y una que pierdes frente a un contenedor.
  5. Explica por qué el código máquina del motor puede compartirse entre inquilinos pero el heap de datos no, usando la metáfora de la imprenta y el manuscrito.
  6. Piensa en una librería nativa que te gustaría usar en el edge y razona si podrías llevarla vía WebAssembly o no.