Zum Inhalt springen
← Alle Beiträge
· 4 Min. Lesezeit·Emre Yurtbay

Cloudflare Tunnel: Services veröffentlichen ohne offene Ports

Mit cloudflared einen Dienst sicher ins Netz bringen, ohne einen einzigen Port nach außen zu öffnen – Schritt für Schritt per CLI und als Docker-Container.

Cloudflare TunnelcloudflaredDockerReverse ProxySelf-HostingNetzwerkDevOps

Wer einen Dienst hinter einem Heimanschluss oder in einem abgeschotteten Netz betreibt, kennt das Problem: Portfreigaben am Router, eine dynamische IP, dazu die Sorge, etwas direkt aus dem Internet erreichbar zu machen. Cloudflare Tunnel dreht den Spieß um. Statt einen Port zu öffnen, baut ein kleiner Daemon namens cloudflared eine ausgehende Verbindung zu Cloudflare auf. Der Datenverkehr läuft durch diesen Tunnel zurück zu Ihrem Dienst – die Firewall bleibt von außen komplett dicht. Dieser Beitrag zeigt einen lauffähigen Minimal-Tunnel in unter 15 Minuten.

Was Cloudflare Tunnel macht – und wann es passt

cloudflared läuft auf Ihrem Host und verbindet sich nur nach außen zu Cloudflares Netzwerk, standardmäßig über QUIC (UDP), mit HTTP/2 (TCP) als Rückfallebene. Benötigt wird dafür nur ausgehend der Port 7844. Es ist keine eingehende Portfreigabe nötig, Ihre öffentliche IP bleibt verborgen, und eine dynamische IP spielt keine Rolle mehr. Cloudflare nimmt die Anfragen an Ihre Domain entgegen und reicht sie durch den bestehenden Tunnel weiter.

Das passt überall dort, wo Sie einen Web-Dienst, eine API oder einen internen Service erreichbar machen wollen, ohne die Angriffsfläche eines offenen Ports zu schaffen – etwa hinter CGNAT, im Heimnetz oder in einer restriktiven Cloud-Umgebung.

                                +---------------------+
   Besucher --HTTPS-->          |     Cloudflare      |
   (app.example.com)            |       Edge          |
                                +----------+----------+
                                           ^
                  ausgehend, Port 7844     |  (kein offener Port)
                                           |
                                +----------+----------+
                                |     cloudflared     |
                                |     (Ihr Host)      |
                                +----------+----------+
                                           |
                                  http://localhost:8080
                                     [  Ihr Dienst  ]

Zwei Betriebsarten

Cloudflare unterscheidet zwei Modelle. Beim remotely-managed Tunnel legen Sie alles im Dashboard an (unter Networking → Tunnels); die Konfiguration liegt bei Cloudflare, und cloudflared startet mit einem Token. Das ist der von Cloudflare empfohlene Weg für die meisten Fälle. Beim locally-managed Tunnel erzeugen Sie den Tunnel per CLI, und die Routing-Regeln stehen in einer lokalen config.yml. Letzteres zeigen wir zuerst, weil dabei jede Regel sichtbar und versionierbar ist.

Der minimale Tunnel über die CLI

Nach der Installation von cloudflared melden Sie sich einmalig an und legen den Tunnel an:

cloudflared tunnel login
cloudflared tunnel create mein-tunnel

login öffnet den Browser und autorisiert cloudflared für eine Ihrer Domains. create legt den Tunnel an und schreibt eine Credentials-Datei nach ~/.cloudflared/<TUNNEL-UUID>.json. Diese UUID brauchen Sie gleich.

Nun die Routing-Regeln in ~/.cloudflared/config.yml. Jede Regel verbindet einen Hostnamen mit einem lokalen Dienst; die letzte Regel muss eine Catch-all-Regel sein:

tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /root/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json

ingress:
  - hostname: app.example.com
    service: http://localhost:8080
  - service: http_status:404

tunnel ist die UUID aus dem vorigen Schritt, credentials-file der Pfad zur erzeugten JSON-Datei. Unter ingress steht pro Hostname ein Block mit service als Ziel. Die abschließende service: http_status:404 fängt alles Übrige ab – ohne diese Catch-all-Regel ist die Konfiguration ungültig.

Fehlt noch der DNS-Eintrag und der Start:

cloudflared tunnel route dns mein-tunnel app.example.com
cloudflared tunnel run mein-tunnel

route dns legt automatisch den passenden CNAME auf <UUID>.cfargotunnel.com an. run startet den Tunnel. Ein Aufruf von https://app.example.com landet jetzt über Cloudflare bei Ihrem Dienst auf Port 8080 – ohne dass an Ihrem Router etwas offen steht.

Als Docker-Container betreiben

Im Dauerbetrieb läuft cloudflared meist als Container. Für einen remotely-managed Tunnel kopieren Sie den Token aus dem Dashboard; Routing und Hostnamen pflegen Sie dann dort, eine config.yml entfällt:

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    restart: unless-stopped
    command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}

Den Token legen Sie als Umgebungsvariable TUNNEL_TOKEN ab (etwa in einer .env-Datei), niemals im Klartext im Compose-File. Ein docker compose up -d genügt.

Drei typische Stolperfallen

1. Die Catch-all-Regel fehlt. Eine config.yml mit ingress-Regeln muss zwingend mit service: http_status:404 enden. Fehlt der Abschluss, startet der Tunnel nicht.

2. localhost im Container ist nicht der Host. Läuft cloudflared im Container, zeigt service: http://localhost:8080 auf den Container selbst, nicht auf Ihren Dienst. Verweisen Sie stattdessen auf den Service-Namen im selben Docker-Netz oder auf host.docker.internal. Ein falsches Ziel quittiert Cloudflare mit einem 502.

3. DNS-Konflikt beim Hostnamen. Existiert für den Hostnamen bereits ein A- oder CNAME-Record, schlägt das Anlegen fehl. Der Eintrag muss als CNAME auf <UUID>.cfargotunnel.com zeigen – löschen Sie den alten Record oder wählen Sie einen anderen Hostnamen.

Wie es weitergeht

Damit steht ein Dienst sicher im Netz, ohne einen einzigen offenen Port. Naheliegende nächste Schritte: weitere Hostnamen als zusätzliche ingress-Regeln ergänzen, cloudflared als systemd-Dienst oder im Container dauerhaft betreiben und bei Bedarf Cloudflare Access für eine Zugriffskontrolle davorschalten. Tiefer geht es in der Übersicht zu Cloudflare Tunnel, beim Anlegen eines remotely-managed Tunnels, in der Referenz zur config.yml und bei den Firewall- und Port-Hinweisen.

Hinweis: Die Beiträge dieses Blogs werden unter Einsatz von KI erstellt und vor der Veröffentlichung redaktionell geprüft. Die redaktionelle Verantwortung trägt Emre Yurtbay (siehe Impressum).

Projekt besprechen