---
id: e62d78ef5850c982dbddc9d3ff9134ae
author: Lemon Mint
title: 'gvx: Esecuzione di Linux Containers, Docker e Kubernetes in un unico processo non privilegiato'
description: Panoramica tecnica di gvx, un runtime per container multipiattaforma e SDK Go con rafforzamento della sicurezza, costruito sul kernel user-space gVisor, che esegue in sicurezza container Linux, Docker-in-Docker e cluster Kubernetes ad alta disponibilità multi-nodo in un singolo processo non privilegiato su Windows, macOS e Linux.
language: it
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
---

## Introduzione: contenitori rafforzati dal punto di vista della sicurezza, senza root né daemon sull'host

Eseguire container Linux, `docker build` o Kubernetes all'interno di una macchina di sviluppo, di un runner CI o di un nodo cloud multi-tenant comporta di solito un compromesso di sicurezza poco confortevole:

- Su **Linux**, i runtime OCI standard (`runc`, `crun`, `runsc`) richiedono privilegi `root`, l'esposizione di `/var/run/docker.sock` dell'host, `--privileged` (`CAP_SYS_ADMIN`) oppure user namespaces (`unshare(CLONE_NEWUSER)`), che ampliano la superficie di attacco del kernel dell'host.
- Su **Windows** e **macOS**, i motori di container (`Docker Desktop`, `Podman Machine`, `Lima`, `OrbStack`) avviano un kernel Linux completo in una macchina virtuale all'interno di WSL2, Hyper-V o `Virtualization.framework` ed eseguono un daemon persistente in background all'interno di tale VM.

**[gvx](https://github.com/gfire-sigs/gvx)** (CLI `rungv` e SDK Go `z.gfire.dev/gvx/pkg/gvx`) offre un'**alternativa sicura e a privilegi zero**. Realizzato come fork tracciabile a monte del kernel Linux in user-space **gVisor** di Google (`Sentry`) e dello stack di rete in user-space (`netstack`), `gvx` esegue direttamente binari `linux/amd64` e `linux/arm64` ELF64 non modificati e container OCI all'interno di **un unico processo host non privilegiato e auto-bloccato** su Windows, macOS e Linux.

> **Garanzia di sicurezza (difesa in profondità a tre anelli)**: `gvx` non indebolisce mai i confini di sicurezza del sistema operativo host, non installa mai driver del kernel e non richiede mai `root`, `sudo`, `--privileged` sull'host o `CLONE_NEWUSER`. Prima di eseguire una singola istruzione del guest, `gvx` blocca il proprio processo host tramite Windows Restricted Tokens e Job Objects, macOS Seatbelt (`deny default`) oppure Linux `PR_SET_NO_NEW_PRIVS` + BPF `seccomp`. Tutte le system call del guest, i privilegi virtuali `UID 0`, i mount (`VFS2`) e le tabelle di rete (`netstack`) sono rigorosamente confinati all'interno del kernel Go `Sentry`, sicuro dal punto di vista della memoria.

| Runtime                    | Sistema operativo host                                |   Avvio a freddo | Occupazione di memoria | Admin o `sudo` necessari | Daemon VM in background | Esecuzione diretta di immagini OCI |
| :------------------------- | :---------------------------------------------------- | ---------------: | ---------------------: | :----------------------- | :---------------------- | :--------------------------------- |
| **`gvx` (`whpx` / `hvf`)** | Windows (`amd64`, `arm64`) e macOS (`arm64`)          |         `~20 ms` |             `~30 MiB` | No                       | No                      | Sì                                 |
| **`gvx` (`systrap`)**      | Windows (`amd64`, `arm64`) e Linux (`amd64`, `arm64`) |   `~25 a 65 ms` |             `~31 MiB` | No                       | No                      | Sì                                 |
| **WSL2 / Docker Desktop**  | Windows e macOS                                       | `800 a 2500 ms` |           `> 512 MiB` | Sì (configurazione iniziale) | Sì                  | Richiede daemon                    |
| **QEMU (Full System)**     | Windows e macOS                                       | `1500 a 4000 ms` |          `> 256 MiB` | No                       | No                      | Richiede immagine disco            |

Su Windows o macOS non viene avviato alcun kernel di macchina virtuale Linux. Sull'host non viene eseguito alcun daemon in background. Sul sistema operativo host non sono richiesti privilegi `root`, `sudo`, user namespaces (`CLONE_NEWUSER`), `mount(2)`, driver `TUN`/`TAP` o `CAP_NET_ADMIN`. All'interno di quel processo non privilegiato, `gvx` esegue **Docker-in-Docker (`docker:dind`)**, `docker build`, `docker compose` e **Kubernetes High-Availability multi-nodo (`k3s` con `etcd` integrato e CNI `Calico`)**.

---

## Ciclo di vita del container e blocco dell'host a tre anelli

Ogni sandbox `gvx` viene eseguita all'interno di un unico processo host non privilegiato e applica un ciclo di vita in tre fasi: pre-apertura di tutti i file descriptor e di tutti i socket host richiesti, blocco del processo del sistema operativo host tramite `pkg/hostlockdown` e ingresso nel ciclo di esecuzione `Sentry` in-process.

![Ciclo di vita del container gvx e auto-blocco dell'host](/assets/images/gvx/gvx-lifecycle-lockdown.png)

Prima di trasferire il controllo alla prima istruzione del guest, `gvx` pre-apre i descrittori delle immagini `.erofs` in cache, i descrittori dei volumi `.ext4`, l'handle `*os.Root` di `/workspace` e i socket di rete dell'host, quindi invoca `hostlockdown.Apply()`:

1. **Windows NT (`lockdown_windows.go`)**: Chiama `AdjustTokenPrivileges` con `SE_PRIVILEGE_REMOVED` per rimuovere i privilegi sul posto, associa un token di impersonificazione ristretto `DISABLE_MAX_PRIVILEGE` con livello di integrità Low (`S-1-16-4096`), assegna il processo a un Job Object anonimo con `JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 1` (bloccando la creazione di qualsiasi processo figlio) e abilita `DisallowWin32kSystemCalls = 1` tramite `SetProcessMitigationPolicy`.
2. **macOS XNU (`lockdown_darwin.go`)**: Imposta `RLIMIT_NPROC = 0` e applica un profilo Seatbelt (`sandbox_init_with_parameters`) a partire da `(version 1)(deny default)`, che blocca `process-fork`, `process-exec*` e tutte le nuove aperture di file dell'host.
3. **Linux (`lockdown_linux.go` + `pkg/gvx/syscallfilter_linux.go`)**: Imposta `PR_SET_NO_NEW_PRIVS` e installa la allowlist BPF `seccomp` di Sentry su tutti i thread (`SECCOMP_FILTER_FLAG_TSYNC`), con regole `EPERM` esplicite per `execve`, `execveat`, `clone`, `openat` e `socket`.

---

## Intercettazione cross-platform delle system call e paginazione da 4 KB (`pkg/sentry/platform`)

I binari Linux ELF64 presuppongono una granularità delle pagine di memoria di 4 KB (`0x1000`) per i mapping dei segmenti `mmap(MAP_FIXED)`. Tuttavia, `VirtualAlloc` e `MapViewOfFile3` di Windows impongono un allineamento a 64 KB (`0x10000`), mentre macOS su Apple Silicon (`darwin/arm64`) utilizza pagine host da 16 KB (`0x4000`). Inoltre, ciascun sistema operativo richiede un meccanismo diverso per intercettare le istruzioni Linux `SYSCALL` (`amd64`) e `SVC #0` (`arm64`) senza avviare una VM Linux.

`gvx` risolve entrambi i problemi tramite quattro backend di esecuzione che implementano `pkg/sentry/platform.Context`:

![Meccanismi di intercettazione delle system call e di paginazione da 4KB in gvx](/assets/images/gvx/gvx-platform-syscalls.png)

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

Su `windows/amd64` e `windows/arm64`, `platform/whpx` crea una partizione hypervisor leggera tramite `WHvCreatePartition` senza avviare il kernel di un sistema operativo guest:

- **Disaccoppiatore di paginazione da 64 KB a 4 KB**: `whpx` registra ciascuna arena da 64 MiB di `pgalloc.MemoryFile` nella partizione una sola volta tramite `WHvMapGpaRange`. Le successive chiamate `MapFile` e `Unmap` modificano solo le tabelle delle pagine del guest all'interno della partizione, applicando permessi `R/W/X` esatti a 4 KB, nonostante la granularità di allocazione di 64 KB di Windows.
- **Scambio di registri a zero API tramite `CommPage`**:
  - Su `amd64`, un'istruzione `SYSCALL` del guest in Ring-3 entra in uno stub `MSR_LSTAR` in Ring-0 interno alla partizione (`stubgen_x86.go`), che salva i GPR della system call fast-path (`RAX, RDI, RSI, RDX, R10, R8, R9, RIP, RSP`) in una `CommPage` condivisa da 4 KB ed esegue `HLT` per uscire da `WHvRunVirtualProcessor`.
  - Su `arm64`, un'istruzione `SVC #0` del guest in EL0 entra nei vettori EL1 (`stubgen_a64.go`), salva `X0..X8, SP_EL0, ELR_EL1, PSTATE` nella `CommPage` e carica dal GPA zero non associato per innescare un'uscita di memoria verso Sentry.

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

Su Apple Silicon (`darwin/arm64`), `platform/hvf` esegue i thread dei container all'interno dei vCPU di `Hypervisor.framework` (`hv_vm_create`, `hv_vcpu_run`):

- **Traduzione MMU a due stadi, 4 KB su 16 KB**: lo Stage-2 (`hv_vm_map`) mappa porzioni di memory-file da 1 GiB nello spazio degli Intermediate Physical Address (IPA) una sola volta, con granularità di pagina host di 16 KB, con `HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC`. All'interno del vCPU, lo stub EL1 `bluepill` (`bluepill_arm64.go`) configura `TCR_EL1.TG0 = 0b00` (granule da 4 KB) in `TTBR0_EL1` e popola le voci di tabella delle pagine Stage-1 da 4 KB con permessi esatti `R-X` oppure `RW-` (`UXN=1`). Poiché la MMU ARM64 applica l'intersezione dei permessi Stage-1 e Stage-2, l'hardware intercetta le violazioni di permesso ai confini esatti di 4 KB.
- **Intercettazione delle system call**: Un'istruzione `SVC #0` del guest in EL0 viene intercettata da `VBAR_EL1` in EL1, salva i registri nella `CommPage` condivisa da 4 KB ed esegue `HVC #0` per restituire il controllo da `hv_vcpu_run` al thread Go di Sentry.

### 3. Fallback user-mode per Windows: `platform/systrap` (`winsystrap`)

Quando la virtualizzazione hardware non è disponibile (ad esempio all'interno di VM cloud annidate), `winsystrap` esegue il codice guest in user mode su `windows/amd64` e `windows/arm64`:

- **Isolamento hardware tramite `CR3` / `TTBR0_EL1`**: I thread del guest vengono eseguiti all'interno di sottoprocessi stub isolati, avviati da un eseguibile generato da 1 KB (`stubimage_xplat.go`), confinati da un token a bassa integrità e da un Job Object.
- **Patching binario sul posto e IPC `winSysmsg`**: Su `amd64`, il patcher (`patcher_amd64_xplat.go`) sostituisce le istruzioni `syscall` (`0f 05`) di 2 byte con salti `jmp rel32` di 5 byte (oppure con trap di breakpoint `CC 90` quando una finestra di 5 byte non è disponibile) verso un'isola fast-path. Su `arm64`, `svc #0` viene patchata con un ramo `B` verso uno slot dell'isola. L'isola salva i registri nella pagina condivisa `winSysmsg` e si sincronizza con Sentry tramite `WaitOnAddress` e `WakeByAddressSingle`, mentre i trap non patchati e i page fault si fermano su un Windows Debug Object (`WaitForDebugEventEx`).

### 4. Motore user-mode Linux: `platform/systrap`

Su `linux/amd64` e `linux/arm64`, `systrap` esegue thread del guest (`sysmsg`) che intercettano le system call del guest tramite `SECCOMP_RET_TRAP` (`SIGSYS`) e applicano il patching binario dei siti `syscall` verso un trampolino in memoria condivisa (`sysmsg`), sincronizzato tramite `SYS_FUTEX`. Nel frattempo, un thread stub `syscallThread` (`syscall_thread.go`) esegue le operazioni sullo spazio degli indirizzi (`mmap`, `munmap`, `clone`) tramite `SECCOMP_RET_USER_NOTIF` (`_RUN_SECCOMP_LOOP`). Quando `gvx` viene eseguito all'interno di una sandbox `gVisor` (`runsc`) esterna, in cui l'unico slot `SECCOMP_FILTER_FLAG_NEW_LISTENER` restituisce `EBUSY`, `gvx` passa automaticamente `syscallThread` a un loop futex in memoria condivisa (`_RUN_SYSCALL_LOOP`), come descritto in dettaglio nella [Parte 3 di questa serie](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03).

---

## Come `gvx` mappa le system call Linux del guest su sottosistemi virtuali e handle dell'host

Una volta che un motore di piattaforma ha intercettato un'istruzione `SYSCALL` (`amd64`) o `SVC #0` (`arm64`) del guest e ha scritto i registri del guest nella pagina condivisa da 4 KB (`CommPage`, `sysmsg` o `winSysmsg`), il controllo torna al goroutine Go del task di `Sentry` (`pkg/sentry/kernel/task_syscall.go`). Anziché inoltrare le system call Linux al kernel del sistema operativo host sottostante, `Task.doSyscall()` salva lo stato originale dei registri (`SyscallSaveOrig()`), valuta gli eventuali filtri BPF `seccomp` del guest e le fermate `ptrace`, quindi invia il numero della system call tramite `linux.AMD64` o `linux.ARM64` (`pkg/sentry/syscalls/linux/linux64.go`, che copre circa 370 system call Linux) verso quattro domini interni:

![Come gvx mappa le system call Linux del guest su sottosistemi e sistema operativo host](/assets/images/gvx/gvx-syscall-mapping.png)

| Dominio di system call | Esempi rappresentativi di system call Linux del guest | Sottosistema interno `gvx` (`pkg/sentry`) | Interazione con il confine del sistema operativo host (dopo `hostlockdown`) |
| :--- | :--- | :--- | :--- |
| **A. Processi, namespace, mount e sincronizzazione** | `clone`, `execve`, `unshare`, `setns`, `mount`, `umount2`, `pivot_root`, `setuid`, `capset`, `futex`, `epoll_wait`, `pipe2` | `kernel.Task`, credenziali virtuali, tabella dei futex e `VFS2` in memoria (`procfs`, `sysfs`, `cgroupfs` v2, `tmpfs`, `overlayfs`) | **Zero system call host** (stato Go interamente in memoria; completamente invisibile al sistema operativo host) |
| **B. Memoria virtuale (`MM`)** | `mmap`, `munmap`, `mremap`, `mprotect`, `brk`, `madvise`, `memfd_create` | `mm.MemoryManager` (albero VMA da 4 KB) e `pgalloc.MemoryFile` | Aggiornamenti bidirezionali delle tabelle delle pagine da 4 KB tramite `WHvMapGpaRange`, `hv_vm_map` + `TTBR0_EL1`, oppure `mmap`/`mprotect` dello stub |
| **C. File e archiviazione (`VFS2`)** | `openat`, `pread64`, `pwrite64`, `getdents64`, `statx`, `renameat2`, `setxattr`, `mknod`, `inotify_add_watch` | Router della tabella di mount di `vfs.VirtualFilesystem` (`fsimpl/erofs`, `fsimpl/ext4lite`, `fsimpl/hostfs` v2) | `pread`/`pwrite` bidirezionali su 1 FD `.erofs` fissato o 1 FD `.ext4`, oppure attraversamenti relativi limitati su `*os.Root` |
| **D. Rete e Netfilter** | `socket`, `bind`, `listen`, `connect`, `sendmsg`, `recvmsg`, `netlink`, `setsockopt(IPT_SO_SET_REPLACE)` | `pkg/tcpip` user-space (`lo`, `eth0`, `docker0`, `veth`, `vxlan`, `wireguard`, `ipset`, `iptables-legacy`) | Il traffico interno alla sandbox non utilizza **alcuna system call host**; il traffico esterno viene mappato su `net.Dial`/`net.Listen` (`-net=nat`) oppure su 1 socket UDP (`-net=wg`) |

Quando il gestore restituisce `(rval, ctrl, err)`, `Task.doSyscallInvoke()` converte qualsiasi errore in un `errno` Linux negativo (`-ExtractErrno(err, sysno)`), riscrive il valore di ritorno nella pagina condivisa dei registri (`RAX` su `amd64`, `X0` su `arm64`) e riprende l'esecuzione del guest.

---

## Storage (`VFS2`) e networking user-space a doppio binario (`netstack`)

Tutte le operazioni su file e rete del guest vengono gestite all'interno del `Sentry` in Go tramite tre filesystem `VFS2` e due tracciati `pkg/netbackend`:

![Motori di storage VFS2 e netstack a doppio binario in gvx](/assets/images/gvx/gvx-storage-netstack.png)

| Motore di storage      | Ruolo di mount in `gvx`                          | Risorsa host di supporto                | Chiamate host `openat` / `NtCreateFile` a runtime | Scansioni per file di Windows Defender (`WdFilter.sys`) |
| :--------------------- | :----------------------------------------------- | :-------------------------------------- | ------------------------------------------------: | ------------------------------------------------------: |
| **`fsimpl/erofs`**     | Strati inferiori in sola lettura dell'immagine OCI | Singolo file `<sha256>.erofs` per strato | `0`                                               | `0`                                                     |
| **`fsimpl/ext4lite`**  | Volumi con nome (`-v name:/path`)                | Singolo file `<name>.ext4` per volume   | `0`                                               | `0`                                                     |
| **`fsimpl/hostfs` v2** | Workspace host (`-workspace`, `-v /host:/guest`) | Directory host `*os.Root` fissata       | `O(depth)` per apertura non in cache              | `1` per chiusura di file host                           |

### Perché `ext4lite` è importante per Docker, database e package manager

Estrarre o scrivere decine di migliaia di file di container direttamente su NTFS di Windows o su APFS di macOS attiva le scansioni dei minifilter antivirus (`WdFilter.sys`), collisioni di sensibilità alle maiuscole e minuscole e l'assenza di attributi estesi POSIX.

`pkg/sentry/fsimpl/ext4lite` implementa un filesystem `ext4` interamente in Go all'interno di un unico file host pre-aperto (`%LOCALAPPDATA%\gvx\cache\volumes\<name>.ext4` su Windows, `~/Library/Caches/gvx/volumes/<name>.ext4` su macOS oppure `~/.cache/gvx/volumes/<name>.ext4` su Linux), con:

- **Blocchi da 4 KiB e block group dinamici da 16 MiB (`meta_bg`)**: crescono dinamicamente senza stalli nell'allocazione di file sparsi (`fallocate` su Linux, `fcntl(F_PREALLOCATE)` su macOS, `FileAllocationInfo` su Windows) e compattano i gruppi finali inutilizzati durante uno smontaggio pulito.
- **Journal JBD2 in modalità ordered (inode 8)**: commit atomici delle transazioni con checksum CRC-32 e blocchi di revoca, più il recupero `fsckLite`.
- **Supporto per lo strato superiore di `overlayfs`**: implementa gli attributi estesi `trusted.overlay.*` e `security.capability`, i whiteout di dispositivi a caratteri (`0:0`), la segnalazione di `d_type` nelle voci delle directory e `RENAME_EXCHANGE`.

---

## Carichi di lavoro pratici: Docker Engine, Kubernetes (`k3s` + `Calico`) e SDK Go

### Installare `rungv`: release precompilate con attestazione SLSA oppure `go install`

È possibile scaricare un archivio `rungv` precompilato per `linux/amd64`, `linux/arm64`, `darwin/arm64`, `windows/amd64` o `windows/arm64` da [GitHub Releases](https://github.com/gfire-sigs/gvx/releases) (ciascuno generato dalla pipeline di release SLSA con un bundle di provenienza Sigstore e `SHA256SUMS`), oppure installare direttamente dal sorgente con `go install`:

```sh
# Opzione A: scarica una release precompilata e verifica la provenienza di build 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

# Opzione B: installa direttamente tramite Go (Linux / Windows)
CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest
```

> Su macOS (`darwin/arm64`), l'archivio di release precompilato è già firmato con il diritto `com.apple.security.hypervisor` (`xattr -d com.apple.quarantine rungv` rimuove la quarantena del browser). Se si compila localmente con `go install` o `go build` su macOS, firmare il binario con `codesign --sign - --entitlements entitlements.plist --force $(which rungv)`.

### 1. Esecuzione di un Docker Engine non privilegiato (`docker:dind`)

Seguendo [`docs/docker-engine.md`](https://github.com/gfire-sigs/gvx/blob/main/docs/docker-engine.md), è possibile eseguire un container `docker:dind` non modificato all'interno di `rungv` su Windows, macOS o Linux e controllarlo con la CLI `docker` dell'host:

```sh
# 1. Crea la directory host per il socket Unix inoltrato
mkdir -p ~/.gvx

# 2. Avvia docker:dind all'interno di una sandbox gvx con un volume 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
```

In un secondo terminale, puntare la CLI `docker` dell'host al socket della sandbox:

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

docker info --format '{{.Driver}} {{.OperatingSystem}}'
# Output: overlayfs Alpine Linux ... (containerizzato)

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

### 2. Esecuzione di Kubernetes (`k3s` + `Calico`) in sandbox non privilegiate

Poiché `gvx` implementa `/dev/kmsg`, `cgroup` v2 (OOM killer tramite `memory.max` e throttling della banda tramite `cpu.max`), `/proc/diskstats`, i dispositivi netlink `wireguard` e `vxlan`, `ipset` e `iptables-legacy` (`addrtype`, `conntrack`, `statistic`, `recent`), un'immagine `rancher/k3s` non modificata raggiunge lo stato `Ready` all'interno di un singolo processo `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
```

È inoltre possibile collegare più nodi `gvx` tramite WireGuard (`flannel-backend=wireguard-native` oppure una mesh `wg0`) con alta disponibilità tramite `etcd` integrato, oppure eseguire **Calico** (`calico-vxlan.yaml` con `FELIX_IPTABLESBACKEND=Legacy`) per applicare le `NetworkPolicy` di Kubernetes interamente nello user space.

### 3. Incorporare container Linux in Go tramite `z.gfire.dev/gvx/pkg/gvx`

Le applicazioni Go possono incorporare container Linux direttamente, senza un daemon di container esterno:

```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)
}
```

---

## Ulteriori letture in questa serie

1. **[gvx: esecuzione di container Linux, Docker e Kubernetes in un unico processo non privilegiato](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)** (questo articolo)
2. **[Esecuzione di Docker, docker build e docker compose senza root né user namespaces](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)**: come avviare `docker:dind` in sandbox bloccate, dove `sudo`, `unshare(CLONE_NEWUSER)`, `mount(2)` e `/dev/fuse` sono disabilitati.
3. **[Esecuzione di Docker-in-Docker all'interno di gVisor e di sandbox annidate con gvx](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)**: perché `apt-get install docker.io` e `runsc` annidato falliscono all'interno di `gVisor` attraverso cinque barriere del kernel, e come `gvx` esegue `docker:dind` annidato tramite un loop futex in memoria condivisa.
4. **[Ambienti Docker isolati e veloci per i rollout RL di agenti AI con gvx](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)**: come scalare `SWE-bench` e i rollout di reinforcement learning per agenti con centinaia di istanze parallele non privilegiate di `docker:dind` e reset copy-on-write dei volumi `.ext4` in 100 millisecondi.
