---
# System prepended metadata

title: Running Cloudflare Tunnel with Docker
tags: [docker, cloudflare]

---

# Running Cloudflare Tunnel with Docker

If you want to expose an internal service to the internet **without opening inbound ports**, **without port-forwarding**, and without maintaining a complex reverse-proxy stack, **Cloudflare Tunnel** is one of the cleanest options.

In this post, I’ll show a **token-based** Cloudflare Tunnel setup using **Docker**. The goal is a setup that is:

- **Configless** (no `config.yml` required)
- **Stateless** (easy to recreate anywhere)
- **Restart-safe** (survives reboots)
- **CI/CD friendly** (works great with secrets)

---

## What you’ll build

By the end, you’ll have a `cloudflared` container running that:

- Authenticates to your Cloudflare account using a **tunnel token**
- Runs continuously on your server
- Can reach your internal services via a Docker network (e.g., `app-net`)

---

## Prerequisites

Before you start:

- Docker is installed on your machine/server
- You have access to **Cloudflare Zero Trust**
- You created a **Tunnel** in Zero Trust
- You have the **Tunnel Token** for that tunnel

> You can find the token in Cloudflare Zero Trust → **Tunnels** → select your tunnel → **Run connector** (or equivalent).

---

## Why token-based (configless) setup?

Using a token directly:

- avoids managing `config.yml` and credentials files
- makes the container **stateless** and reproducible
- fits nicely into infrastructure automation and CI/CD pipelines

In production, you should store the token as a secret (e.g., `.env`, Docker secrets, CI/CD secrets) instead of hardcoding it.

---

## Step-by-step: the script structure

### 1) Run Bash in “safe mode”

We start with strict Bash flags:

- exit on error
- fail on undefined variables
- fail on pipeline errors

This prevents silent failures and makes the script more reliable.

### 2) Define the core variables

We keep three things configurable:

- container name
- image version (pinned for reproducibility)
- tunnel token

### 3) Start the Docker container

We run `cloudflared` in detached mode and ensure it restarts automatically.

We also attach it to a Docker network (`app-net`) so it can reach internal services by container name.

### 4) Run the tunnel with `--no-autoupdate`

When using Docker, auto-updating inside the container is not desirable. You should update by changing the image tag.

---

## Full script (copy/paste)

> Save as `cloudflare-tunnel.sh`, then run: `chmod +x cloudflare-tunnel.sh && ./cloudflare-tunnel.sh`

```bash
#!/usr/bin/env bash
set -euo pipefail

# ============================================================
# Cloudflare Tunnel setup script (bash)
#
# This script:
# - starts a cloudflared container
# - runs a Cloudflare Tunnel using a token
# ============================================================

# ------------------------------------------------------------
# 0) Base variables
# ------------------------------------------------------------
CONTAINER="cloudflared"
IMAGE="cloudflare/cloudflared:2025.11.1"
TUNNEL_TOKEN="eyJhIjo..."  # <-- replace with your real token

# ------------------------------------------------------------
# 1) Start cloudflared container
# ------------------------------------------------------------
docker run -d \
  --name "$CONTAINER" \
  --restart unless-stopped \
  --network app-net \
  "$IMAGE" \
  tunnel --no-autoupdate run --token "$TUNNEL_TOKEN"
