---
id: e62d78ef5850c982dbddc9d3ff9134ae
author: Lemon Mint
title: 'gvx: Att köra Linux Containers, Docker och Kubernetes i en enda Unprivileged Process'
description: Teknisk översikt över gvx, en säkerhetshärdad, plattformsoberoende containerruntime och Go SDK byggd på gVisors användarrymdskärna, som säkert kör Linux-containrar, Docker-in-Docker och högtillgängliga Kubernetes-kluster med flera noder i en enda processutan privilegier över Windows, macOS och Linux.
language: sv
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
---

## Inledning: Säkerhetshärdade containrar utan root eller värddaemoner

Att köra Linux-containrar, `docker build` eller Kubernetes på en utvecklingsdator, en CI-runner eller en multitenant-molnnod medför vanligtvis en obekväm säkerhetsavvägning:

- På **Linux** kräver standardmässiga OCI-runtimes (`runc`, `crun`, `runsc`) antingen `root`-behörighet, exponering av värdens `/var/run/docker.sock`, `--privileged` (`CAP_SYS_ADMIN`) eller användarnamnrymder (`unshare(CLONE_NEWUSER)`), vilka utökar värdkärnans attackyta.
- På **Windows** och **macOS** startar containermotorer (`Docker Desktop`, `Podman Machine`, `Lima`, `OrbStack`) en fullständig Linux-kärna i en virtuell maskin inom WSL2, Hyper-V eller `Virtualization.framework` och kör en beständig bakgrundsdaemon inuti den virtuella maskinen.

**[gvx](https://github.com/gfire-sigs/gvx)** (`rungv`-CLI:t och `z.gfire.dev/gvx/pkg/gvx` Go-SDK:t) erbjuder ett **säkert alternativ med noll behörighetsbehov**. Det är byggt som en uppströmsspårbar fork av Googles **gVisor** användarlägeskärna för Linux (`Sentry`) och användarlägesnätverksstacken (`netstack`), och `gvx` kör oförändrade `linux/amd64`- och `linux/arm64`-ELF64-binärer samt OCI-containrar direkt inuti **en enda, obehörig och självlåst värdprocess** på Windows, macOS och Linux.

> **Säkerhetsgaranti (Tre-ringsförsvar i djupet)**: `gvx` försvagar aldrig värd-OS:ets säkerhetsgränser, installerar aldrig kärndrivrutiner och kräver aldrig `root`, `sudo`, `--privileged` på värden eller `CLONE_NEWUSER`. Innan en enda gästinstruktion körs låser `gvx` sin egen värdprocess med Windows Restricted Tokens och Job Objects, macOS Seatbelt (`deny default`) eller Linux `PR_SET_NO_NEW_PRIVS` + BPF `seccomp`. Alla gästsystemanrop, virtuella `UID 0`-privilegier, monteringar (`VFS2`) och nätverkstabeller (`netstack`) är strikt begränsade inuti den minnessäkra Go-kärnan `Sentry`.

| Runtime                    | Värd-OS                                               |        Kallstart | Minnesfotavtryck | Admin eller `sudo` krävs | Bakgrunds-VM-daemon | Direkt körning av OCI-avbild |
| :------------------------- | :---------------------------------------------------- | ----------------: | ---------------: | :--------------------- | :------------------- | :------------------- |
| **`gvx` (`whpx` / `hvf`)** | Windows (`amd64`, `arm64`) & macOS (`arm64`)          |          `~20 ms` |        `~30 MiB` | Nej                    | Nej                  | Ja                   |
| **`gvx` (`systrap`)**      | Windows (`amd64`, `arm64`) & Linux (`amd64`, `arm64`) |    `~25 till 65 ms` |        `~31 MiB` | Nej                    | Nej                  | Ja                   |
| **WSL2 / Docker Desktop**  | Windows & macOS                                       |  `800 till 2500 ms` |      `> 512 MiB` | Ja (initial installation) | Ja                   | Kräver daemon        |
| **QEMU (Full System)**     | Windows & macOS                                       | `1500 till 4000 ms` |      `> 256 MiB` | Nej                    | Nej                  | Kräver diskavbild    |

Ingen Linux-VM-kärna startas på Windows eller macOS. Ingen bakgrundsdaemon körs på värden. Inga `root`-, `sudo`-, användarnamnrymds- (`CLONE_NEWUSER`), `mount(2)`-, `TUN`/`TAP`-drivrutins- eller `CAP_NET_ADMIN`-privilegier krävs på värd-OS:et. Inuti den obehöriga processen kör `gvx` **Docker-in-Docker (`docker:dind`)**, `docker build`, `docker compose` och **högtillgänglig Kubernetes med flera noder (`k3s` med inbäddad `etcd` och `Calico` CNI)**.

---

## Containerlivscykel och låsning av värd i tre ringar

Varje `gvx`-sandlåda körs inuti en obehörig värdprocess och genomdriver en livscykel i tre faser: föröppning av alla nödvändiga värdfilbeskrivare och uttag, låsning av värdprocessen via `pkg/hostlockdown` samt inträde i den interna `Sentry`-exekveringsslingan.

![gvx Containerlivscykel och självlåsning av värd](/assets/images/gvx/gvx-lifecycle-lockdown.png)

Innan kontrollen överförs till den första gästinstruktionen föröppnar `gvx` sina cachade `.erofs`-lagerbeskrivare, `.ext4`-volymfilbeskrivare, `/workspace`-handtaget `*os.Root` och värdens nätverksuttag, varefter den anropar `hostlockdown.Apply()`:

1. **Windows NT (`lockdown_windows.go`)**: Anropar `AdjustTokenPrivileges` med `SE_PRIVILEGE_REMOVED` för att på plats ta bort privilegier, binder en `DISABLE_MAX_PRIVILEGE`-begränsad personifieringstoken med låg integritetsnivå (`S-1-16-4096`), tilldelar processen till ett anonymt Job Object med `JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 1` (vilket blockerar skapande av underordnade processer) och aktiverar `DisallowWin32kSystemCalls = 1` via `SetProcessMitigationPolicy`.
2. **macOS XNU (`lockdown_darwin.go`)**: Sätter `RLIMIT_NPROC = 0` och tillämpar en Seatbelt-profil (`sandbox_init_with_parameters`) som börjar med `(version 1)(deny default)` och blockerar `process-fork`, `process-exec*` samt alla nya öppningar av värdfiler.
3. **Linux (`lockdown_linux.go` + `pkg/gvx/syscallfilter_linux.go`)**: Sätter `PR_SET_NO_NEW_PRIVS` och installerar Sentrys BPF `seccomp`-tillåtelselista över alla trådar (`SECCOMP_FILTER_FLAG_TSYNC`) med explicita `EPERM`-regler för `execve`, `execveat`, `clone`, `openat` och `socket`.

---

## Plattformsoberoende avlyssning av systemanrop och 4 KB-paging (`pkg/sentry/platform`)

Linux-ELF64-binärer förutsätter en minnessidgranularitet på 4 KB (`0x1000`) för segmentmappningar via `mmap(MAP_FIXED)`. Windows `VirtualAlloc` och `MapViewOfFile3` tvingar dock fram 64 KB-justering (`0x10000`), och Apple Silicon macOS (`darwin/arm64`) använder 16 KB-sidor (`0x4000`) på värden. Dessutom kräver varje OS en annan mekanism för att avlyssna Linux-instruktionerna `SYSCALL` (`amd64`) och `SVC #0` (`arm64`) utan att starta en Linux-VM.

`gvx` löser båda problemen över fyra exekveringsbackends som implementerar `pkg/sentry/platform.Context`:

![gvx Plattformens avlyssning av systemanrop och 4 KB-paging](/assets/images/gvx/gvx-platform-syscalls.png)

### 1. Windows hårdvarumotor: `platform/whpx` (`WinHvPlatform.dll`)

På `windows/amd64` och `windows/arm64` skapar `platform/whpx` en lättviktig hypervisor-partition via `WHvCreatePartition` utan att starta en gästoperativsystemkärna:

- **Frikoppling av 64 KB till 4 KB-paging**: `whpx` registrerar varje 64 MiB-arena i `pgalloc.MemoryFile` hos partitionen en gång via `WHvMapGpaRange`. Efterföljande anrop till `MapFile` och `Unmap` ändrar endast gästens sidtabeller inuti partitionen, vilket upprätthåller exakta 4 KB-behörigheter `R/W/X` trots Windows allokeringsgranularitet på 64 KB.
- **Registerutbyte utan API-anrop via `CommPage`**:
  - På `amd64` går gästens Ring-3-`SYSCALL` in i en Ring-0-stub `MSR_LSTAR` inuti partitionen (`stubgen_x86.go`), som skriver de snabba systemanropens GPR-register (`RAX, RDI, RSI, RDX, R10, R8, R9, RIP, RSP`) till en delad 4 KB-`CommPage` och kör `HLT` för att avsluta `WHvRunVirtualProcessor`.
  - På `arm64` går gästens EL0-`SVC #0` in i EL1-vektorerna (`stubgen_a64.go`), skriver `X0..X8, SP_EL0, ELR_EL1, PSTATE` till `CommPage` och läser från en ej backad GPA-nolla för att utlösa ett minnesavbrott tillbaka till Sentry.

### 2. macOS hårdvarumotor: `platform/hvf` (`Hypervisor.framework`)

På Apple Silicon (`darwin/arm64`) kör `platform/hvf` containertrådar inuti `Hypervisor.framework`-vCPU:er (`hv_vm_create`, `hv_vcpu_run`):

- **Tvåstegs-MMU-översättning med 4 KB över 16 KB**: Steg 2 (`hv_vm_map`) mappar 1 GiB-delar av minnesfiler till det mellanliggande fysiska adressutrymmet (IPA) en gång, med 16 KB-granularitet för värdsidor och med `HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC`. Inuti vCPU:n konfigurerar EL1-stubben `bluepill` (`bluepill_arm64.go`) `TCR_EL1.TG0 = 0b00` (4 KB-granulat) i `TTBR0_EL1` och fyller i 4 KB-sidtabellposter för steg 1 med exakta behörigheter `R-X` eller `RW-` (`UXN=1`). Eftersom ARM64-MMU:n upprätthåller skärningspunkten mellan behörigheterna för steg 1 och steg 2 fångar hårdvaran behörighetsfel vid exakta 4 KB-gränser.
- **Avlyssning av systemanrop**: Gästens EL0-`SVC #0` går till EL1 `VBAR_EL1`, skriver register till den delade 4 KB-`CommPage` och kör `HVC #0` för att återvända från `hv_vcpu_run` till Go-trådens Sentry.

### 3. Windows användarlägesreserv: `platform/systrap` (`winsystrap`)

När hårdvaruppvisning inte är tillgänglig (till exempel inuti kapslade molnvirtuella maskiner) kör `winsystrap` gästkod i användarläge på `windows/amd64` och `windows/arm64`:

- **Isolering via hårdvarans `CR3` / `TTBR0_EL1`**: Gästtrådar körs inuti isolerade stubbunderprocesser som startas från en genererad körbar fil på 1 KB (`stubimage_xplat.go`), begränsade av en token med låg integritetsnivå och ett Job Object.
- **Binär patchning på plats och `winSysmsg`-IPC**: På `amd64` ersätter patcharen (`patcher_amd64_xplat.go`) 2-byte-instruktionerna `syscall` (`0f 05`) med 5-byte-hopp `jmp rel32` (eller `CC 90`-brytpunktsfällor när ett 5-byte-fönster inte är tillgängligt) till en snabbvägsö. På `arm64` patchas `svc #0` med en `B`-förgrening till en ö-plats. Ön skriver register till den delade `winSysmsg`-sidan och synkroniseras med Sentry via `WaitOnAddress` och `WakeByAddressSingle`, medan opatchade fällor och sidfel stannar vid ett Windows Debug Object (`WaitForDebugEventEx`).

### 4. Linux användarlägesmotor: `platform/systrap`

På `linux/amd64` och `linux/arm64` kör `systrap` gästtrådar (`sysmsg`) som fångar gästens systemanrop via `SECCOMP_RET_TRAP` (`SIGSYS`) och binärpatchar `syscall`-platser till en trampolin i delat minne (`sysmsg`) som synkroniseras via `SYS_FUTEX`. Samtidigt utför en stubbad `syscallThread` (`syscall_thread.go`) operationer på adressutrymmet (`mmap`, `munmap`, `clone`) via `SECCOMP_RET_USER_NOTIF` (`_RUN_SECCOMP_LOOP`). När `gvx` körs inuti en yttre `gVisor`-sandlåda (`runsc`) där den enda `SECCOMP_FILTER_FLAG_NEW_LISTENER`-platsen returnerar `EBUSY`, växlar `gvx` automatiskt `syscallThread` till en futex-slinga i delat minne (`_RUN_SYSCALL_LOOP`), vilket beskrivs i detalj i [del 3 i denna serie](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03).

---

## Hur `gvx` mappar gästens Linux-systemanrop till virtuella delsystem och värdhandtag

När en plattformsmotor har avlyssnat en gästs `SYSCALL` (`amd64`) eller `SVC #0` (`arm64`) och skrivit gästregistren till den delade 4 KB-sidan (`CommPage`, `sysmsg` eller `winSysmsg`), återgår kontrollen till Go-goroutinen för Sentry-uppgiften (`pkg/sentry/kernel/task_syscall.go`). I stället för att vidarebefordra Linux-systemanrop till värd-OS:ets underliggande kärna sparar `Task.doSyscall()` det ursprungliga registertillståndet (`SyscallSaveOrig()`), utvärderar eventuella BPF `seccomp`-filter och `ptrace`-stopp i gästen och skickar systemanropsnumret vidare via `linux.AMD64` eller `linux.ARM64` (`pkg/sentry/syscalls/linux/linux64.go`, som omfattar cirka 370 Linux-systemanrop) till fyra interna domäner:

![Hur gvx mappar gästens Linux-systemanrop till delsystem och värd-OS](/assets/images/gvx/gvx-syscall-mapping.png)

| Systemanropsdomän | Representativa Linux-systemanrop i gästen | Internt `gvx`-delsystem (`pkg/sentry`) | Interaktion med värd-OS (efter `hostlockdown`) |
| :--- | :--- | :--- | :--- |
| **A. Process, namnrymd, montering & synkronisering** | `clone`, `execve`, `unshare`, `setns`, `mount`, `umount2`, `pivot_root`, `setuid`, `capset`, `futex`, `epoll_wait`, `pipe2` | `kernel.Task`, virtuella credentials, futex-tabell och `VFS2` i minnet (`procfs`, `sysfs`, `cgroupfs` v2, `tmpfs`, `overlayfs`) | **Noll systemanrop till värden** (100 % Go-tillstånd i minnet; helt osynligt för värd-OS) |
| **B. Virtuellt minne (`MM`)** | `mmap`, `munmap`, `mremap`, `mprotect`, `brk`, `madvise`, `memfd_create` | `mm.MemoryManager` (VMA-träd med 4 KB) och `pgalloc.MemoryFile` | Dubbelriktade uppdateringar av 4 KB-sidtabeller via `WHvMapGpaRange`, `hv_vm_map` + `TTBR0_EL1` eller stubbens `mmap`/`mprotect` |
| **C. Filer och lagring (`VFS2`)** | `openat`, `pread64`, `pwrite64`, `getdents64`, `statx`, `renameat2`, `setxattr`, `mknod`, `inotify_add_watch` | `vfs.VirtualFilesystem`-monteringstabellens router (`fsimpl/erofs`, `fsimpl/ext4lite`, `fsimpl/hostfs` v2) | Dubbelriktade `pread`/`pwrite` mot 1 fixerad `.erofs`-FD eller 1 `.ext4`-FD, eller avgränsade relativa sökningar på `*os.Root` |
| **D. Nätverk och Netfilter** | `socket`, `bind`, `listen`, `connect`, `sendmsg`, `recvmsg`, `netlink`, `setsockopt(IPT_SO_SET_REPLACE)` | Användarlägets `pkg/tcpip` (`lo`, `eth0`, `docker0`, `veth`, `vxlan`, `wireguard`, `ipset`, `iptables-legacy`) | Trafik inom sandlådan använder **noll systemanrop till värden**; extern trafik mappas till `net.Dial`/`net.Listen` (`-net=nat`) eller 1 UDP-uttag (`-net=wg`) |

När hanteraren returnerar `(rval, ctrl, err)` omvandlar `Task.doSyscallInvoke()` eventuella fel till ett negativt Linux-`errno` (`-ExtractErrno(err, sysno)`), skriver returvärdet tillbaka till den delade registersidan (`RAX` på `amd64`, `X0` på `arm64`) och återupptar gästens exekvering.

---

## Lagring (`VFS2`) och tvåspårig nätverkning i användarläge (`netstack`)

All gästens fil- och nätverksverksamhet hanteras inuti Go-kärnan `Sentry` via tre `VFS2`-filsystem och två `pkg/netbackend`-spår:

![gvx VFS2-lagringsmotorer och tvåspårig netstack](/assets/images/gvx/gvx-storage-netstack.png)

| Lagringsmotor          | Monteringsroll i `gvx`                           | Underliggande värdresurs               | Värdens `openat`- / `NtCreateFile`-anrop vid körning | Windows Defender (`WdFilter.sys`) skanningar per fil |
| :--------------------- | :----------------------------------------------- | :------------------------------------- | -----------------------------------------------: | -----------------------------------------------: |
| **`fsimpl/erofs`**     | Skrivskyddade nedre lager för OCI-avbilder       | En enda `<sha256>.erofs`-fil per lager |                                              `0` |                                              `0` |
| **`fsimpl/ext4lite`**  | Namngivna volymer (`-v name:/path`)              | En enda `<name>.ext4`-fil per volym    |                                              `0` |                                              `0` |
| **`fsimpl/hostfs` v2** | Värdarbetsyta (`-workspace`, `-v /host:/guest`)  | Fixerad värdkatalog som `*os.Root`     |                     `O(djup)` per ej cachad öppning |                          `1` per värdfil som stängs |

### Varför `ext4lite` är viktigt för Docker, databaser och paketdomare

Att extrahera eller skriva tiotusentals containerfiler direkt till Windows NTFS eller macOS APFS utlöser skanningar av antivirusfilterdrivrutinen (`WdFilter.sys`), kollisioner mellan versaler och gemener samt saknade utökade POSIX-attribut.

`pkg/sentry/fsimpl/ext4lite` implementerar ett rent Go-baserat `ext4`-filsystem inuti en enda förtöppnad värdfil (`%LOCALAPPDATA%\gvx\cache\volumes\<name>.ext4` på Windows, `~/Library/Caches/gvx/volumes/<name>.ext4` på macOS eller `~/.cache/gvx/volumes/<name>.ext4` på Linux) med:

- **Block om 4 KiB och dynamiska blockgrupper om 16 MiB (`meta_bg`)**: Växer dynamiskt utan stopp orsakade av allokering av glesa filer (`fallocate` på Linux, `fcntl(F_PREALLOCATE)` på macOS, `FileAllocationInfo` på Windows) och packar ihop oanvända bakre grupper vid ren avmontering.
- **JBD2-journal i ordnat läge (inod 8)**: Atomära transaktionsbekräftelser med CRC-32-kontrollsummor och återkallningsblock, samt återställning via `fsckLite`.
- **Stöd för `overlayfs` övre lager**: Implementerar utökade attribut `trusted.overlay.*` och `security.capability`, teckenenhets-whiteouts (`0:0`), rapportering av `d_type` för katalogposter samt `RENAME_EXCHANGE`.

---

## Praktiska arbetslaster: Docker-motor, Kubernetes (`k3s` + `Calico`) och Go-SDK

### Installera `rungv`: SLSA-attesterade förbyggda utgåvor eller `go install`

Du kan ladda ner ett förbyggt `rungv`-arkiv för `linux/amd64`, `linux/arm64`, `darwin/arm64`, `windows/amd64` eller `windows/arm64` från [GitHub Releases](https://github.com/gfire-sigs/gvx/releases) (vart och ett byggt av SLSA-release-pipelinen med ett Sigstore-provenansbuntel och `SHA256SUMS`), eller installera direkt från källkoden med `go install`:

```sh
# Alternativ A: Ladda ner en förbyggd utgåva och verifiera SLSA-byggprovenans
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

# Alternativ B: Installera direkt via Go (Linux / Windows)
CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest
```

> På macOS (`darwin/arm64`) är det förbyggda utgåvearkivet redan signerat med rättigheten `com.apple.security.hypervisor` (`xattr -d com.apple.quarantine rungv` tar bort webbläsarens karantän). Om du bygger lokalt med `go install` eller `go build` på macOS signerar du binären med `codesign --sign - --entitlements entitlements.plist --force $(which rungv)`.

### 1. Köra en obehörig Docker-motor (`docker:dind`)

Enligt [`docs/docker-engine.md`](https://github.com/gfire-sigs/gvx/blob/main/docs/docker-engine.md) kan du köra en oförändrad `docker:dind`-container inuti `rungv` på Windows, macOS eller Linux och styra den med din värds `docker`-CLI:

```sh
# 1. Skapa värdkatalogen för det vidarebefordrade Unix-uttaget
mkdir -p ~/.gvx

# 2. Starta docker:dind inuti en gvx-sandlåda med en ext4lite-volym
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
```

I en andra terminal pekar du din värds `docker`-CLI mot sandlådans uttag:

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

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

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

### 2. Köra Kubernetes (`k3s` + `Calico`) i obehöriga sandlådor

Eftersom `gvx` implementerar `/dev/kmsg`, `cgroup` v2 (OOM-dödaren `memory.max` och bandbreddsbegränsningen `cpu.max`), `/proc/diskstats`, netlink-enheterna `wireguard` och `vxlan`, `ipset` samt `iptables-legacy` (`addrtype`, `conntrack`, `statistic`, `recent`), når en oförändrad `rancher/k3s`-avbild tillståndet `Ready` inuti en enda `rungv`-process:

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

Du kan även ansluta flera `gvx`-noder över WireGuard (`flannel-backend=wireguard-native` eller ett `wg0`-nät) med inbäddad `etcd` för hög tillgänglighet, eller köra **Calico** (`calico-vxlan.yaml` med `FELIX_IPTABLESBACKEND=Legacy`) för att helt i användarläge upprätthålla Kubernetes `NetworkPolicy`.

### 3. Bädda in Linux-containrar i Go via `z.gfire.dev/gvx/pkg/gvx`

Go-applikationer kan bädda in Linux-containrar direkt utan en extern containerdaemon:

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

---

## Vidare läsning i denna serie

1. **[gvx: Köra Linux-containrar, Docker och Kubernetes i en enda obehörig process](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)** (detta inlägg)
2. **[Köra Docker, docker build och docker compose utan root eller användarnamnrymder](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)**: Hur man startar `docker:dind` i låsta sandlådor där `sudo`, `unshare(CLONE_NEWUSER)`, `mount(2)` och `/dev/fuse` är blockerade.
3. **[Köra Docker-in-Docker inuti gVisor och kapslade sandlådor med gvx](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)**: Varför `apt-get install docker.io` och kapslad `runsc` misslyckas inuti `gVisor` över fem kärnbarriärer, och hur `gvx` kör kapslad `docker:dind` med en futex-slinga i delat minne.
4. **[Snabba isolerade Docker-miljöer för RL-utrullningar av AI-agenter med gvx](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)**: Skalning av `SWE-bench` och förstärkningsinlärning för agenter med hundratals parallella obehöriga `docker:dind`-instanser och kopieringsvid-skrivning-återställningar av `.ext4`-volymer på 100 millisekunder.
