---
id: e62d78ef5850c982dbddc9d3ff9134ae
author: Lemon Mint
title: 'gvx: Ausführung von Linux-Containern, Docker und Kubernetes in einem einzigen unprivilegierten Prozess'
description: Technische Übersicht zu gvx, einer sicherheitsgehärteten, plattformübergreifenden Container-Laufzeitumgebung und Go-SDK, die auf dem gVisor-Kernel im Benutzerraum aufbaut und Linux-Container, Docker-in-Docker sowie hochverfügbare Multi-Node-Kubernetes-Cluster innerhalb eines einzigen nicht privilegierten Prozesses sicher ausführt, und zwar unter Windows, macOS und Linux.
language: de
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
---

## Einführung: Sicherheitsgehärtete Container ohne Root oder Host-Daemons

Das Ausführen von Linux-Containern, `docker build` oder Kubernetes innerhalb eines Entwicklungsrechners, eines CI-Runners oder eines Multi-Tenant-Cloud-Knotens erzwingt üblicherweise einen unangenehmen Sicherheitskompromiss:

- Unter **Linux** erfordern Standard-OCI-Runtimes (`runc`, `crun`, `runsc`) entweder `root`-Rechte, die Exposition des Host-Sockets `/var/run/docker.sock`, `--privileged` (`CAP_SYS_ADMIN`) oder User Namespaces (`unshare(CLONE_NEWUSER)`), die die Angriffsfläche des Host-Kernels vergrößern.
- Unter **Windows** und **macOS** starten Container-Engines (`Docker Desktop`, `Podman Machine`, `Lima`, `OrbStack`) einen vollständigen Linux-Virtual-Machine-Kernel innerhalb von WSL2, Hyper-V oder `Virtualization.framework` und betreiben in dieser VM einen persistenten Hintergrunddaemon.

**[gvx](https://github.com/gfire-sigs/gvx)** (`rungv` CLI und das `z.gfire.dev/gvx/pkg/gvx` Go SDK) bietet eine **sichere Alternative ohne Privilegien**. Als upstream-nachverfolgbarer Fork des User-Space-Linux-Kernels `Sentry` von Googles **gVisor** und des User-Space-Netzwerkstacks `netstack` konzipiert, führt `gvx` unveränderte `linux/amd64`- und `linux/arm64`-ELF64-Binärdateien sowie OCI-Container direkt innerhalb **eines einzigen unprivilegierten, selbst gesperrten Host-Prozesses** unter Windows, macOS und Linux aus.

> **Sicherheitsgarantie (Drei-Ring-Defense-in-Depth)**: `gvx` schwächt niemals die Sicherheitsgrenzen des Host-Betriebssystems, installiert niemals Kernel-Treiber und erfordert niemals `root`, `sudo`, `--privileged` auf dem Host oder `CLONE_NEWUSER`. Bevor auch nur eine einzige Gast-Instruktion ausgeführt wird, sperrt `gvx` seinen eigenen Host-Prozess mithilfe von Windows Restricted Tokens und Job Objects, macOS Seatbelt (`deny default`) oder Linux `PR_SET_NO_NEW_PRIVS` + BPF-`seccomp`. Alle Gast-Systemaufrufe, virtuellen `UID 0`-Privilegien, Mounts (`VFS2`) und Netzwerktabellen (`netstack`) sind strikt innerhalb des speichersicheren Go-`Sentry`-Kernels eingeschlossen.

| Runtime                    | Host-Betriebssystem                                   |           Kaltstart | Speicherbedarf   | Administrator- oder `sudo`-Rechte nötig | Hintergrund-VM-Daemon | Direkte Ausführung von OCI-Images |
| :------------------------- | :---------------------------------------------------- | ------------------: | ---------------: | :-------------------------------------- | :-------------------- | :-------------------------------- |
| **`gvx` (`whpx` / `hvf`)** | Windows (`amd64`, `arm64`) & macOS (`arm64`)          |           `~20 ms` | `~30 MiB`        | Nein                                    | Nein                  | Ja                                |
| **`gvx` (`systrap`)**      | Windows (`amd64`, `arm64`) & Linux (`amd64`, `arm64`) |     `~25 bis 65 ms` | `~31 MiB`        | Nein                                    | Nein                  | Ja                                |
| **WSL2 / Docker Desktop**  | Windows & macOS                                       |   `800 bis 2500 ms` | `> 512 MiB`      | Ja (Ersteinrichtung)                    | Ja                    | Erfordert Daemon                  |
| **QEMU (Full System)**     | Windows & macOS                                       |  `1500 bis 4000 ms` | `> 256 MiB`      | Nein                                    | Nein                  | Erfordert Disk Image              |

Auf Windows oder macOS wird kein Linux-VM-Kernel gestartet. Auf dem Host läuft kein Hintergrunddaemon. Für das Host-Betriebssystem sind keine `root`-, `sudo`-, User-Namespace- (`CLONE_NEWUSER`), `mount(2)`-, `TUN`/`TAP`-Treiber- oder `CAP_NET_ADMIN`-Privilegien erforderlich. Innerhalb dieses unprivilegierten Prozesses führt `gvx` **Docker-in-Docker (`docker:dind`)**, `docker build`, `docker compose` sowie **hochverfügbares Multi-Node-Kubernetes (`k3s` mit eingebettetem `etcd` und `Calico` CNI)** aus.

---

## Container-Lebenszyklus und Drei-Ring-Host-Sperrung

Jede `gvx`-Sandbox läuft innerhalb eines unprivilegierten Host-Prozesses und durchläuft einen dreiphasigen Lebenszyklus: Vorab-Öffnung aller erforderlichen Host-File-Deskriptoren und Sockets, Sperrung des Host-Betriebssystemprozesses über `pkg/hostlockdown` und Eintritt in die prozessinterne `Sentry`-Ausführungsschleife.

![gvx Container Lifecycle and Host Self-Lockdown](/assets/images/gvx/gvx-lifecycle-lockdown.png)

Bevor die Kontrolle an die erste Gast-Instruktion übergeben wird, öffnet `gvx` vorab seine zwischengespeicherten `.erofs`-Image-Layer-Deskriptoren, `.ext4`-Volume-Deskriptoren, das `/workspace`-`*os.Root`-Handle und Host-Netzwerk-Sockets und ruft anschließend `hostlockdown.Apply()` auf:

1. **Windows NT (`lockdown_windows.go`)**: Ruft `AdjustTokenPrivileges` mit `SE_PRIVILEGE_REMOVED` auf, um Privilegien direkt zu entfernen, bindet ein `DISABLE_MAX_PRIVILEGE`-eingeschränktes Impersonierungstoken mit Low-Integrität (`S-1-16-4096`), weist den Prozess einem anonymen Job Object mit `JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 1` zu (wodurch die Erstellung jedes Kindprozesses blockiert wird) und aktiviert `DisallowWin32kSystemCalls = 1` über `SetProcessMitigationPolicy`.
2. **macOS XNU (`lockdown_darwin.go`)**: Setzt `RLIMIT_NPROC = 0` und wendet ein Seatbelt-Profil (`sandbox_init_with_parameters`) an, das mit `(version 1)(deny default)` beginnt und `process-fork`, `process-exec*` sowie alle neuen Host-Dateiöffnungen blockiert.
3. **Linux (`lockdown_linux.go` + `pkg/gvx/syscallfilter_linux.go`)**: Setzt `PR_SET_NO_NEW_PRIVS` und installiert die Sentry-BPF-`seccomp`-Allowlist über alle Threads (`SECCOMP_FILTER_FLAG_TSYNC`) mit expliziten `EPERM`-Regeln für `execve`, `execveat`, `clone`, `openat` und `socket`.

---

## Plattformübergreifendes Abfangen von Systemaufrufen und 4-KB-Paging (`pkg/sentry/platform`)

Linux-ELF64-Binärdateien setzen für `mmap(MAP_FIXED)`-Segmentzuordnungen eine Speicherseitengranularität von 4 KB (`0x1000`) voraus. Windows `VirtualAlloc` und `MapViewOfFile3` erzwingen jedoch eine Ausrichtung auf 64 KB (`0x10000`), und Apple-Silicon-macOS (`darwin/arm64`) verwendet 16-KB-Host-Seiten (`0x4000`). Darüber hinaus benötigt jedes Betriebssystem einen anderen Mechanismus, um Linux-`SYSCALL`- (`amd64`) und `SVC #0`-Instruktionen (`arm64`) abzufangen, ohne eine Linux-VM zu starten.

`gvx` löst beide Probleme über vier Ausführungs-Backends, die `pkg/sentry/platform.Context` implementieren:

![gvx Platform Syscall Interception and 4KB Paging Mechanics](/assets/images/gvx/gvx-platform-syscalls.png)

### 1. Windows-Hardware-Engine: `platform/whpx` (`WinHvPlatform.dll`)

Auf `windows/amd64` und `windows/arm64` erstellt `platform/whpx` über `WHvCreatePartition` eine leichtgewichtige Hypervisor-Partition, ohne einen Gast-Betriebssystemkernel zu booten:

- **64-KB-zu-4-KB-Paging-Entkoppler**: `whpx` registriert jede 64-MiB-Arena von `pgalloc.MemoryFile` einmalig über `WHvMapGpaRange` bei der Partition. Nachfolgende `MapFile`- und `Unmap`-Aufrufe ändern lediglich die Gastseitentabellen innerhalb der Partition und erzwingen exakte 4-KB-`R/W/X`-Berechtigungen trotz der 64-KB-Allokationsgranularität von Windows.
- **Registeraustausch über `CommPage` ohne API-Aufrufe**:
  - Auf `amd64` gelangt ein `SYSCALL` aus Ring 3 des Gastes in einen innerhalb der Partition liegenden Ring-0-`MSR_LSTAR`-Stub (`stubgen_x86.go`), der die GPRs für den Fast-Path-Systemaufruf (`RAX, RDI, RSI, RDX, R10, R8, R9, RIP, RSP`) in eine gemeinsame 4-KB-`CommPage` schreibt und `HLT` ausführt, um `WHvRunVirtualProcessor` zu verlassen.
  - Auf `arm64` gelangt ein `SVC #0` aus EL0 des Gastes in die EL1-Vektoren (`stubgen_a64.go`), schreibt `X0..X8, SP_EL0, ELR_EL1, PSTATE` in die `CommPage` und lädt von einer nicht hinterlegten GPA-Null, um einen Speicherexit zurück zum Sentry auszulösen.

### 2. macOS-Hardware-Engine: `platform/hvf` (`Hypervisor.framework`)

Auf Apple Silicon (`darwin/arm64`) führt `platform/hvf` Container-Threads innerhalb von `Hypervisor.framework`-vCPUs aus (`hv_vm_create`, `hv_vcpu_run`):

- **Zweistufige 4-KB-über-16-KB-MMU-Übersetzung**: Stage-2 (`hv_vm_map`) bildet 1-GiB-Chunks der Memory-File einmalig mit 16-KB-Host-Seitengranularität in den Intermediate-Physical-Address-(IPA)-Raum ab, mit `HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC`. Innerhalb der vCPU konfiguriert der EL1-`bluepill`-Stub (`bluepill_arm64.go`) `TCR_EL1.TG0 = 0b00` (4-KB-Granule) in `TTBR0_EL1` und befüllt 4-KB-Stage-1-Seitentabelleneinträge mit exakten `R-X`- oder `RW-` (`UXN=1`)-Berechtigungen. Da die ARM64-MMU die Schnittmenge der Stage-1- und Stage-2-Berechtigungen durchsetzt, fängt die Hardware Berechtigungsfehler exakt an 4-KB-Grenzen ab.
- **Systemaufruf-Trap**: Ein `SVC #0` aus EL0 des Gastes trifft auf EL1 `VBAR_EL1`, schreibt Register in die gemeinsame 4-KB-`CommPage` und führt `HVC #0` aus, um aus `hv_vcpu_run` in den Go-Sentry-Thread zurückzukehren.

### 3. Windows-User-Mode-Fallback: `platform/systrap` (`winsystrap`)

Wenn Hardwarevirtualisierung nicht verfügbar ist (etwa innerhalb verschachtelter Cloud-VMs), führt `winsystrap` Gastcode im User Mode auf `windows/amd64` und `windows/arm64` aus:

- **Hardware-`CR3`- / `TTBR0_EL1`-Isolation**: Gast-Threads laufen innerhalb isolierter Stub-Subprozesse, die aus einer generierten 1-KB-ausführbaren Datei (`stubimage_xplat.go`) gestartet werden und durch ein Low-Integrity-Token und ein Job Object eingeschränkt sind.
- **Binärpatching an Ort und Stelle & `winSysmsg`-IPC**: Auf `amd64` ersetzt der Patcher (`patcher_amd64_xplat.go`) 2-Byte-`syscall`-Instruktionen (`0f 05`) durch 5-Byte-`jmp rel32`-Sprünge (oder durch `CC 90`-Breakpoint-Traps, wenn kein 5-Byte-Fenster verfügbar ist) in eine Fast-Path-Insel. Auf `arm64` wird `svc #0` durch einen `B`-Branch zu einem Inselslot ersetzt. Die Insel schreibt Register in die gemeinsame `winSysmsg`-Seite und synchronisiert sich mit dem Sentry über `WaitOnAddress` und `WakeByAddressSingle`, während nicht gepatchte Traps und Seitenfehler an einem Windows-Debug-Object (`WaitForDebugEventEx`) anhalten.

### 4. Linux-User-Mode-Engine: `platform/systrap`

Auf `linux/amd64` und `linux/arm64` führt `systrap` Gast-Threads (`sysmsg`) aus, die Gastsystemaufrufe über `SECCOMP_RET_TRAP` (`SIGSYS`) abfangen und `syscall`-Stellen binär in einen gemeinsam genutzten Speicher-Trampolin (`sysmsg`) patchen, der über `SYS_FUTEX` synchronisiert wird. Gleichzeitig führt ein Stub-`syscallThread` (`syscall_thread.go`) Adressraumoperationen (`mmap`, `munmap`, `clone`) über `SECCOMP_RET_USER_NOTIF` (`_RUN_SECCOMP_LOOP`) aus. Wenn `gvx` innerhalb einer äußeren `gVisor`-(`runsc`)-Sandbox läuft, in der der einzelne `SECCOMP_FILTER_FLAG_NEW_LISTENER`-Slot `EBUSY` zurückgibt, schaltet `gvx` `syscallThread` automatisch auf eine Futex-Schleife mit gemeinsamem Speicher (`_RUN_SYSCALL_LOOP`) um, wie in [Teil 3 dieser Serie](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03) beschrieben.

---

## Wie `gvx` Linux-Systemaufrufe des Gastes auf virtuelle Subsysteme und Host-Handles abbildet

Sobald eine Plattform-Engine eine `SYSCALL`- (`amd64`) oder `SVC #0`-Instruktion (`arm64`) des Gastes abfängt und die Gastregister in die 4-KB-gemeinsame Seite (`CommPage`, `sysmsg` oder `winSysmsg`) schreibt, kehrt die Kontrolle zur Go-`Sentry`-Task-Goroutine zurück (`pkg/sentry/kernel/task_syscall.go`). Statt Linux-Systemaufrufe an den zugrunde liegenden Host-Betriebssystemkernel weiterzuleiten, speichert `Task.doSyscall()` den ursprünglichen Registerzustand (`SyscallSaveOrig()`), wertet alle im Gast eingebetteten BPF-`seccomp`-Filter und `ptrace`-Stopps aus und leitet die Systemaufrufnummer über `linux.AMD64` oder `linux.ARM64` (`pkg/sentry/syscalls/linux/linux64.go`, das etwa 370 Linux-Systemaufrufe abdeckt) in vier interne Domänen weiter:

![How gvx Maps Guest Linux Syscalls to Subsystems and Host OS](/assets/images/gvx/gvx-syscall-mapping.png)

| Systemaufruf-Domäne | Repräsentative Linux-Systemaufrufe des Gastes | Interne `gvx`-Subsystem (`pkg/sentry`) | Interaktion mit der Host-OS-Grenze (nach `hostlockdown`) |
| :--- | :--- | :--- | :--- |
| **A. Prozess, Namespace, Mount & Synchronisation** | `clone`, `execve`, `unshare`, `setns`, `mount`, `umount2`, `pivot_root`, `setuid`, `capset`, `futex`, `epoll_wait`, `pipe2` | `kernel.Task`, virtuelle Credentials, Futex-Tabelle und im Speicher liegendes `VFS2` (`procfs`, `sysfs`, `cgroupfs` v2, `tmpfs`, `overlayfs`) | **Null Host-Systemaufrufe** (100 % im Go-Zustand im Speicher; für das Host-Betriebssystem vollständig unsichtbar) |
| **B. Virtueller Speicher (`MM`)** | `mmap`, `munmap`, `mremap`, `mprotect`, `brk`, `madvise`, `memfd_create` | `mm.MemoryManager` (4-KB-VMA-Baum) & `pgalloc.MemoryFile` | Bidirektionale 4-KB-Seitentabellenaktualisierungen über `WHvMapGpaRange`, `hv_vm_map` + `TTBR0_EL1` oder Stub-`mmap`/`mprotect` |
| **C. Dateien & Speicher (`VFS2`)** | `openat`, `pread64`, `pwrite64`, `getdents64`, `statx`, `renameat2`, `setxattr`, `mknod`, `inotify_add_watch` | Mount-Tabellen-Router `vfs.VirtualFilesystem` (`fsimpl/erofs`, `fsimpl/ext4lite`, `fsimpl/hostfs` v2) | Bidirektionales `pread`/`pwrite` auf 1 fixierten `.erofs`-FD oder 1 `.ext4`-FD oder eingeschränkte relative Pfadauflösungen auf `*os.Root` |
| **D. Netzwerk & Netfilter** | `socket`, `bind`, `listen`, `connect`, `sendmsg`, `recvmsg`, `netlink`, `setsockopt(IPT_SO_SET_REPLACE)` | Benutzerraum-`pkg/tcpip` (`lo`, `eth0`, `docker0`, `veth`, `vxlan`, `wireguard`, `ipset`, `iptables-legacy`) | Datenverkehr innerhalb der Sandbox verwendet **null Host-Systemaufrufe**; externer Datenverkehr wird auf `net.Dial`/`net.Listen` (`-net=nat`) oder 1 UDP-Socket (`-net=wg`) abgebildet |

Wenn der Handler `(rval, ctrl, err)` zurückgibt, wandelt `Task.doSyscallInvoke()` jeden Fehler in einen negativen Linux-`errno` um (`-ExtractErrno(err, sysno)`), schreibt den Rückgabewert zurück auf die gemeinsame Registerseite (`RAX` auf `amd64`, `X0` auf `arm64`) und setzt die Ausführung des Gastes fort.

---

## Speicher (`VFS2`) und zweigleisiger User-Space-Netzwerkbetrieb (`netstack`)

Alle Datei- und Netzwerkoperationen des Gastes werden innerhalb des Go-`Sentry` über drei `VFS2`-Dateisysteme und zwei `pkg/netbackend`-Stränge bedient:

![gvx VFS2 Storage Engines and Dual-Track Netstack](/assets/images/gvx/gvx-storage-netstack.png)

| Speicher-Engine        | Einhängerolle in `gvx`                           | Zugrunde liegende Host-Ressource       | Host-`openat`- / `NtCreateFile`-Aufrufe zur Laufzeit | Windows-Defender-(`WdFilter.sys`)-Scans pro Datei |
| :--------------------- | :----------------------------------------------- | :------------------------------------- | ---------------------------------------------------: | ------------------------------------------------: |
| **`fsimpl/erofs`**     | Schreibgeschützte untere OCI-Image-Layer         | Eine einzelne `<sha256>.erofs`-Datei pro Layer |                                                `0` |                                               `0` |
| **`fsimpl/ext4lite`**  | Benannte Volumes (`-v name:/path`)               | Eine einzelne `<name>.ext4`-Datei pro Volume   |                                                `0` |                                               `0` |
| **`fsimpl/hostfs` v2** | Host-Workspace (`-workspace`, `-v /host:/guest`) | Fixiertes `*os.Root`-Host-Verzeichnis          |                              `O(depth)` pro uncachierter Öffnung |                           `1` pro Schließen einer Host-Datei |

### Warum `ext4lite` für Docker, Datenbanken und Paketmanager wichtig ist

Das Entpacken oder Schreiben von Zehntausenden Container-Dateien direkt auf Windows NTFS oder macOS APFS löst Antivirus-Minifilter-Scans (`WdFilter.sys`), Kollisionen bei der Groß-/Kleinschreibung und fehlende erweiterte POSIX-Attribute aus.

`pkg/sentry/fsimpl/ext4lite` implementiert ein rein in Go geschriebenes `ext4`-Dateisystem innerhalb einer einzelnen vorab geöffneten Host-Datei (`%LOCALAPPDATA%\gvx\cache\volumes\<name>.ext4` unter Windows, `~/Library/Caches/gvx/volumes/<name>.ext4` unter macOS oder `~/.cache/gvx/volumes/<name>.ext4` unter Linux) mit:

- **4-KiB-Blöcke und dynamische 16-MiB-Blockgruppen (`meta_bg`)**: Wächst dynamisch, ohne Stalls durch Sparse-File-Allokation (`fallocate` unter Linux, `fcntl(F_PREALLOCATE)` unter macOS, `FileAllocationInfo` unter Windows), und verdichtet nicht genutzte nachlaufende Gruppen beim sauberen Aushängen.
- **JBD2-Journal im Ordered-Modus (Inode 8)**: Atomare Transaktionsbestätigungen mit CRC-32-Prüfsummen und Revoke-Blöcken, zusammen mit einer `fsckLite`-Wiederherstellung.
- **Unterstützung der `overlayfs`-Upper-Layer**: Implementiert die erweiterten Attribute `trusted.overlay.*` und `security.capability`, Character-Device-Whiteouts (`0:0`), `d_type`-Meldung für Verzeichniseinträge und `RENAME_EXCHANGE`.

---

## Praktische Workloads: Docker Engine, Kubernetes (`k3s` + `Calico`) und Go SDK

### Installation von `rungv`: SLSA-attestierte vorgefertigte Releases oder `go install`

Sie können ein vorgefertigtes `rungv`-Archiv für `linux/amd64`, `linux/arm64`, `darwin/arm64`, `windows/amd64` oder `windows/arm64` von [GitHub Releases](https://github.com/gfire-sigs/gvx/releases) herunterladen (jeweils erstellt durch die SLSA-Release-Pipeline mit einem Sigstore-Provenance-Bundle und `SHA256SUMS`) oder direkt aus dem Quellcode mit `go install` installieren:

```sh
# Option A: Vorgefertigtes Release herunterladen und SLSA-Build-Provenance verifizieren
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

# Option B: Direkte Installation über Go (Linux / Windows)
CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest
```

> Auf macOS (`darwin/arm64`) ist das vorgefertigte Release-Archiv bereits mit dem Berechtigungsanspruch `com.apple.security.hypervisor` signiert (`xattr -d com.apple.quarantine rungv` entfernt die Browser-Quarantäne). Wenn Sie lokal mit `go install` oder `go build` unter macOS bauen, signieren Sie die Binärdatei mit `codesign --sign - --entitlements entitlements.plist --force $(which rungv)`.

### 1. Ausführen einer unprivilegierten Docker Engine (`docker:dind`)

Gemäß [`docs/docker-engine.md`](https://github.com/gfire-sigs/gvx/blob/main/docs/docker-engine.md) können Sie einen unveränderten `docker:dind`-Container innerhalb von `rungv` unter Windows, macOS oder Linux ausführen und mit Ihrer Host-`docker`-CLI steuern:

```sh
# 1. Host-Verzeichnis für den weitergeleiteten Unix-Socket erstellen
mkdir -p ~/.gvx

# 2. docker:dind innerhalb einer gvx-Sandbox mit einem ext4lite-Volume starten
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 einem zweiten Terminal richten Sie Ihre Host-`docker`-CLI auf den Sandbox-Socket:

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

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

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

### 2. Ausführen von Kubernetes (`k3s` + `Calico`) in unprivilegierten Sandboxes

Da `gvx` `/dev/kmsg`, `cgroup` v2 (`memory.max`-OOM-Killer und `cpu.max`-Bandbreitendrosselung), `/proc/diskstats`, `wireguard`- und `vxlan`-Netlink-Geräte, `ipset` sowie `iptables-legacy` (`addrtype`, `conntrack`, `statistic`, `recent`) implementiert, erreicht ein unverändertes `rancher/k3s`-Image innerhalb eines einzigen `rungv`-Prozesses den Status `Ready`:

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

Sie können außerdem mehrere `gvx`-Knoten über WireGuard verbinden (`flannel-backend=wireguard-native` oder ein `wg0`-Mesh) mit eingebettetem `etcd` für Hochverfügbarkeit oder **Calico** (`calico-vxlan.yaml` mit `FELIX_IPTABLESBACKEND=Legacy`) ausführen, um Kubernetes-`NetworkPolicy` vollständig im Benutzerraum durchzusetzen.

### 3. Einbetten von Linux-Containern in Go über `z.gfire.dev/gvx/pkg/gvx`

Go-Anwendungen können Linux-Container direkt ohne externen Container-Daemon einbetten:

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

---

## Weiterführende Literatur in dieser Serie

1. **[gvx: Ausführen von Linux-Containern, Docker und Kubernetes in einem einzigen unprivilegierten Prozess](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)** (dieser Beitrag)
2. **[Ausführen von Docker, docker build und docker compose ohne Root oder User Namespaces](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)**: Wie Sie `docker:dind` in abgesicherten Sandboxes booten, in denen `sudo`, `unshare(CLONE_NEWUSER)`, `mount(2)` und `/dev/fuse` blockiert sind.
3. **[Ausführen von Docker-in-Docker innerhalb von gVisor und verschachtelten Sandboxes mit gvx](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)**: Warum `apt-get install docker.io` und verschachteltes `runsc` innerhalb von `gVisor` an fünf Kernel-Barrieren scheitern und wie `gvx` verschachteltes `docker:dind` über eine Futex-Schleife mit gemeinsamem Speicher ausführt.
4. **[Schnelle isolierte Docker-Umgebungen für RL-Rollouts von KI-Agenten mit gvx](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)**: Skalierung von `SWE-bench` und Reinforcement-Learning-Rollouts von Agenten mit Hunderten paralleler unprivilegierter `docker:dind`-Instanzen und 100-Millisekunden-Copy-on-Write-`.ext4`-Volume-Resets.
