-
Notifications
You must be signed in to change notification settings - Fork 0
External Access
This page explains how containers are securely published to the internet — accessible from anywhere without exposing the NAS directly.
Every service published externally goes through a multi-layer chain that provides encryption, authentication, and automatic configuration:
flowchart LR
User[User / Device] -->|HTTPS| CF[Cloudflare Edge<br/>Zero Trust Access]
CF -->|Authenticated| Tunnel[Cloudflare Tunnel<br/>cloudflared container]
Tunnel -->|Origin TLS| Traefik[Traefik<br/>Reverse Proxy]
Traefik -->|Routes by Host rule| Service[Container Service]
No ports are exposed to the public internet. The cloudflared container creates an outbound-only tunnel to Cloudflare's edge — traffic flows inbound through that tunnel without needing any port forwards or firewall rules on the home network.
Before traffic even reaches the NAS, Cloudflare Zero Trust enforces authentication at the edge. Users must authenticate via an identity provider (e.g. Google, GitHub) before they're allowed through to the application.
This provides an additional authentication layer on top of whatever native auth the container itself supports. Even if a service has its own login page, the Zero Trust policy gates access before the request ever reaches it.
💡 This means even misconfigured or accidentally unsecured containers aren't exposed to the internet — Zero Trust blocks unauthenticated requests at the Cloudflare edge before they can reach the NAS.
The cloudflared container runs on the NAS and maintains a persistent outbound connection to Cloudflare's edge network. This tunnel:
- Requires no inbound ports — it's an outbound-only connection
- Automatically reconnects if dropped
- Routes traffic for configured hostnames from Cloudflare's edge into the home network
Traffic arrives at Traefik via the tunnel with full TLS — Cloudflare terminates TLS at the edge, then re-encrypts to the origin (Traefik) using Full (Strict) mode, meaning Traefik also presents a valid origin certificate.
Traefik receives the request from the tunnel and routes it to the correct container based on Host rules defined via Docker labels:
labels:
- traefik.enable=true
- traefik.http.routers.myapp.rule=Host(`myapp.${DOMAIN}`)
- traefik.http.routers.myapp.entryPoints=websecureTraefik terminates origin TLS with its own certificate, providing end-to-end encryption from user → Cloudflare edge → tunnel → Traefik → container.
The target container receives HTTP(S) from Traefik on its internal Docker network. It doesn't need to know anything about tunnels, or external access — it just serves its application.
sequenceDiagram
participant User
participant CF as Cloudflare Edge
participant ZT as Zero Trust Access
participant Tunnel as cloudflared (NAS)
participant Traefik as Traefik (NAS)
participant App as Container
User->>CF: HTTPS request (myapp.domain.com)
CF->>ZT: Check access policy
ZT->>User: Auth challenge (IdP login)
User->>ZT: Authenticated
ZT->>CF: Request approved
CF->>Tunnel: Forward via tunnel (origin TLS)
Tunnel->>Traefik: Route to Traefik (HTTPS)
Traefik->>App: Route by Host rule (HTTP)
App->>Traefik: Response
Traefik->>Tunnel: Response
Tunnel->>CF: Response
CF->>User: HTTPS response
One of the key design goals is zero manual configuration when adding a new service. This is achieved through two mechanisms working together:
Traefik watches the Docker socket and automatically creates routing rules for any container with traefik.enable=true. This means:
- Container starts → Traefik detects the labels → route is created immediately
- Container stops → Traefik detects the removal → route disappears immediately
-
No config files to edit — routing is defined in the
docker-compose.ymlalongside the service
labels:
- traefik.enable=true
- traefik.http.routers.myapp.rule=Host(`myapp.${DOMAIN}`)
- traefik.http.routers.myapp.entryPoints=websecure
- traefik.http.routers.myapp.middlewares=Real-IP@file, Headers@file💡 If a container is stopped or removed, its Traefik route vanishes with it. No stale routes, no 502 errors pointing at dead containers — the route simply doesn't exist anymore.
Cloudflare-Companion monitors running containers for Traefik Host() labels and automatically creates the corresponding DNS CNAME records in Cloudflare pointing at the tunnel:
- Container starts with a Host label → Cloudflare-Companion creates the DNS record
- Container stops → DNS record must be manually removed (auto-cleanup is not supported)
⚠️ Cloudflare-Companion only handles creation of DNS records, not deletion. If you remove a container or change its hostname, you need to manually delete the stale CNAME in the Cloudflare dashboard.
This completes the automation chain — from docker compose up to a fully routable, TLS-secured, Zero Trust-protected service with no manual steps (aside from DNS cleanup on removal).
flowchart TD
DC[docker-compose.yml<br/>with Traefik labels] -->|Container starts| Docker[Docker Engine]
Docker -->|Label detection| Traefik[Traefik<br/>creates route]
Docker -->|Label detection| CFC[Cloudflare-Companion<br/>creates DNS record]
Traefik -->|Routes traffic| App[Container]
CFC -->|CNAME → tunnel| CF[Cloudflare DNS]
Services with a static IP on the IoT network (e.g. Plex at 10.1.11.100, Home Assistant at 10.1.11.10) are also accessible directly on the LAN without going through the tunnel (provided internal firewall rules allow). This provides:
- Faster local access (no round-trip through Cloudflare)
- Continued access if the internet goes down
- Direct device-to-device communication for IoT protocols
The external (tunnel) and internal (LAN IP) access paths coexist — the same container is reachable both ways.
| Hop | TLS | Certificate |
|---|---|---|
| User → Cloudflare Edge | TLS 1.3 | Cloudflare edge certificate (automatic) |
| Cloudflare → Traefik (via tunnel) | Origin TLS (Full Strict) | Cloudflare origin certificate on Traefik |
| Traefik → Container | HTTP / HTTPS | N/A (internal Docker network) |
Full (Strict) mode in Cloudflare means it validates Traefik's origin certificate — a self-signed cert won't work. This prevents MITM between Cloudflare and the origin.
| Layer | Protection |
|---|---|
| Cloudflare Edge | DDoS protection, WAF, bot detection |
| Zero Trust Access | Identity-based authentication (IdP) before reaching the app |
| Cloudflare Tunnel | No exposed ports, outbound-only connection |
| Origin TLS (Full Strict) | Encrypted and validated origin connection |
| Traefik Middlewares | Security headers, real IP forwarding |
| Container Auth | Native application-level authentication |
🔗 Cloudflare Zero Trust | Cloudflare Tunnel | Traefik | Cloudflare-Companion
Overview
How-To