Container workflow for Cloudflare Locally-Managed Tunnels

My objective is to establish locally-managed tunnels that terminate on docker containers. My preference would be to do this using only environment variables but I can inject configuration using docker volumes if that is the recommended approach.

I feel as though the documentation is not clear on the actions required to stand up tunnels in containers (at least not to me). For example, the “File structure for public hostnames” section describes a configuration file that includes the tunnel UUID and a JSON creds file. This seems as though it would require “out-of-band” CLI commands to gather this and then inject the config into the container.

What is the recommended approach for a cloudflared container to authenticate, establish the tunnel and then add a route for a public hostname?

In case others are interested, here is the solution I came up with. It requires rolling your own container so that you can run cloudflared CLI within the container.

Dockerfile

FROM debian:bullseye-slim

RUN apt-get update && apt-get install curl -y && \
  mkdir -p --mode=0755 /usr/share/keyrings && \
  curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | \
  tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null  && \
  echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] \
  https://pkg.cloudflare.com/cloudflared bullseye main" | \
  tee /etc/apt/sources.list.d/cloudflared.list && \
  apt-get update && apt-get install cloudflared && \
  apt-get clean && apt autoremove

ENTRYPOINT sh /usr/local/bin/entrypoint.sh

entrypoint.sh

#!/bin/bash

TUNNEL_NAME=`mktemp -u docker-XXXX`
echo "About to start $TUNNEL_NAME"
cloudflared tunnel create $TUNNEL_NAME
cloudflared tunnel route dns $TUNNEL_NAME $TUNNEL_NAME.example.com
cloudflared tunnel run --url http://192.168.55.10 $TUNNEL_NAME

docker_compose.yml

version: '3.4'

services:

  tunnel:
    image: jeffbrl/cloudflared
    container_name: jtunnel
    restart: unless-stopped
    command: tunnel run
    volumes:
      - ./cert.pem:/etc/cloudflared/cert.pem
      - ./entrypoint.sh:/usr/local/bin/entrypoint.sh
    networks:
      tunnelnet0:
        ipv4_address: 192.168.55.5

  publicweb:
    image: lscr.io/linuxserver/nginx:latest
    container_name: tunnelweb33
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
    networks:
      tunnelnet0:
        ipv4_address: 192.168.55.10

networks:
  tunnelnet0:
    ipam:
      config:
        - subnet: 192.168.55.0/28

I’ll post a gist on github when I have time.

If there are better ways to accomplish my objective, I remain interested in hearing.