Warum ein self-hosted VS Code Server?

Als Embedded-Software-Entwickler arbeite ich an verschiedenen Stellen: zu Hause am Desktop, auf dem Notebook, manchmal auch nur über ein Tablet. Eine IDE, die überall gleich konfiguriert, mit denselben Extensions bestückt und direkt am Projekt dran ist , das ist kein Luxus, das ist Effizienz.

Cloud-Lösungen wie GitHub Codespaces oder Gitpod lösen das Problem prinzipiell, bringen aber Abhängigkeiten und laufende Kosten mit sich. Mein Ansatz ist anders: ein kleines Debian-System im Heimnetz (oder VPS), darauf Podman ohne Root-Rechte, und code-server als Container. Verwaltung über Cockpit , das ist die schlanke, webbasierte Systemverwaltung, die Debian seit Version 12 beinahe von Haus aus mitbringt.

Das Konzept auf einen Blick

Browser (überall) → HTTPS:8080 → code-server Container (Podman, rootless)
                                              ↓
                                   ~/code-server/projects/ (Host)

Browser (lokal) → HTTPS:9090 → Cockpit (Systemverwaltung + Podman UI)

Der Stack besteht aus 3 Kernkomponenten:

  • Debian 12 Bookworm Minimal — schlanke Basis, kein grafischer Overhead

  • Cockpit — webbasierte Serververwaltung inklusive Podman-Integration

  • code-server — VS Code als Web-App, läuft rootless im Podman-Container

  • Claude Code — KI-Assistent direkt im Terminal innerhalb von code-server

Voraussetzungen

Bevor es losgeht, sollte folgendes bereitstehen:

  • Debian 12 (Bookworm) Minimal Installation , physisch, als VM oder VPS

  • Root-Zugriff oder sudo-Berechtigung

  • Internetzugang auf dem Server

  • Mindestens 2 GB RAM und 10 GB freier Speicher

Der Stack ist bewusst sparsam ausgelegt. Auf meinem BeagleBone-Testsetup läuft er, auf einem Raspberry Pi 4 läuft er, auf einem kleinen Proxmox-Node sowieso.

Basisinstallation: Debian Minimal vorbereiten

Nach einer frischen Minimal-Installation das System aktualisieren und die nötigsten Pakete installieren:

# System aktualisieren
apt update && apt upgrade -y

# Grundlegende Werkzeuge
apt install -y sudo curl wget gnupg2 ca-certificates \
    apt-transport-https software-properties-common

Falls noch kein regulärer Benutzer vorhanden ist, diesen anlegen und mit sudo-Rechten ausstatten:

useradd -m -s /bin/bash <UserName>
passwd <UserName>
usermod -aG sudo <UserName>

<UserName> durch den gewünschten Benutzernamen ersetzen, z.B. paul.
(In den obigen Befehlen <UserName> durch den tatsächlichen Benutzernamen ersetzen.)

Alle weiteren Schritte werden als normaler Benutzer (nicht root) ausgeführt, außer es ist ausdrücklich anders angegeben. Das ist besonders für Podman wichtig, da rootless Containers einer der Kernvorteile gegenüber Docker sind.

Cockpit: Serververwaltung per Browser

Cockpit ist ein webbasiertes Admin-Interface, das direkt auf dem Server läuft. Kein zusätzlicher Management-Server, keine Datenbank , Cockpit kommuniziert direkt mit systemd, NetworkManager und natürlich Podman.

Installation

# Cockpit installieren
apt install -y cockpit

# Dienst aktivieren und starten
systemctl enable --now cockpit.socket

# Falls UFW aktiv ist: Port freigeben
ufw allow 9090/tcp

Erster Zugriff

https://<SERVER-IP>:9090

Beim ersten Aufruf erscheint eine Zertifikatswarnung (selbst-signiertes Zertifikat). Diese einmalig bestätigen, dann mit dem Linux-Benutzernamen und Passwort anmelden.

Cockpit zeigt danach einen Überblick über CPU, RAM, Netzwerk und laufende Dienste. Klar, kompakt, ohne Schnickschnack.

Podman-Integration nachrüsten

apt install -y cockpit-podman

Nach der Installation erscheint im linken Menü der Punkt „Podman containers". Dort lassen sich Container grafisch starten, stoppen, Logs einsehen und Volumes verwalten , alles ohne Terminal.

Cockpit Podman UI
Figure 1. Cockpit mit Podman-Übersicht

Podman: Rootless Containers ohne Daemon

Podman ist die daemonlose Alternative zu Docker. Der entscheidende Vorteil im Heimserver-Kontext: Containers laufen unter dem eigenen Benutzeraccount, ohne Root-Rechte und ohne permanenten System-Daemon.

Installation und Aktivierung

# Podman installieren
apt install -y podman

# Version prüfen
podman --version

# User-Socket aktivieren (für rootless Betrieb)
systemctl --user enable --now podman.socket

Der User-Socket unter /run/user/<UID>/podman/podman.sock ist der Schlüssel für die Integration mit Drone CI oder anderen CI-Systemen, die einen Docker-kompatiblen Socket erwarten. Einfach einen Symlink nach /var/run/docker.sock anlegen oder den Socket-Pfad in der CI-Konfiguration direkt angeben.

Warum Podman statt Docker?

DockerPodman

Benötigt Root-Daemon (dockerd)

Kein Daemon, komplett rootless

Containers laufen standardmäßig als root

Containers laufen als Benutzer

Docker Socket = Sicherheitsrisiko

Kein Socket auf Root-Ebene nötig

Systemd-Integration komplex

Native systemd --user Integration

Docker Compose erforderlich

Pods als Konzept eingebaut

Für einen Heimserver, auf dem ich auch produktive Daten habe, ist das kein akademischer Unterschied , das ist ein echtes Sicherheitsargument.

code-server: VS Code im Browser

code-server ist das Open-Source-Projekt von Coder, das VS Code als Webserver ausliefert. Der offizielle Docker-Container funktioniert 1:1 mit Podman.

Verzeichnisse anlegen

# Als normaler Benutzer (nicht root)
mkdir -p ~/code-server/config
mkdir -p ~/code-server/projects

Das projects-Verzeichnis auf dem Host wird in den Container gemountet. Alle Projektdaten liegen damit auf dem Host und ein Container-Neustart ändert nichts daran.

Container starten

podman run -d \
  --name code-server \
  -p 8080:8080 \
  -e PASSWORD=meinPasswort \
  -u $(id -u):$(id -g) \
  -v ~/code-server/config:/home/coder/.config/code-server:Z \
  -v ~/code-server/projects:/home/coder/project:Z \
  docker.io/codercom/code-server:latest

Parameter erklärt

ParameterBedeutung

-d

Container im Hintergrund starten (detached)

--name code-server

Eindeutiger Container-Name für spätere Befehle

-p 8080:8080

Port-Weiterleitung Host:Container

-e PASSWORD=…​

Login-Passwort für die VS Code Web-UI

-u $(id -u):$(id -g)

Container läuft als aktueller Benutzer dass ist wichtig für Datei-Permissions

-v …​:Z

Volume-Mount mit SELinux-Relabeling (:Z ist auf Debian zwar nicht strikt nötig, schadet aber nicht)

VS Code aufrufen

http://<SERVER-IP>:8080

Passwort eingeben und fertig. VS Code läuft im Browser, vollständig funktionsfähig inklusive Terminal, Git-Integration und Extension-Support.

VS Code Web Interface
Figure 2. VS Code Web mit Claude Code CLI im integrierten Terminal

Autostart via Systemd

Ein Container, der nach jedem Reboot manuell gestartet werden muss, ist kein produktiver Container. Podman bringt einen Generator für systemd-Units mit:

# Unit-Datei generieren (im aktuellen Verzeichnis)
podman generate systemd --name code-server --files --new

# In den Benutzer-Systemd-Ordner verschieben
mkdir -p ~/.config/systemd/user/
mv container-code-server.service ~/.config/systemd/user/

# Dienst aktivieren und starten
systemctl --user enable container-code-server.service
systemctl --user start container-code-server.service

# Linger aktivieren: Benutzer-Dienste laufen auch ohne aktive Session
loginctl enable-linger $(whoami)

loginctl enable-linger ist der entscheidende Schritt. Ohne ihn werden Benutzer-Systemd-Units erst gestartet, wenn sich der Benutzer einloggt und nicht beim Serverstart. Mit Linger laufen sie permanent.

HTTPS: Ohne TLS fehlen Features

Manche VS Code Features , darunter die Markdown Preview , funktionieren nur über HTTPS. Für den internen Betrieb genügt ein selbst-signiertes Zertifikat.

Zertifikat erstellen

mkdir -p ~/code-server/certs

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout ~/code-server/certs/cert.key \
  -out ~/code-server/certs/cert.crt \
  -subj "/CN=$(hostname -I | awk '{print $1}')" \
  -addext "subjectAltName=IP:$(hostname -I | awk '{print $1}'),IP:127.0.0.1"

Konfigurationsdatei anlegen

cat > ~/code-server/config/config.yaml << EOF
bind-addr: 0.0.0.0:8080
auth: password
password: meinPasswort
cert: /home/coder/.config/code-server/certs/cert.crt
cert-key: /home/coder/.config/code-server/certs/cert.key
EOF

Container neu starten mit TLS

# Alten Container entfernen
podman rm -f code-server

# Neu starten mit Zertifikat-Volume
podman run -d \
  --name code-server \
  -p 8080:8080 \
  -e PASSWORD=meinPasswort \
  -u $(id -u):$(id -g) \
  -v ~/code-server/config:/home/coder/.config/code-server:Z \
  -v ~/code-server/projects:/home/coder/project:Z \
  -v ~/code-server/certs:/home/coder/.config/code-server/certs:Z \
  docker.io/codercom/code-server:latest

Aufruf danach über:

https://<SERVER-IP>:8080

Die Zertifikatswarnung einmalig bestätigen und danach läuft alles reibungslos.

Claude Code: KI-Assistent direkt im Terminal

Claude Code ist ein terminal-basierter KI-Coding-Assistent von Anthropic. Er läuft direkt im integrierten Terminal von code-server und weder ein Browser-Plugin, noch ein Cloud-IDE-Lock-in, oder ein anderer Umweg ist nötig.

Voraussetzungen

  • Ein Anthropic-Account: Pro, Max, Teams, Enterprise oder API über die Console

  • Optional: Amazon Bedrock, Google Vertex AI oder Microsoft Foundry als Backend

Installation auf dem Host oder im Container

# Empfohlen: Native Install mit Auto-Update
curl -fsSL https://claude.ai/install.sh | bash

Alternativ via Homebrew (kein automatisches Update):

brew install --cask claude-code

Erster Start und Login

# Im Projektverzeichnis starten
cd ~/code-server/projects/mein-projekt
claude

Beim ersten Start erscheint ein Login-Prompt. Nach einmaliger Authentifizierung werden die Credentials unter ~/.claude/ gespeichert und kein erneuter Login ist nötig.

Wichtige Befehle

BefehlFunktion

claude

Interaktiven Modus starten

claude "Aufgabe"

Einmalige Aufgabe direkt ausführen

claude -p "Frage"

Einzelne Abfrage, dann beenden (pipe-freundlich)

claude -c

Letzte Konversation fortsetzen

claude -r

Frühere Konversation wieder aufnehmen

claude commit

Automatisch Git-Commit mit sinnvoller Message erstellen

/clear

Konversationsverlauf löschen

/help

Alle verfügbaren Slash-Commands anzeigen

exit oder Ctrl+C

Claude Code beenden

Beispiele aus der Praxis

# Projekt verstehen
what does this project do?

# Eingabevalidierung ergänzen
add input validation to the user registration form

# Tests schreiben lassen
write unit tests for the calculator functions

# Commit erstellen
commit my changes with a descriptive message

# Code-Review
review my changes and suggest improvements

Integration: Claude Code im Container

Claude Code kann entweder auf dem Host oder direkt im code-server Container laufen. Für eine nahtlose Integration im Container gibt es zwei Ansätze:

Variante A: Installation direkt im Container

# Shell im Container öffnen
podman exec -it code-server bash

# Claude Code im Container installieren
curl -fsSL https://claude.ai/install.sh | bash

# Claude starten
claude

Credentials landen unter ~/.claude/ im Container-Dateisystem. Um sie über Neustarts hinweg zu erhalten, das Volume beim podman run einbinden: -v ~/.claude:/home/coder/.claude:Z

Variante B: Credentials per Dockerfile einbinden

Für eine reproduzierbare, versionierbare Umgebung empfehle ich einen eigenen Container:

FROM docker.io/codercom/code-server:latest

ENV DEBIAN_FRONTEND=noninteractive
USER root

# Basis-Pakete
RUN apt-get update && apt-get install -y \
    bash curl wget git jq tree unzip zip tar \
    openssh-client ca-certificates gnupg sudo \
    build-essential procps findutils diffutils \
    libicu-dev zstd \
    && rm -rf /var/lib/apt/lists/*

# Node.js und Python
RUN apt-get update && apt-get install -y nodejs npm python3 \
    python3-pip python3-venv

# Claude Code als Benutzer 'coder' installieren
USER coder
RUN curl -fsSL https://claude.ai/install.sh | bash
ENV PATH="/home/coder/.local/bin:${PATH}"

# Credentials vom Host in den Container kopieren
COPY --chown=coder:coder .claude/ /home/coder/.claude/
COPY --chown=coder:coder .claude.json /home/coder/.claude.json

Die Credentials-Dateien (.claude/ und .claude.json) enthalten API-Tokens. Niemals in ein öffentliches Repository committen. In .gitignore aufnehmen und ggf. über CI/CD-Secrets oder ein Secret-Mount zur Build-Zeit einbinden.

Nützliche Alltags-Befehle

Container-Management

# Status aller Container
podman ps

# Logs live verfolgen
podman logs -f code-server

# Container stoppen / starten / neu starten
podman stop code-server
podman start code-server
podman restart code-server

# Container vollständig entfernen (Daten bleiben auf Host-Volumes erhalten)
podman rm -f code-server

# Shell im Container öffnen
podman exec -it code-server bash

# VS Code Extension installieren
podman exec -it code-server \
  code-server --install-extension ms-python.python

Verwaltung über Cockpit

Wer das Terminal vermeiden möchte: Cockpit bietet nach Installation von cockpit-podman eine vollständige grafische Oberfläche.

  1. Browser öffnen: https://<SERVER-IP>:9090

  2. Mit Linux-Benutzer anmelden

  3. Im linken Menü: „Podman containers" auswählen

  4. Container starten, stoppen, Logs lesen , alles per Klick

Zusammenfassung und Schnellreferenz

KomponenteURL / Befehl

Cockpit Web-UI

https://<IP>:9090

VS Code Web-UI

https://<IP>:8080

Passwort ändern

-e PASSWORD=neuesPasswort in podman run

Projekte (Host-Pfad)

~/code-server/projects/

Extension installieren

podman exec -it code-server code-server --install-extension <id>

Claude Code starten

claude im integrierten Terminal

Fazit

Der Stack ist kompakt, sicher und vollständig ohne Cloud-Abhängigkeit betreibbar. Was mich persönlich am meisten überzeugt hat:

  • Rootless Podman eliminiert eine ganze Klasse von Sicherheitsrisiken

  • Cockpit macht den Server auch ohne permanentes SSH-Terminal handhabbar

  • Systemd-Linger sorgt für zuverlässigen Autostart ohne Root-Konfiguration

  • Claude Code im Terminal ist effizienter als jedes Browser-Plugin , es sieht den Code, den Projekt-Kontext und das Git-Repository direkt

Das vollständige Repository mit allen Konfigurationsdateien: * https://github.com/paul-fleischmann-com/selfhosted-vscode-stack