---
id: e62d78ef5850c982dbddc9d3ff9134ae
author: Lemon Mint
title: 'gvx: Ejecución de Linux Containers, Docker y Kubernetes en un único proceso sin privilegios'
description: Descripción técnica general de gvx, un entorno de ejecución de contenedores y SDK de Go multiplataforma y reforzado en seguridad, construido sobre el núcleo de espacio de usuario gVisor, que ejecuta de forma segura contenedores Linux, Docker-in-Docker y clústeres de Kubernetes de alta disponibilidad multinodo en un único proceso sin privilegios en Windows, macOS y Linux.
language: es
date: 2026-11-06T12:02:56.008516536Z
path: /blog/posts/gvx-cross-platform-linux-containers-z48916d01
go_package: z.gfire.dev/gvx
go_repourl: https://github.com/gfire-sigs/gvx.git
category: lowlevel
---

## Introducción: contenedores reforzados en seguridad sin root ni daemons del host

Ejecutar contenedores Linux, `docker build` o Kubernetes dentro de una máquina de desarrollo, un runner de CI o un nodo de nube multiinquilino suele imponer un compromiso de seguridad incómodo:

- En **Linux**, los runtimes OCI estándar (`runc`, `crun`, `runsc`) requieren privilegios de `root`, exposición del host en `/var/run/docker.sock`, `--privileged` (`CAP_SYS_ADMIN`) o espacios de nombres de usuario (`unshare(CLONE_NEWUSER)`) que amplían la superficie de ataque del kernel del host.
- En **Windows** y **macOS**, los motores de contenedores (`Docker Desktop`, `Podman Machine`, `Lima`, `OrbStack`) arrancan un kernel completo de máquina virtual Linux dentro de WSL2, Hyper-V o `Virtualization.framework` y ejecutan un daemon persistente en segundo plano dentro de esa VM.

**[gvx](https://github.com/gfire-sigs/gvx)** (la CLI `rungv` y el SDK de Go `z.gfire.dev/gvx/pkg/gvx`) proporciona una **alternativa segura y de privilegio cero**. Construido como una bifurcación del kernel Linux en espacio de usuario `Sentry` y de la pila de red en espacio de usuario `netstack` de **gVisor** de Google, con seguimiento de cambios upstream, `gvx` ejecuta binarios ELF64 `linux/amd64` y `linux/arm64` sin modificar y contenedores OCI directamente dentro de **un único proceso del host sin privilegios y autobloqueado** en Windows, macOS y Linux.

> **Garantía de seguridad (defensa en profundidad en tres anillos)**: `gvx` nunca debilita los límites de seguridad del sistema operativo del host, nunca instala drivers de kernel y nunca requiere `root`, `sudo`, `--privileged` en el host ni `CLONE_NEWUSER`. Antes de ejecutar una sola instrucción del invitado, `gvx` bloquea su propio proceso del host mediante Restricted Tokens y Job Objects de Windows, Seatbelt de macOS (`deny default`) o `PR_SET_NO_NEW_PRIVS` de Linux junto con `seccomp` BPF. Todas las llamadas al sistema del invitado, los privilegios virtuales `UID 0`, los montajes (`VFS2`) y las tablas de red (`netstack`) quedan estrictamente confinados dentro del kernel `Sentry` de Go, seguro frente a fallos de memoria.

| Runtime                    | Sistema operativo del host                            |   Arranque en frío | Huella de memoria | Requiere admin o `sudo` | Daemon de VM en segundo plano | Ejecución directa de imagen OCI |
| :------------------------- | :---------------------------------------------------- | -----------------: | ----------------: | :---------------------- | :---------------------------- | :------------------------------ |
| **`gvx` (`whpx` / `hvf`)** | Windows (`amd64`, `arm64`) y macOS (`arm64`)          |          `~20 ms` |        `~30 MiB` | No                      | No                            | Sí                              |
| **`gvx` (`systrap`)**      | Windows (`amd64`, `arm64`) y Linux (`amd64`, `arm64`) |    `~25 a 65 ms` |        `~31 MiB` | No                      | No                            | Sí                              |
| **WSL2 / Docker Desktop**  | Windows y macOS                                       |  `800 a 2500 ms` |      `> 512 MiB` | Sí (configuración inicial) | Sí                            | Requiere daemon                 |
| **QEMU (sistema completo)** | Windows y macOS                                      | `1500 a 4000 ms` |      `> 256 MiB` | No                      | No                            | Requiere imagen de disco        |

En Windows o macOS no arranca ningún kernel de máquina virtual Linux. No se ejecuta ningún daemon en segundo plano en el host. No se requieren privilegios de `root`, `sudo`, espacios de nombres de usuario (`CLONE_NEWUSER`), `mount(2)`, drivers `TUN`/`TAP` ni `CAP_NET_ADMIN` en el sistema operativo del host. Dentro de ese proceso sin privilegios, `gvx` ejecuta **Docker-in-Docker (`docker:dind`)**, `docker build`, `docker compose` y **Kubernetes de alta disponibilidad con múltiples nodos (`k3s` con `etcd` embebido y el CNI `Calico`)**.

---

## Ciclo de vida del contenedor y bloqueo del host en tres anillos

Cada sandbox de `gvx` se ejecuta dentro de un único proceso del host sin privilegios y aplica un ciclo de vida de tres fases: preabrir todos los descriptores de archivo y sockets del host necesarios, bloquear el proceso del sistema operativo del host mediante `pkg/hostlockdown` y entrar en el bucle de ejecución `Sentry` dentro del proceso.

![Ciclo de vida del contenedor gvx y autobloqueo del host](/assets/images/gvx/gvx-lifecycle-lockdown.png)

Antes de transferir el control a la primera instrucción del invitado, `gvx` preabre sus descriptores de capas de imagen `.erofs` en caché, sus descriptores de archivos de volumen `.ext4`, el handle `*os.Root` de `/workspace` y los sockets de red del host, y después invoca `hostlockdown.Apply()`:

1. **Windows NT (`lockdown_windows.go`)**: Llama a `AdjustTokenPrivileges` con `SE_PRIVILEGE_REMOVED` para eliminar privilegios in situ, vincula un token de suplantación restringido `DISABLE_MAX_PRIVILEGE` con integridad Baja (`S-1-16-4096`), asigna el proceso a un Job Object anónimo con `JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 1` (bloqueando la creación de cualquier proceso hijo) y habilita `DisallowWin32kSystemCalls = 1` mediante `SetProcessMitigationPolicy`.
2. **macOS XNU (`lockdown_darwin.go`)**: Establece `RLIMIT_NPROC = 0` y aplica un perfil Seatbelt (`sandbox_init_with_parameters`) que parte de `(version 1)(deny default)` y bloquea `process-fork`, `process-exec*` y todas las nuevas aperturas de archivos del host.
3. **Linux (`lockdown_linux.go` + `pkg/gvx/syscallfilter_linux.go`)**: Establece `PR_SET_NO_NEW_PRIVS` e instala la lista blanca BPF `seccomp` de Sentry en todos los hilos (`SECCOMP_FILTER_FLAG_TSYNC`), con reglas `EPERM` explícitas para `execve`, `execveat`, `clone`, `openat` y `socket`.

---

## Interceptación de llamadas al sistema multiplataforma y paginación de 4 KB (`pkg/sentry/platform`)

Los binarios ELF64 de Linux asumen una granularidad de página de memoria de 4 KB (`0x1000`) para los mapeos de segmentos con `mmap(MAP_FIXED)`. Sin embargo, `VirtualAlloc` y `MapViewOfFile3` de Windows imponen una alineación de 64 KB (`0x10000`), y macOS en Apple Silicon (`darwin/arm64`) usa páginas del host de 16 KB (`0x4000`). Además, cada sistema operativo requiere un mecanismo distinto para interceptar las instrucciones `SYSCALL` (`amd64`) y `SVC #0` (`arm64`) de Linux sin arrancar una VM Linux.

`gvx` resuelve ambos problemas mediante cuatro backends de ejecución que implementan `pkg/sentry/platform.Context`:

![Mecánica de interceptación de llamadas al sistema y paginación de 4 KB en la plataforma gvx](/assets/images/gvx/gvx-platform-syscalls.png)

### 1. Motor de hardware de Windows: `platform/whpx` (`WinHvPlatform.dll`)

En `windows/amd64` y `windows/arm64`, `platform/whpx` crea una partición de hipervisor ligera mediante `WHvCreatePartition` sin arrancar el kernel de un SO invitado:

- **Desacoplador de paginación de 64 KB a 4 KB**: `whpx` registra cada arena de 64 MiB de `pgalloc.MemoryFile` en la partición una sola vez mediante `WHvMapGpaRange`. Las llamadas posteriores a `MapFile` y `Unmap` modifican únicamente las tablas de páginas del invitado dentro de la partición, aplicando permisos `R/W/X` exactos de 4 KB pese a la granularidad de asignación de 64 KB de Windows.
- **Intercambio de registros sin llamadas a la API mediante `CommPage`**:
  - En `amd64`, un `SYSCALL` del invitado en Ring 3 entra en un stub de Ring 0 dentro de la partición mediante `MSR_LSTAR` (`stubgen_x86.go`), que vuelca los GPR de la ruta rápida de llamadas al sistema (`RAX, RDI, RSI, RDX, R10, R8, R9, RIP, RSP`) en una `CommPage` compartida de 4 KB y ejecuta `HLT` para salir de `WHvRunVirtualProcessor`.
  - En `arm64`, un `SVC #0` del invitado en EL0 entra en los vectores de EL1 (`stubgen_a64.go`), vuelca `X0..X8, SP_EL0, ELR_EL1, PSTATE` en `CommPage` y carga desde el GPA sin respaldo en cero para provocar una salida por acceso a memoria hacia Sentry.

### 2. Motor de hardware de macOS: `platform/hvf` (`Hypervisor.framework`)

En Apple Silicon (`darwin/arm64`), `platform/hvf` ejecuta los hilos del contenedor dentro de vCPU de `Hypervisor.framework` (`hv_vm_create`, `hv_vcpu_run`):

- **Traducción MMU de dos etapas de 4 KB sobre 16 KB**: La etapa 2 (`hv_vm_map`) mapea fragmentos de 1 GiB del archivo de memoria en el espacio de direcciones físicas intermedias (IPA) una sola vez, con granularidad de página del host de 16 KB y con `HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC`. Dentro de la vCPU, el stub EL1 `bluepill` (`bluepill_arm64.go`) configura `TCR_EL1.TG0 = 0b00` (granularidad de 4 KB) en `TTBR0_EL1` y rellena las entradas de tabla de páginas de la etapa 1 de 4 KB con permisos exactos `R-X` o `RW-` (`UXN=1`). Como la MMU de ARM64 aplica la intersección de los permisos de la etapa 1 y la etapa 2, el hardware captura los fallos de permiso en límites exactos de 4 KB.
- **Interceptación de llamadas al sistema**: Un `SVC #0` del invitado en EL0 transfiere el control a `VBAR_EL1` en EL1, vuelca los registros en la `CommPage` compartida de 4 KB y ejecuta `HVC #0` para regresar desde `hv_vcpu_run` al hilo Go de Sentry.

### 3. Alternativa en modo usuario de Windows: `platform/systrap` (`winsystrap`)

Cuando la virtualización por hardware no está disponible (por ejemplo, dentro de VM anidadas en la nube), `winsystrap` ejecuta el código del invitado en modo usuario en `windows/amd64` y `windows/arm64`:

- **Aislamiento por hardware de `CR3` / `TTBR0_EL1`**: Los hilos del invitado se ejecutan dentro de subprocesos stub aislados que se arrancan desde un ejecutable generado de 1 KB (`stubimage_xplat.go`), confinados por un token de integridad Baja y un Job Object.
- **Parcheo binario in situ e IPC `winSysmsg`**: En `amd64`, el parcheador (`patcher_amd64_xplat.go`) sustituye las instrucciones `syscall` (`0f 05`) de 2 bytes por saltos `jmp rel32` de 5 bytes (o por trampas de punto de interrupción `CC 90` cuando no hay una ventana de 5 bytes disponible) hacia una isla de ruta rápida. En `arm64`, `svc #0` se parchea con una rama `B` hacia un slot de isla. La isla vuelca los registros en la página compartida `winSysmsg` y se sincroniza con Sentry mediante `WaitOnAddress` y `WakeByAddressSingle`, mientras que las trampas no parcheadas y los fallos de página se detienen en un Windows Debug Object (`WaitForDebugEventEx`).

### 4. Motor en modo usuario de Linux: `platform/systrap`

En `linux/amd64` y `linux/arm64`, `systrap` ejecuta hilos del invitado (`sysmsg`) que interceptan las llamadas al sistema del invitado mediante `SECCOMP_RET_TRAP` (`SIGSYS`) y parchean en binario los sitios `syscall` hacia un trampolín de memoria compartida (`sysmsg`) sincronizado mediante `SYS_FUTEX`. Mientras tanto, un hilo auxiliar `syscallThread` (`syscall_thread.go`) ejecuta las operaciones sobre el espacio de direcciones (`mmap`, `munmap`, `clone`) mediante `SECCOMP_RET_USER_NOTIF` (`_RUN_SECCOMP_LOOP`). Cuando `gvx` se ejecuta dentro de un sandbox externo de `gVisor` (`runsc`) donde la única ranura `SECCOMP_FILTER_FLAG_NEW_LISTENER` devuelve `EBUSY`, `gvx` cambia automáticamente `syscallThread` a un bucle de futex en memoria compartida (`_RUN_SYSCALL_LOOP`), tal como se detalla en la [Parte 3 de esta serie](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03).

---

## Cómo `gvx` asigna las llamadas al sistema de Linux del invitado a subsistemas virtuales y handles del host

Una vez que un motor de plataforma intercepta una instrucción `SYSCALL` (`amd64`) o `SVC #0` (`arm64`) del invitado y escribe los registros del invitado en la página compartida de 4 KB (`CommPage`, `sysmsg` o `winSysmsg`), el control regresa a la goroutine de tarea de Go `Sentry` (`pkg/sentry/kernel/task_syscall.go`). En lugar de reenviar las llamadas al sistema de Linux al kernel del sistema operativo subyacente, `Task.doSyscall()` guarda el estado original de los registros (`SyscallSaveOrig()`), evalúa los filtros BPF `seccomp` y las paradas `ptrace` del invitado, y despacha el número de llamada al sistema mediante `linux.AMD64` o `linux.ARM64` (`pkg/sentry/syscalls/linux/linux64.go`, que cubre unas 370 llamadas al sistema de Linux) hacia cuatro dominios internos:

![Cómo gvx asigna las llamadas al sistema de Linux del invitado a subsistemas y al sistema operativo del host](/assets/images/gvx/gvx-syscall-mapping.png)

| Dominio de llamadas al sistema | Llamadas al sistema representativas del invitado Linux | Subsistema interno de `gvx` (`pkg/sentry`) | Interacción con el límite del SO del host (tras `hostlockdown`) |
| :--- | :--- | :--- | :--- |
| **A. Procesos, espacios de nombres, montajes y sincronización** | `clone`, `execve`, `unshare`, `setns`, `mount`, `umount2`, `pivot_root`, `setuid`, `capset`, `futex`, `epoll_wait`, `pipe2` | `kernel.Task`, credenciales virtuales, tabla de futex y `VFS2` en memoria (`procfs`, `sysfs`, `cgroupfs` v2, `tmpfs`, `overlayfs`) | **Cero llamadas al sistema del host** (100 % estado Go en memoria; completamente invisible para el SO del host) |
| **B. Memoria virtual (`MM`)** | `mmap`, `munmap`, `mremap`, `mprotect`, `brk`, `madvise`, `memfd_create` | `mm.MemoryManager` (árbol de VMA de 4 KB) y `pgalloc.MemoryFile` | Actualizaciones bidireccionales de tablas de páginas de 4 KB mediante `WHvMapGpaRange`, `hv_vm_map` + `TTBR0_EL1` o `mmap`/`mprotect` del stub |
| **C. Archivos y almacenamiento (`VFS2`)** | `openat`, `pread64`, `pwrite64`, `getdents64`, `statx`, `renameat2`, `setxattr`, `mknod`, `inotify_add_watch` | Enrutador de la tabla de montajes `vfs.VirtualFilesystem` (`fsimpl/erofs`, `fsimpl/ext4lite`, `fsimpl/hostfs` v2) | `pread`/`pwrite` bidireccionales sobre 1 FD `.erofs` fijado o 1 FD `.ext4`, o recorridos relativos acotados sobre `*os.Root` |
| **D. Redes y Netfilter** | `socket`, `bind`, `listen`, `connect`, `sendmsg`, `recvmsg`, `netlink`, `setsockopt(IPT_SO_SET_REPLACE)` | `pkg/tcpip` en espacio de usuario (`lo`, `eth0`, `docker0`, `veth`, `vxlan`, `wireguard`, `ipset`, `iptables-legacy`) | El tráfico interno del sandbox usa **cero llamadas al sistema del host**; el tráfico externo se asigna a `net.Dial`/`net.Listen` (`-net=nat`) o a 1 socket UDP (`-net=wg`) |

Cuando el manejador devuelve `(rval, ctrl, err)`, `Task.doSyscallInvoke()` convierte cualquier error en un `errno` negativo de Linux (`-ExtractErrno(err, sysno)`), escribe el valor de retorno de nuevo en la página compartida de registros (`RAX` en `amd64`, `X0` en `arm64`) y reanuda la ejecución del invitado.

---

## Almacenamiento (`VFS2`) y redes en espacio de usuario de doble vía (`netstack`)

Todas las operaciones de archivos y de red del invitado se atienden dentro de Go `Sentry` a través de tres sistemas de archivos `VFS2` y dos pistas de `pkg/netbackend`:

![Motores de almacenamiento VFS2 de gvx y netstack de doble vía](/assets/images/gvx/gvx-storage-netstack.png)

| Motor de almacenamiento | Rol de montaje en `gvx`                           | Recurso de respaldo del host               | Llamadas `openat` / `NtCreateFile` del host en tiempo de ejecución | Escaneos por archivo de Windows Defender (`WdFilter.sys`) |
| :---------------------- | :------------------------------------------------ | :----------------------------------------- | -----------------------------------------------------------------: | --------------------------------------------------------: |
| **`fsimpl/erofs`**      | Capas inferiores de imagen OCI de solo lectura    | Un único archivo `<sha256>.erofs` por capa |                                                              `0` |                                                       `0` |
| **`fsimpl/ext4lite`**   | Volúmenes con nombre (`-v name:/path`)            | Un único archivo `<name>.ext4` por volumen |                                                              `0` |                                                       `0` |
| **`fsimpl/hostfs` v2**  | Espacio de trabajo del host (`-workspace`, `-v /host:/guest`) | Directorio del host fijado mediante `*os.Root` |                                                `O(depth)` por apertura no cacheada |                                  `1` por cierre de archivo del host |

### Por qué `ext4lite` importa para Docker, bases de datos y gestores de paquetes

Extraer o escribir decenas de miles de archivos de contenedor directamente en NTFS de Windows o APFS de macOS activa los escaneos del minifiltro antivirus (`WdFilter.sys`), colisiones por sensibilidad a mayúsculas y la ausencia de atributos extendidos POSIX.

`pkg/sentry/fsimpl/ext4lite` implementa un sistema de archivos `ext4` en Go puro dentro de un único archivo del host preabierto (`%LOCALAPPDATA%\gvx\cache\volumes\<name>.ext4` en Windows, `~/Library/Caches/gvx/volumes/<name>.ext4` en macOS o `~/.cache/gvx/volumes/<name>.ext4` en Linux), con las siguientes características:

- **Bloques de 4 KiB y grupos de bloques dinámicos de 16 MiB (`meta_bg`)**: Crece dinámicamente sin bloqueos por asignación de archivos dispersos (`fallocate` en Linux, `fcntl(F_PREALLOCATE)` en macOS, `FileAllocationInfo` en Windows) y compacta los grupos finales no utilizados al desmontar limpiamente.
- **Diario JBD2 en modo ordenado (inodo 8)**: Confirmaciones de transacciones atómicas con sumas de comprobación CRC-32 y bloques de revocación, además de recuperación mediante `fsckLite`.
- **Soporte de capa superior de `overlayfs`**: Implementa los atributos extendidos `trusted.overlay.*` y `security.capability`, los whiteouts de dispositivos de caracteres (`0:0`), el informe de entradas de directorio `d_type` y `RENAME_EXCHANGE`.

---

## Cargas de trabajo prácticas: motor Docker, Kubernetes (`k3s` + `Calico`) y SDK de Go

### Instalar `rungv`: versiones precompiladas con atestación SLSA o `go install`

Puede descargar un archivo `rungv` precompilado para `linux/amd64`, `linux/arm64`, `darwin/arm64`, `windows/amd64` o `windows/arm64` desde [Releases de GitHub](https://github.com/gfire-sigs/gvx/releases) (cada uno compilado por el pipeline de publicación SLSA con un paquete de procedencia de Sigstore y `SHA256SUMS`), o instalarlo directamente desde el código fuente con `go install`:

```sh
# Opción A: Descargar una versión precompilada y verificar la procedencia de compilación SLSA
gh attestation verify rungv-v0.2.0-linux-amd64.tar.gz --repo gfire-sigs/gvx \
  --signer-workflow gfire-sigs/gvx/.github/workflows/release.yml \
  --source-ref refs/tags/v0.2.0 --deny-self-hosted-runners
shasum -a 256 -c SHA256SUMS --ignore-missing
tar -xzf rungv-v0.2.0-linux-amd64.tar.gz

# Opción B: Instalar directamente mediante Go (Linux / Windows)
CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest
```

> En macOS (`darwin/arm64`), el archivo de la versión precompilada ya está firmado con el derecho `com.apple.security.hypervisor` (`xattr -d com.apple.quarantine rungv` elimina la marca de cuarentena del navegador). Si compila localmente con `go install` o `go build` en macOS, firme el binario con `codesign --sign - --entitlements entitlements.plist --force $(which rungv)`.

### 1. Ejecutar un motor Docker sin privilegios (`docker:dind`)

Siguiendo [`docs/docker-engine.md`](https://github.com/gfire-sigs/gvx/blob/main/docs/docker-engine.md), puede ejecutar un contenedor `docker:dind` sin modificar dentro de `rungv` en Windows, macOS o Linux y controlarlo con su CLI `docker` del host:

```sh
# 1. Crear el directorio del host para el socket Unix reenviado
mkdir -p ~/.gvx

# 2. Iniciar docker:dind dentro de un sandbox de gvx con un volumen ext4lite
rungv run \
  -p unix:~/.gvx/docker.sock:2375 \
  -p 127.0.0.1:9000-9019:9000-9019 \
  -v gvx-docker:/var/lib/docker \
  -e DOCKER_TLS_CERTDIR= \
  docker:dind --tls=false
```

En una segunda terminal, apunte su CLI `docker` del host al socket del sandbox:

```sh
docker context create gvx --docker host=unix://$HOME/.gvx/docker.sock
docker context use gvx

docker info --format '{{.Driver}} {{.OperatingSystem}}'
# Salida: overlayfs Alpine Linux ... (en contenedor)

docker build -t myapp ./app
docker compose up -d
```

### 2. Ejecutar Kubernetes (`k3s` + `Calico`) en sandboxes sin privilegios

Dado que `gvx` implementa `/dev/kmsg`, `cgroup` v2 (el OOM killer de `memory.max` y la limitación de ancho de banda de `cpu.max`), `/proc/diskstats`, los dispositivos netlink `wireguard` y `vxlan`, `ipset` e `iptables-legacy` (`addrtype`, `conntrack`, `statistic`, `recent`), una imagen `rancher/k3s` sin modificar alcanza el estado `Ready` dentro de un único proceso `rungv`:

```sh
mkdir -p ~/.gvx/k3s

rungv run \
  -e IPTABLES_MODE=legacy \
  -p 127.0.0.1:6443:6443 \
  -p 127.0.0.1:30000-30019:30000-30019 \
  -v k3s:/var/lib/rancher \
  --mount type=bind,src=$HOME/.gvx/k3s,dst=/output \
  rancher/k3s:v1.31.4-k3s1 server \
  --disable=traefik,servicelb \
  --write-kubeconfig=/output/kubeconfig.yaml \
  --write-kubeconfig-mode=644
```

También puede unir varios nodos `gvx` mediante WireGuard (`flannel-backend=wireguard-native` o una malla `wg0`) con alta disponibilidad de `etcd` embebido, o ejecutar **Calico** (`calico-vxlan.yaml` con `FELIX_IPTABLESBACKEND=Legacy`) para aplicar `NetworkPolicy` de Kubernetes íntegramente en espacio de usuario.

### 3. Incrustar contenedores Linux en Go mediante `z.gfire.dev/gvx/pkg/gvx`

Las aplicaciones en Go pueden incrustar contenedores Linux directamente, sin un daemon de contenedores externo:

```go
package main

import (
	"context"
	"fmt"
	"os"

	"z.gfire.dev/gvx/pkg/gvx"
)

func main() {
	ctx := context.Background()
	sb, err := gvx.New(ctx, gvx.Config{
		Platform:      gvx.PlatformAuto,
		NetworkMode:   gvx.NetModeNAT,
		OCIImage:      "alpine:3.20",
		WorkspacePath: "./repo",
		Argv:          []string{"/usr/bin/git", "status"},
		Stdout:        os.Stdout,
		Stderr:        os.Stderr,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "init sandbox: %v\n", err)
		os.Exit(1)
	}
	defer sb.Close()

	exitCode, err := sb.Run(ctx)
	if err != nil {
		fmt.Fprintf(os.Stderr, "run sandbox: %v\n", err)
		os.Exit(1)
	}
	os.Exit(exitCode)
}
```

---

## Más lecturas de esta serie

1. **[gvx: ejecutar contenedores Linux, Docker y Kubernetes en un único proceso sin privilegios](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)** (esta entrada)
2. **[Ejecutar Docker, docker build y docker compose sin root ni espacios de nombres de usuario](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)**: Cómo arrancar `docker:dind` en sandboxes muy restringidos donde `sudo`, `unshare(CLONE_NEWUSER)`, `mount(2)` y `/dev/fuse` están bloqueados.
3. **[Ejecutar Docker-in-Docker dentro de gVisor y sandboxes anidados con gvx](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)**: Por qué fallan `apt-get install docker.io` y `runsc` anidado dentro de `gVisor` a través de cinco barreras del kernel, y cómo `gvx` ejecuta `docker:dind` anidado mediante un bucle de futex en memoria compartida.
4. **[Entornos Docker aislados y rápidos para rollouts de RL de agentes de IA con gvx](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)**: Escalado de `SWE-bench` y de rollouts de aprendizaje por refuerzo de agentes con cientos de instancias paralelas de `docker:dind` sin privilegios y reinicios de volúmenes `.ext4` con copy-on-write en 100 milisegundos.
