Skip to content

Raspberry Pi with Cloudflare Tunnel

This example runs dpl on a Raspberry Pi at home and publishes a static site through a Cloudflare Tunnel. The router forwards no ports and the Pi needs no public address. Cloudflare terminates TLS and reaches nginx through the connector container.

Four units: tunnel (cloudflare-tunnel), nginx (http-server), site-domain (domain), and site (a static app).

  • A 64-bit Raspberry Pi (3 or newer) with Raspberry Pi OS Lite 64-bit. The current release is based on Debian 13 (trixie) and ships Podman 5.4, which meets dpl’s Podman 4.8+ requirement.
  • A domain whose DNS is hosted on Cloudflare. The free plan is enough.

Install Podman and dpl as root:

Terminal window
sudo -i
apt install podman
curl -fsSL https://dpl.cesbo.com/install.sh | sh

In the Zero Trust dashboard open Networks, then Tunnels. Create a tunnel with the Cloudflared connector and copy its token. Store the token as a secret:

Terminal window
dpl secret create cf-tunnel-token

Create /opt/dpl/conf/tunnel.yaml:

type: cloudflare-tunnel
secret: cf-tunnel-token

Deploy it:

Terminal window
dpl deploy tunnel

The dashboard shows the connector as healthy once it connects. In the tunnel settings add a Public Hostname: pi.example.com with service http://dpl--nginx:80. nginx is the name of the http-server unit below. Cloudflare creates the DNS record.

Create /opt/dpl/conf/nginx.yaml:

type: http-server
http_port: false

Nothing is published on the Pi. nginx is reachable only on the dpl network, where the connector finds it as dpl--nginx.

Create /opt/dpl/conf/site-domain.yaml:

type: domain
server: nginx
hosts: ["pi.example.com"]
proxy:
type: cloudflare-tunnel
routes:
- kind: serve_files
location: /
root: ${site:export}

proxy.type: cloudflare-tunnel makes nginx trust the connector and log the visitor’s IP from CF-Connecting-IP. It requires http_port: false on the http-server. The domain deploy refuses otherwise.

Any folder with an index.html will do. Create /opt/dpl/conf/site.yaml:

type: app
image: docker.io/library/alpine:3
builds:
- files: ["*"]
exports:
- source: /app
path: /

There is no build script and no runtime. dpl copies the archive into the image and exports it for nginx. For a generated site see Static site.

Deploy:

Terminal window
tar -czf /tmp/site.tar.gz -C path/to/site .
dpl deploy site /tmp/site.tar.gz

The app deploy also deploys the domain and starts nginx. Open https://pi.example.com.

Terminal window
dpl inspect tunnel
dpl inspect nginx
tail /opt/dpl/state/tunnel/log/runtime.log

The tunnel log shows Registered tunnel connection lines once the connector is online. If the dashboard reports the tunnel as down, that log is the place to look.