# Claude Code im Container: Podman, Docker und geteilte Setups

> Wie du Claude Code in Podman (rootless/rootful) und Docker betreibst, Container und ~/.claude-Ordner zwischen mehreren Nutzern teilst und Rechte mit Gruppen, setgid und Default-ACLs sauber setzt.

Source: https://www.jpkc.com/db/blog/claude-code-container/

Claude Code läuft am schnellsten dort installiert, wo du arbeitest: direkt auf dem Host. Aber „direkt auf dem Host" ist nicht immer die beste Idee. Sobald du den Agenten unbeaufsichtigt arbeiten lässt, ihn an fremdem Code ansetzt oder ihn mehreren Menschen auf einer Maschine zur Verfügung stellen willst, wird ein Container von der Kür zur Pflicht. Dieser Artikel zeigt dir die ganze Bandbreite: vom offiziellen Devcontainer über selbstgebaute Podman- und Docker-Setups bis zu dem Szenario, das mich am meisten interessiert hat — **eine** Claude-Code-Installation, die sich mehrere Benutzerkonten auf demselben Rechner teilen, inklusive gemeinsam genutzter `~/.claude`-Konfiguration.

Das ist bewusst ein einsteigerfreundlicher Rundumschlag. Wenn du schon Container-Profi bist, kannst du die Grundlagenkapitel überspringen und direkt zu den Rechte- und Sharing-Themen springen. Wenn nicht, nehme ich dich an die Hand: Jeder Begriff wird eingeordnet, jeder Befehl erklärt.

## Warum Claude Code überhaupt in einen Container?

Claude Code ist ein agentisches Werkzeug. Es liest Dateien, führt Shell-Befehle aus, ruft Tools auf — und je weniger es dich dabei um Erlaubnis fragen muss, desto produktiver ist es, aber desto größer ist auch der Radius, den ein Fehler oder ein böswilliger Prompt anrichten kann. Genau hier setzt Containerisierung an. Sie zieht eine Grenze um das, was der Agent sehen, schreiben und erreichen kann.

Vier konkrete Gründe, warum sich der Aufwand lohnt:

- **Reproduzierbarkeit.** Ein Container-Image definiert die exakte Umgebung — Node-Version, Tools, Abhängigkeiten — für alle gleich. „Bei mir läuft's" hört auf, ein Argument zu sein.
- **Isolation bei autonomem Betrieb.** Erst eine Grenze um den ganzen Prozess macht Modi wie `--dangerously-skip-permissions` oder den unbeaufsichtigten Lauf vertretbar. Ohne Container ist dieser Schalter im Klartext ein Freibrief auf dein gesamtes Home-Verzeichnis.
- **Arbeit an nicht vertrautem Code.** Ein fremdes Repository kann Prompt-Injection-Fallen enthalten. Im Container kann der Schaden das Projektverzeichnis und das, was die Netz-Policy erlaubt, kaum verlassen.
- **Eine Installation für viele.** Statt Claude Code in jedem Benutzerkonto einzeln zu installieren und zu pflegen, stellst du es einmal als Image bereit. Das ist der rote Faden der zweiten Artikelhälfte.

Anthropic empfiehlt Container ausdrücklich als zusätzliche Isolationsschicht und vergibt einen wichtigen Grundsatz gleich mit: Isolation senkt den Schaden eines Einbruchs, beseitigt das Risiko aber nicht. Was an die Anthropic-API geht, geht mit oder ohne Container an die API. Ein Container ist eine Wand, kein Zauber.

## Wie das grundsätzlich aussieht

Bevor wir in Varianten einsteigen, das gemeinsame Grundmuster. Egal welcher Ansatz: Drei Dinge treffen im Container zusammen.

1. **Dein Projektverzeichnis** wird per *Bind-Mount* in den Container gehängt. Der Container sieht deinen Code, und Änderungen, die Claude macht, erscheinen sofort in deinem echten Repository auf dem Host. Der Code „zieht nicht um", er wird nur durchgereicht.
2. **Claude Code selbst** ist im Image installiert und läuft als Prozess *innerhalb* der Grenze. Alle Shell-Befehle, die der Agent absetzt, laufen im Container — nicht auf deinem Host.
3. **Der Zustand unter `~/.claude`** — Anmeldung, Einstellungen, Verlauf, Skills — liegt entweder in einem mitgemounteten Verzeichnis oder in einem Container-Volume, damit er einen Neustart überlebt.

Editoren wie VS Code, Cursor oder JetBrains, die den Dev-Containers-Standard unterstützen, klinken sich in genau diesen Container ein: Du editierst wie gewohnt in der Oberfläche, aber Terminal, Sprachserver und Build-Tools laufen drinnen. Ein reiner Terminal-Workflow (`podman run … claude`) funktioniert genauso — nur ohne IDE-Anbindung.

Merke dir die zwei beweglichen Teile: **das Projekt** (kommt vom Host rein, schreibbar) und **die Config** (`~/.claude`, persistiert). Fast alle Feinheiten in diesem Artikel drehen sich darum, wer mit welchen Rechten auf diese beiden Teile zugreifen darf.

## Die Isolationsstufen im Überblick

Container sind nicht die einzige Möglichkeit, Claude Code einzuhegen — und nicht für jeden Zweck die richtige. Anthropic beschreibt eine ganze Leiter von Isolationsstufen, von leichtgewichtig bis maximal getrennt. Es lohnt sich, sie zu kennen, damit du nicht mit Kanonen auf Spatzen schießt:

| Ansatz | Was isoliert wird | Docker nötig? | Aufwand |
|---|---|---|---|
| **Bash-Sandbox** (`/sandbox`) | Nur Bash-Befehle und ihre Kindprozesse | Nein | Minimal |
| **Sandbox-Runtime** | Der ganze Claude-Code-Prozess (Datei-Tools, MCP, Hooks) | Nein | Gering |
| **Devcontainer** | Komplette Entwicklungsumgebung | Ja | Mittel |
| **Custom Container** | Komplette Entwicklungsumgebung | Ja | Mittel–hoch |
| **Virtuelle Maschine** | Vollständiges Betriebssystem | Nein | Hoch |
| **Claude Code on the web** | Vollständiges OS, von Anthropic gehostet | Nein | Keiner (Abo nötig) |

Die ersten beiden laufen ohne Container direkt auf dem Host-Betriebssystem. Die **Bash-Sandbox** (eingebaut, aktivierbar mit `/sandbox`) nutzt OS-Primitive — `bubblewrap` unter Linux und WSL2, Seatbelt unter macOS — und beschränkt nur die Shell-Befehle. Datei-Tools, MCP-Server und Hooks laufen weiter ungebremst. Sie ist ideal, um im Alltag auf der eigenen Maschine weniger Erlaubnis-Prompts zu sehen, aber sie reicht für vollautonomen Betrieb nicht aus.

Die **Sandbox-Runtime** (`@anthropic-ai/sandbox-runtime`, ein Forschungs-Preview) packt denselben `bubblewrap`/Seatbelt-Käfig um den *gesamten* Prozess. Sie verweigert standardmäßig alle Schreib- und Netzzugriffe; du erlaubst in `~/.srt-settings.json` gezielt dein Projektverzeichnis, die Config-Pfade `~/.claude` und `~/.claude.json` sowie die nötigen Domains. Gestartet wird mit `npx @anthropic-ai/sandbox-runtime claude`.

Alles ab **Devcontainer** abwärts steckt Claude Code in einen Container oder eine VM. Genau diese Stufen sind das Thema dieses Artikels. Die VM bietet die stärkste Trennung (eigener Kernel) und ist die Wahl für wirklich nicht vertrauenswürdigen Code; **Claude Code on the web** ist eine von Anthropic gehostete VM mit Egress-Proxy für alle, die gar keine lokale Umgebung aufsetzen wollen.

> **Merksatz:** Permission-Modi (was darf laufen, fragt es vorher?) und Isolation (was kann ein Befehl erreichen, wenn er läuft?) sind zwei verschiedene Dinge, die zusammenarbeiten. `--dangerously-skip-permissions` schaltet die Einzelfreigabe ab — dann ist die Isolationsgrenze das Einzige, was den Agenten noch begrenzt. Diesen Schalter deshalb **nur** in Container, VM oder Sandbox-Runtime verwenden.

## Grundbegriffe für Einsteiger

Vier Begriffe begegnen dir ab hier ständig. Wenn du sie schon kennst, spring zum nächsten Kapitel.

**Rootless vs. rootful.** Klassisch (rootful) läuft der Container-Daemon als `root`, und Prozesse im Container sind echtes `root` auf dem Host — eine bekannte Angriffsfläche. *Rootless* bedeutet: Container laufen unter deinem normalen Benutzerkonto, ganz ohne Daemon mit Root-Rechten. Podman kann beides, ist aber für den rootless-Betrieb gebaut; Docker ist traditionell rootful (es gibt auch einen rootless-Modus, der hier aber Nebenschauplatz ist).

**User-Namespaces.** Der Mechanismus hinter rootless. Ein [User-Namespace](https://de.wikipedia.org/wiki/Linux-Namespaces) bildet Benutzer-IDs *im* Container auf andere IDs *auf dem Host* ab. `root` (UID 0) im Container ist dann z. B. dein Host-User (UID 1000) — drinnen mächtig, draußen harmlos. Diese Abbildung ist der Grund, warum Dateirechte zwischen Host und Container manchmal überraschen.

**UID/GID.** Linux kennt Benutzer und Gruppen nur als Zahlen: die *User-ID* (UID) und die *Group-ID* (GID). Namen wie `alice` oder `shared` sind nur Etiketten, die über `/etc/passwd` bzw. `/etc/group` auf diese Zahlen zeigen. Für Dateirechte zählt immer die Zahl. Das wird gleich wichtig, weil ein Gruppenname, den der Host kennt, im Container-Image oft gar nicht existiert — die *numerische* GID dagegen funktioniert überall.

**Bind-Mount vs. Named Volume.** Ein *Bind-Mount* hängt einen konkreten Host-Pfad in den Container (`-v /home/alice/projekt:/workspace`) — ideal für deinen Code und für geteilte Config. Ein *Named Volume* ist ein von der Container-Engine verwalteter Speicher ohne festen Host-Pfad (`-v claude-config:/home/node/.claude`) — ideal, um Zustand über Container-Neubauten hinweg zu erhalten, etwa im Devcontainer.

## Ansatz A — Der offizielle Devcontainer

Anthropic liefert einen fertigen Weg, Claude Code in einen [Dev Container](https://code.claude.com/docs/en/devcontainer) zu bringen. Das ist der bequemste Einstieg, wenn du mit VS Code, Codespaces oder einer JetBrains-IDE arbeitest, und gleichzeitig die Referenz, an der wir die selbstgebauten Setups messen.

Im einfachsten Fall ist es ein Dreizeiler. Du legst `.devcontainer/devcontainer.json` an und ziehst das **Claude Code Dev Container Feature** herein:

```json
{
	"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
	"features": {
		"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}
	}
}
```

Der Versions-Tag `:1.0` pinnt das Install-Skript des Features, nicht die Claude-Code-Version — das Feature installiert immer das aktuelle Claude Code, und das aktualisiert sich im Container per Default selbst. „Rebuild Container", `claude` im integrierten Terminal starten, im Browser anmelden — fertig.

Drei Bausteine machen aus diesem Minimal-Setup eine ernstzunehmende Isolation; sie stecken im **Referenz-Container** des [`anthropics/claude-code`-Repos](https://github.com/anthropics/claude-code/tree/main/.devcontainer) (drei Dateien: `devcontainer.json`, `Dockerfile`, `init-firewall.sh`):

**1. Persistente Anmeldung.** Das Home-Verzeichnis des Containers wird bei jedem Rebuild verworfen — du müsstest dich jedes Mal neu anmelden. Ein Named Volume auf `~/.claude` löst das:

```json
"mounts": [
	"source=claude-code-config,target=/home/node/.claude,type=volume"
]
```

`/home/node` ist das Home des Standard-Users `node` im Image. Willst du den Zustand pro Projekt trennen statt ihn über alle Repos zu teilen, nimmst du `source=claude-code-config-${devcontainerId}`. Liegt das Volume woanders als unter `~/.claude`, setzt du `CLAUDE_CONFIG_DIR` auf den Mount-Pfad.

**2. Netz-Egress-Firewall.** Der Referenz-Container bringt ein Skript `init-firewall.sh` mit, das per `iptables` allen ausgehenden Verkehr blockt — bis auf die Domains, die Claude Code und deine Tools brauchen (Standard ist „default deny"). Eine Firewall im Container zu betreiben braucht Extra-Rechte, deshalb gibt die Referenz über `runArgs` die Capabilities `NET_ADMIN` und `NET_RAW` mit. Beides ist für Claude Code selbst nicht zwingend — du kannst es weglassen und dich auf eigene Netzkontrollen verlassen.

**3. Non-Root-User.** Der Container läuft als Nicht-Root-Benutzer (`node`), und das ist die Voraussetzung für den entscheidenden Komfort: Weil die Befehlsausführung im Container eingehegt ist *und* nicht als root läuft, darfst du `--dangerously-skip-permissions` für den unbeaufsichtigten Betrieb setzen. Die CLI **verweigert** diesen Schalter, wenn sie als root gestartet wird — `remoteUser` muss also ein Nicht-Root-Konto sein.

Organisations-Policy lässt sich elegant zentral verankern: Claude Code liest unter Linux `/etc/claude-code/managed-settings.json` mit höchster Priorität (überschreibt alles aus `~/.claude` oder dem Projekt). Per `containerEnv` setzt du globale Variablen, etwa `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` und `DISABLE_AUTOUPDATER`.

### Wo der Devcontainer aufhört

Der offizielle Devcontainer ist exzellent für **einen Entwickler an einem Projekt mit einer IDE**. Genau dort liegen aber auch seine Grenzen, und die sind der Grund für den Rest dieses Artikels:

- Er ist **an einen Editor gebunden.** Reiner Terminal-Betrieb, Cron-Jobs, ein gemeinsamer Server-Login ohne IDE — dafür ist er nicht gedacht.
- Er denkt **pro Nutzer und pro Projekt**, nicht „eine Installation, viele Menschen". Das Volume `claude-code-config` gehört dem Container, nicht einer geteilten Host-Gruppe.
- Anthropic warnt ausdrücklich: Mit `--dangerously-skip-permissions` kann ein bösartiges Projekt **alles** exfiltrieren, was im Container erreichbar ist — **auch die in `~/.claude` gespeicherten Anmeldedaten**. Mounte deshalb keine Host-Secrets wie `~/.ssh` oder Cloud-Credential-Dateien hinein.

Den letzten Punkt nimm ernst: Er ist der Kern des Sharing-Problems, das wir gleich angehen. Wer Zugriff auf `~/.claude` hat, hat potenziell Zugriff auf das OAuth-Token.

## Ansatz B — Rootless Podman

[Podman](https://www.jpkc.com/db/cheatsheets/containers/podman/) im rootless-Modus ist mein bevorzugter Weg für selbstgebaute Setups: kein Daemon, keine Root-Rechte, Container laufen unter deinem normalen Konto. Du brauchst keine IDE und keinen Devcontainer-Standard — ein `podman run` genügt.

Ein minimales Dockerfile (Podman liest dasselbe Format):

```dockerfile
FROM node:22-bookworm-slim

# Create a non-root user; Claude Code refuses --dangerously-skip-permissions as root
RUN useradd --create-home --shell /bin/bash dev

# Install Claude Code globally, pinned for reproducible builds
RUN npm install -g @anthropic-ai/claude-code@2.0.0

USER dev
WORKDIR /workspace
ENTRYPOINT ["claude"]
```

Bauen und starten:

```bash
podman build -t claude-code:local .

podman run --rm -it \
	-v "$PWD":/workspace:Z \
	-v "$HOME/.claude":/home/dev/.claude:Z \
	claude-code:local
```

Zwei Details, die Einsteiger oft stolpern lassen:

- Das **`:Z`** am Mount weist Podman an, das SELinux-Label des Verzeichnisses passend umzuschreiben. Auf SELinux-Systemen (Fedora, RHEL) bekommst du sonst „Permission denied", obwohl die Unix-Rechte stimmen. Auf Systemen ohne SELinux (viele Debian/Ubuntu-Installationen, WSL2) ist `:Z` wirkungslos, aber harmlos.
- Du mountest hier **dein eigenes** `~/.claude` hinein. Für den Single-User-Fall ist das genau richtig. Für den geteilten Mehrbenutzer-Fall ändern wir das gleich grundlegend.

Der eigentliche rootless-Stolperstein ist die **Gruppenzugehörigkeit**. Standardmäßig bildet Podman die Supplementär-Gruppen deines Host-Users im User-Namespace auf `nobody` ab — der Container verliert also jede zusätzliche Gruppenmitgliedschaft. Sobald du auf ein gruppen-geschütztes Verzeichnis schreiben willst (genau unser Sharing-Szenario), brauchst du:

```bash
podman run --group-add keep-groups ...
```

`keep-groups` (technisch die Annotation `run.oci.keep_original_groups=1`) behält die Host-GIDs für die Rechteprüfung am Dateisystem bei. Ist dein Host-User Mitglied der geteilten Gruppe, darf der Container-Prozess dann schreiben. Wir kommen in Kapitel 11 darauf zurück.

> **Praxis · WSL2:** Unter WSL2 läuft Podman rootless problemlos. SELinux ist dort nicht aktiv, also kannst du `:Z` weglassen. Achte aber darauf, dass dein Projekt und `~/.claude` im **Linux-Dateisystem** (`/home/...`) liegen, nicht unter `/mnt/c/...` — Mounts über die Windows-Grenze sind langsam und ihre Rechte-Semantik (kein echtes `chmod`/ACL) bricht alles, was in diesem Artikel über Gruppen und ACLs steht.

## Ansatz C — Rootful Docker

Bei klassischem, rootful [Docker](https://www.jpkc.com/db/cheatsheets/containers/docker/) ist die Lage anders — und in einem Punkt heikler. Container-GIDs mappen hier **direkt** auf Host-GIDs. Das macht das Teilen einer Gruppe einerseits einfacher (keine `nobody`-Abbildung), birgt andererseits die größte Falle des ganzen Themas: Wenn der Container-Prozess als root schreibt, gehören die erzeugten Dateien auf dem Host `root:root` — und durchbrechen jedes Gruppenmodell.

Die saubere Variante lässt den Prozess als deinen User laufen und gibt die geteilte Gruppe als **numerische** GID mit (der Gruppenname existiert im Image meist nicht):

```bash
docker run --rm -it \
	--user "$(id -u):$(id -g)" \
	--group-add 1234 \
	-v "$PWD":/workspace \
	-v "$HOME/.claude":/home/dev/.claude \
	claude-code:local
```

Mit [Docker Compose](https://www.jpkc.com/db/cheatsheets/containers/docker-compose/) sieht dasselbe so aus:

```yaml
services:
  claude:
    image: claude-code:local
    user: "1000:1000"        # deine Host-UID:GID
    group_add:
      - "1234"               # numerische GID der Gruppe 'shared'
    volumes:
      - ./:/workspace
      - ${HOME}/.claude:/home/dev/.claude
    stdin_open: true
    tty: true
```

Die `1234` ermittelst du auf dem Host mit `getent group shared | cut -d: -f3`. Schreib sie wirklich numerisch hin — verlässt du dich auf den Namen, scheitert es, weil das schlanke Image keine Zeile `shared` in `/etc/group` hat.

> **Praxis · DDEV:** Wenn du mit DDEV arbeitest, ist dieser Teil schon gelöst. DDEV mappt den Container-User ohnehin auf deinen Host-User. Solange das gemountete Verzeichnis die in Kapitel 11 beschriebenen Rechte trägt, kannst du Claude Code dort ohne weitere `--user`-/`group_add`-Akrobatik betreiben.

## Kernszenario: Eine Installation, viele Nutzer

Jetzt zum eigentlichen Ziel. Stell dir einen gemeinsamen Entwicklungs-Server vor — oder schlicht einen Familien- oder Team-Rechner — auf dem mehrere Menschen mit eigenen Linux-Konten arbeiten. Du willst Claude Code **nicht** in jedem Konto einzeln installieren, aktuell halten und konfigurieren. Stattdessen:

- **Ein Image, zentral gepflegt.** Du baust `claude-code:local` einmal (oder ziehst es aus einer internen Registry). Aktualisieren heißt: Image neu bauen, nicht *n* Konten anfassen. Version-Pinning im Dockerfile macht die Umgebung für alle identisch.
- **Jeder Nutzer startet denselben Container** über sein eigenes Konto. Bei rootless Podman läuft der Container unter dem jeweiligen Host-User — die Trennung der Konten bleibt also erhalten, obwohl die *Software* geteilt ist.
- **Geteilt wird die Installation und (Teile der) Konfiguration**, nicht die Identität. Das ist die wichtige Unterscheidung: Eine gemeinsame Code-Basis ja, eine gemeinsame Anmeldung nur mit Bedacht (siehe Kapitel 10).

Der Knackpunkt liegt nicht im Image — das ist trivial zu teilen — sondern in den **gemeinsam genutzten Verzeichnissen** auf dem Host: ein geteilter Skill-/Command-/Settings-Bestand, vielleicht ein gemeinsames Projekt-Verzeichnis. Mehrere Konten, die in dieselben Dateien schreiben sollen, ohne sich gegenseitig auszusperren — das ist ein klassisches Unix-Rechteproblem, und es löst sich nicht mit „einfach alle in eine Gruppe stecken". Warum, und wie es richtig geht, klären die nächsten beiden Kapitel.

## `~/.claude` gemeinsam teilen — aber richtig

Der Reiz ist groß, einfach das ganze `~/.claude`-Verzeichnis zwischen allen zu teilen: einmal anmelden, einmal Skills pflegen, alle profitieren. Bevor du das tust, musst du wissen, **was in diesem Verzeichnis steckt** — denn nicht alles darf gemeinsam genutzt werden.

`~/.claude` (verschiebbar über `CLAUDE_CONFIG_DIR`) enthält grob drei Sorten von Inhalten:

**Unkritisch und gut teilbar (Konfiguration & Erweiterungen):**

- `settings.json` — Berechtigungen, Hooks, Modell-Defaults, Umgebungsvariablen
- `CLAUDE.md` — deine globalen Anweisungen über alle Projekte
- `skills/`, `commands/`, `agents/`, `rules/`, `output-styles/`, `workflows/` — wiederverwendbare Erweiterungen
- `plugins/` — installierte Plugins und Marketplaces
- `themes/`, `keybindings.json` — Optik und Tastenkürzel

**Pro Nutzer und sensibel (Identität & Verlauf) — NICHT teilen:**

- `.credentials.json` — das **OAuth-Token**. Unter Linux liegt es als Datei, **nur durch Dateirechte geschützt, nicht verschlüsselt**. Wer es lesen kann, kann sich als dieser Account ausgeben.
- `projects/<projekt>/<session>.jsonl` — die **Session-Transkripte**. Hier landet alles, was Claude während einer Sitzung gelesen hat — liest ein Tool eine `.env` oder gibt ein Befehl ein Geheimnis aus, steht dieser Wert im Transkript.
- `history.jsonl` — deine Prompt-Historie
- `.claude.json` (im Home, neben dem Verzeichnis) — App-Zustand und UI-Präferenzen
- `shell-snapshots/`, `file-history/` — flüchtiger Sitzungszustand und Pre-Edit-Snapshots

Daraus folgen zwei legitime Wege — und du musst dich bewusst für einen entscheiden.

### Weg 1 (empfohlen): Config teilen, Identität trennen

Die saubere Aufteilung: Du teilst nur die **unkritische Konfiguration** read-only und gibst jedem Nutzer ein **eigenes, beschreibbares** `~/.claude` für Anmeldung, Transkripte und Verlauf.

Praktisch heißt das: Der geteilte Bestand (Skills, Commands, Agents, eine Basis-`settings.json`) liegt in einem gemeinsamen Verzeichnis wie `/srv/claude-shared/` und wird **read-only** in den Container gemountet. Der schreibbare, private Teil liegt pro Nutzer und wird über `CLAUDE_CONFIG_DIR` als Hauptverzeichnis gesetzt:

```bash
podman run --rm -it \
	-e CLAUDE_CONFIG_DIR=/home/dev/.claude \
	-v "$HOME/.claude":/home/dev/.claude:Z \
	-v /srv/claude-shared/skills:/home/dev/.claude/skills:ro \
	-v /srv/claude-shared/commands:/home/dev/.claude/commands:ro \
	-v /srv/claude-shared/agents:/home/dev/.claude/agents:ro \
	claude-code:local
```

Jeder meldet sich einmal mit dem eigenen Konto an (`.credentials.json` bleibt privat), niemand sieht die Transkripte der anderen, aber alle teilen sich denselben kuratierten Skill- und Command-Bestand. Aktualisiert ein Admin einen Skill in `/srv/claude-shared/`, haben ihn beim nächsten Start alle. Das read-only `:ro` verhindert, dass der Agent den geteilten Bestand versehentlich verändert.

### Weg 2: Komplette `~/.claude` teilen — mit offenen Augen

Es gibt Situationen, in denen ein **gemeinsames Konto** gewollt ist — etwa ein dediziertes Team-/Service-Konto mit eigenem Abo, das mehrere Personen unbeaufsichtigt nutzen. Dann teilst du das ganze `~/.claude` als einen beschreibbaren Mount (die Rechte dafür baust du in Kapitel 11). Das funktioniert technisch — aber kauf es dir mit klarem Blick auf die Risiken ein:

- **Geteiltes Token = geteilte Identität.** Alle Aktionen laufen unter demselben Account; Nachvollziehbarkeit, wer was getan hat, ist dahin. Das Token liegt unverschlüsselt und ist für jeden mit Zugriff lesbar.
- **Transkripte für alle.** Jede `projects/.../*.jsonl` ist für alle Gruppenmitglieder sichtbar. Liest Claude in irgendeiner Sitzung ein Geheimnis, sehen es alle.
- **Gleichzeitigkeit.** Mehrere parallele Sitzungen auf demselben Zustandsverzeichnis können sich in die Quere kommen (Verlauf, Snapshots). Für echten Parallelbetrieb ist Weg 1 ohnehin robuster.

Wenn du Weg 2 gehst, dann bewusst und eng: ein eigens dafür angelegtes Konto, ein scharfer Egress-Filter (Kapitel 12), und auf keinen Fall vermischt mit den privaten Home-Verzeichnissen echter Personen. Für die meisten Setups ist Weg 1 die richtige Wahl; Weg 2 ist das Werkzeug für den Sonderfall „eine Maschine, ein Bot-Konto, mehrere Bediener".

## Rechte & Gruppen im Detail

Jetzt der technische Kern — und die Stelle, an der die meisten „nur eine Gruppe"-Lösungen scheitern. Eine gemeinsame Gruppe ist die richtige Grundlage, reicht aber allein nicht: Neu erstellte Dateien bekommen standardmäßig die *primäre* Gruppe ihres Erzeugers und, dank `umask 022`, kein Gruppen-Schreibrecht. Damit ein geteiltes Verzeichnis dauerhaft funktioniert, brauchst du **drei Bausteine** auf dem Host plus eine **separate Behandlung im Container**.

### Basis: Gruppe + setgid + Default-ACLs

```bash
# 1. Gruppe anlegen und Nutzer aufnehmen
sudo groupadd shared
sudo usermod -aG shared alice
sudo usermod -aG shared bob
# Note: membership takes effect only after re-login (or `newgrp shared`)

# 2. Verzeichnis mit setgid-Bit
sudo mkdir -p /srv/shared
sudo chown root:shared /srv/shared
sudo chmod 2775 /srv/shared        # leading 2 = setgid

# 3. Default-ACLs (der eigentliche Schlüssel)
sudo setfacl -R  -m g:shared:rwx /srv/shared    # existing entries
sudo setfacl -R -d -m g:shared:rwx /srv/shared  # default for new entries
```

Was hier passiert, und warum jeder Schritt nötig ist:

Das **setgid-Bit** (die führende `2` in `2775`) sorgt dafür, dass neue Dateien und Unterordner die Gruppe `shared` **erben** — statt der primären Gruppe ihres Erzeugers. Ohne setgid würde Alices neue Datei der Gruppe `alice` gehören, und Bob stünde außen vor.

Die **Default-ACL** ist der eigentliche Schlüssel. Sie sorgt dafür, dass neue Dateien gruppen-**schreibbar** sind — unabhängig von der `umask` des jeweiligen Nutzers. Genau hier scheitern die einfachen Lösungen: Die `umask` ist eine pro-Prozess- bzw. pro-Login-Einstellung. Du müsstest sie für jeden Nutzer *und* jeden Dienst auf `002` erzwingen — fragil und leicht zu vergessen. Eine [Default-ACL](https://de.wikipedia.org/wiki/Access_Control_List) liegt dagegen am Dateisystem und gilt universell, egal mit welcher `umask` jemand schreibt. Deshalb ist **setgid + Default-ACL** robuster als setgid + globale `umask`.

### Die Container-Brücke

Das Verzeichnis ist damit auf dem Host korrekt. Ob ein *Container* hineinschreiben kann, hängt aber davon ab, mit welcher UID/GID der Prozess **im Host-Namespace** landet. Hier trennen sich Podman und Docker, wie schon angedeutet:

**Rootless Podman** mappt Host-Supplementärgruppen standardmäßig auf `nobody` — der Container verliert die `shared`-Mitgliedschaft. Lösung:

```bash
podman run --group-add keep-groups ...
```

`keep-groups` behält die Host-GIDs für die Dateisystem-Rechteprüfung. Da dein Host-User in `shared` ist, darf der Container-Prozess dann schreiben. Beachte: Neu erzeugte Dateien gehören weiterhin der gemappten UID — aber die Default-ACL fängt das Schreibrecht der Gruppe ab.

**Rootful Docker** mappt Container-GIDs direkt auf Host-GIDs. Gib die numerische GID als Supplementärgruppe mit:

```yaml
services:
  app:
    group_add:
      - "1234"   # numerische GID von 'shared'
```

bzw. `docker run --group-add 1234`. Und noch einmal die Warnung, weil sie hier am meisten kostet: Schreibt der Container-Prozess als **root**, landen die Dateien als `root:root` auf dem Host und durchbrechen das Modell. Entweder den Prozess als deinen User laufen lassen (`--user`) oder im Image umask/GID passend setzen.

### Zwei Praxis-Fallen

Selbst mit perfekt gesetzten ACLs gibt es zwei Wege, sich das Modell unbemerkt zu zerschießen:

1. **Werkzeuge, die Rechte „mitschleppen".** `cp -p`, `rsync -a` oder ein `tar`-Restore übernehmen Original-Eigentümer und -Rechte und **umgehen** damit die Vererbung. Befüllst du das geteilte Verzeichnis so, ziehst du die alten, falschen Rechte mit herein. Entweder bewusst ohne Permission-Preservation arbeiten (`cp` ohne `-p`, `rsync` ohne `-a` bzw. mit `--no-perms --no-owner --no-group`) oder hinterher `setfacl`/`chgrp` nachziehen.
2. **Programme, die ihre Dateien selbst auf `0600` setzen.** Manche Tools erzwingen restriktive Rechte auf ihre eigenen Dateien und überschreiben damit die ACL-Defaults — von außen nicht verhinderbar. Das ist kein theoretischer Fall: Genau so verhält sich Claude Code mit `.credentials.json`. Es ist *gut*, dass das Token eng bleibt — aber es bestätigt, warum du Anmeldedaten nach Weg 1 ohnehin **nicht** über die Gruppe teilst, sondern pro Nutzer hältst.

## Sicherheit & Threat-Model

Container geben ein trügerisches Gefühl von Sicherheit, wenn man die Grenzen nicht kennt. Die wichtigsten Punkte, gebündelt:

- **Credentials sind das Kronjuwel.** Das OAuth-Token in `~/.claude/.credentials.json` liegt unter Linux unverschlüsselt, nur durch Dateirechte geschützt. Mit `--dangerously-skip-permissions` kann ein bösartiges Repo alles im Container Erreichbare exfiltrieren — auch dieses Token. Konsequenz: Token pro Nutzer, niemals in einen Mount legen, auf den fremder Code mit weiten Rechten zugreift.
- **`--dangerously-skip-permissions` nur in einer Grenze.** Container, VM oder Sandbox-Runtime — nie blank auf dem Host. Willst du weniger Prompts ohne die Sicherheitsprüfungen abzuschalten, nutze stattdessen den **Auto-Mode**: Ein Klassifizierer prüft jede Aktion, statt blind durchzuwinken.
- **Netz-Egress einschränken.** Die wirksamste Einzelmaßnahme für unbeaufsichtigten Betrieb. Die `init-firewall.sh` der Referenz erlaubt nur die nötigen Domains. Selbst wenn der Agent kompromittiert wird, kann er Daten nur dorthin schicken, wohin die Policy ihn lässt.
- **Keine Host-Secrets mounten.** `~/.ssh`, Cloud-Credential-Dateien, Keychains haben im Container nichts zu suchen. Brauchst du Zugriff, nutze kurzlebige, eng begrenzte Tokens statt der dauerhaften Schlüssel.
- **Nicht vertrauenswürdiger Code gehört in eine VM** — oder in Claude Code on the web. Ein Container teilt sich den Kernel mit dem Host; für echten „ich kenne diesen Code nicht"-Betrieb ist die kernel-getrennte VM die ehrlichere Wahl.

Und der Grundsatz, der über allem steht: Isolation reduziert den Schaden, sie beseitigt das Risiko nicht. Prüfe weiterhin, was der Agent tut.

## Entscheidungshilfe

Welcher Ansatz für welches Ziel? Die Kurzfassung:

| Du willst … | Nimm … |
|---|---|
| Im Alltag auf der eigenen Maschine weniger Prompts | Bash-Sandbox (`/sandbox`) |
| MCP/Hooks zusätzlich isolieren, ohne Docker | Sandbox-Runtime |
| Unbeaufsichtigt mit `--dangerously-skip-permissions` arbeiten | Devcontainer/Container **mit** Egress-Firewall |
| Eine teamweite, standardisierte Umgebung | Devcontainer ins Repo committen |
| An einem nicht vertrauenswürdigen Repo arbeiten | VM oder Claude Code on the web |
| **Eine Installation für viele Nutzer** | Systemweites Image + geteilte Gruppe (Kap. 9–11) |
| Auf einem Windows-Host arbeiten | Container/VM oder Bash-Sandbox in WSL2 |

Für das Mehrbenutzer-Ziel heißt das konkret: **rootless Podman** als Engine, ein zentral gepflegtes, versions-gepinntes Image, die **setgid + Default-ACL**-Basis auf dem geteilten Verzeichnis, **`--group-add keep-groups`** beim Start und **Weg 1** beim `~/.claude`-Sharing (Config geteilt, Identität pro Nutzer).

## Fazit

Claude Code im Container ist kein Selbstzweck, sondern die Voraussetzung dafür, dem Agenten mehr Autonomie zu geben, ohne die Kontrolle zu verlieren. Der offizielle Devcontainer ist der schnellste Einstieg für die IDE-Arbeit; rootless Podman und rootful Docker geben dir die Freiheit, eigene Mehrbenutzer-Setups zu bauen.

Der eigentliche Hebel beim Teilen liegt nicht in der Container-Engine, sondern im Dateisystem: **setgid** für die Gruppenvererbung, **Default-ACLs** für das Schreibrecht unabhängig von der `umask`, und je nach Engine `keep-groups` (Podman) oder die numerische `group_add` (Docker) als Brücke in den Container. Trenne dabei konsequent, was teilbar ist (Skills, Commands, Settings) von dem, was es nicht ist (Token, Transkripte). Dann teilen sich beliebig viele Konten eine einzige, zentral gepflegte Installation — sauber, nachvollziehbar und ohne dass irgendwer mehr Rechte hat, als er braucht.

Wenn du tiefer in die Werkzeuge einsteigen willst: die Cheat-Sheets zu [Podman](https://www.jpkc.com/db/cheatsheets/containers/podman/), [Docker](https://www.jpkc.com/db/cheatsheets/containers/docker/) und [Docker Compose](https://www.jpkc.com/db/cheatsheets/containers/docker-compose/) liegen hier in der Datenbank, und der [Docker/Podman Composer](https://www.jpkc.com/db/tools/docker/) hilft beim Umschreiben zwischen `run`-Befehlen und Compose-Dateien.

