Dev Blog

Remote Development Setup: Code-Server, Coolify & Hetzner - und die fiesesten Architektur-Fallen

Als freiberuflicher Softwareentwickler und IT-Berater ist Flexibilität alles. Ob ich nun an komplexen Java-Spring-Boot-Backends schraube, JavaScript-Frontends baue oder Three.js-Visualisierungen rendere: Die Entwicklungsumgebung muss flüssig, reproduzierbar und von überall erreichbar sein.

Deshalb habe ich den klassischen Hosting-Ansatz hinter mir gelassen und meine Infrastruktur auf einen eigenen Hetzner-Server umgezogen, komplett orchestriert von Coolify. Das Herzstück: eine Remote-Instanz von VS Code (Code-Server).

In der Theorie ist das ein Traum-Setup. In der Praxis bin ich in architektonische Fallstricke getappt, die mich fast in den Wahnsinn getrieben haben. Hier sind die größten Stolpersteine und wie du sie elegant umgehst.

Falle 1: Die Port-Proxy-Illusion (oder: der schwarze Bildschirm des Todes)

VS Code im Browser bietet eine scheinbar geniale Funktion: integriertes Port-Forwarding. Startest du einen lokalen Test-Server, zum Beispiel auf Port 4096, tunnelt die IDE das automatisch auf eine URL wie https://deine-ide.de/proxy/4096/.

Für reine Backend-Entwicklung, etwa eine Spring-Boot-REST-API, die nur JSON zurückgibt, ist das fantastisch. Keine Firewall-Regeln, direkter Zugriff.

Sobald du aber ein modernes Frontend mit Vue, React oder Vite oder ein Tool mit grafischer Oberfläche startest, stehst du vor einem schwarzen Bildschirm und endlosen 404-Fehlern in der Konsole.

Der Grund: Moderne Frameworks kompilieren mit absoluten Pfaden, zum Beispiel <script src="/main.js">. Der Browser ignoriert den Unterordner /proxy/4096/, sucht die Dateien stur im Hauptverzeichnis des Servers und scheitert.

Die Lösung: Traefik und Subdomains

Die einzig saubere Architektur ist, den integrierten VS-Code-Tunnel komplett zu ignorieren. Stattdessen nutzen wir den Traefik Reverse Proxy in Coolify:

  1. Lege beim Domain-Provider einen Wildcard-Eintrag (*.deinedomain.de) auf deinen Hetzner-Server an.
  2. Trage in Coolify unter den Domains für den Container eine neue Subdomain inklusive des internen Ports ein: https://dev.deinedomain.de:4096.
  3. Traefik kümmert sich automatisch um das SSL-Zertifikat und routet die saubere Haupt-URL ohne Port-Anhängsel direkt in den Container. Das Frontend lädt korrekt.

Falle 2: Der Localhost-Trugschluss (502 Bad Gateway)

Du hast die Subdomain in Coolify eingerichtet, freust dich auf die Live-Vorschau und bekommst ein eiskaltes 502 Bad Gateway. Traefik läuft, aber der Container blockt ab.

Das passiert, wenn dein interner Server, zum Beispiel Five Server oder dein eigenes Tool, standardmäßig auf 127.0.0.1 lauscht. Das bedeutet in der Docker-Welt: Der Service spricht nur mit sich selbst im Inneren des Containers. Wenn der Traefik-Proxy von außen anklopft, wird die Tür zugeschlagen.

Der Fix: Du musst deinen Test-Server zwingen, auf allen Netzwerkschnittstellen zu lauschen. Startest du ihn über das Terminal, nutze --hostname=0.0.0.0 oder setze die Umgebungsvariable HOST="0.0.0.0". Nutzt du eine VS-Code-Erweiterung wie Five Server, passe die settings.json an:

{
  "fiveServer.host": "0.0.0.0",
  "fiveServer.port": 5500
}

Falle 3: Die Docker-Demenz (heute „Restart“, morgen weg)

Du richtest dir mühsam deine .bashrc mit cleveren Aliasen ein, generierst einen SSH-Key für GitHub, fährst den Container über Coolify herunter und klickst am nächsten Tag auf „Deploy“. Plötzlich ist alles weg. Deine Aliase sind gelöscht, Git fordert wieder Passwörter.

Das ist kein Bug, sondern ein Feature. Ein gestoppter Container taucht kurzzeitig als „Restart“-Option auf. Über Nacht räumt Coolifys Garbage Collection den Server auf und löscht die schlafende Container-Hülle komplett. Ein Klick auf „Deploy“ baut den Container frisch aus dem Basis-Image und alles, was nicht persistent war, ist verloren.

Die saubere Struktur

Verlasse dich nie auf den flüchtigen Container-Speicher. Nutze zwingend Bind-Mounts oder Docker-Volumes für alles, was bleiben soll.

volumes:
  - type: bind
    source: /opt/appdata/workspace # Dein Code
    target: /workspace
  - type: bind
    source: /opt/appdata/config    # VS Code Settings, SSH Keys & .bashrc
    target: /config

Falle 4: CORS-Konfiguration hinter dem Reverse Proxy

Die letzte Falle schnappt oft bei der Fullstack-Entwicklung zu. Dein Frontend liegt auf https://dev.deinedomain.de und sendet Requests an dein Spring-Backend. Aus alter Gewohnheit trägst du in deine CorsConfiguration den Port mit ein:

// FALSCH, wenn ein Reverse Proxy davor sitzt!
configuration.setAllowedOriginPatterns(
    Arrays.asList("https://dev.deinedomain.de:5500")
);

Wieder blockiert das Backend. Warum? Weil der Port 5500 nur eine interne Routing-Anweisung in Coolify ist. Dein Browser kommuniziert nach außen völlig transparent über den Standard-Port 443 (HTTPS) mit dem Traefik-Proxy. Der Browser setzt den Origin-Header folglich schlicht auf https://dev.deinedomain.de.

Lass den Port im Backend einfach weg, und die CORS-Fehler lösen sich in Luft auf.


Fazit: Ein eigenes Coolify-/Code-Server-Setup auf Hetzner erfordert anfangs etwas Nerven und solides Docker-Verständnis. Aber wenn das Routing einmal sauber steht, die Volumes gemountet sind und die Ports stimmen, hast du eine hochperformante private Cloud-IDE, die klassischen SaaS-Lösungen in nichts nachsteht.