# How to Securely Expose Your Local Lab Using Cloudflare Tunnel and Docker

In the world of Cloud Engineering, security is not an afterthought; it’s a foundation. When hosting a portfolio or a home lab, the old-school method of **Port Forwarding** is a major security risk. It exposes your home IP and leaves your network vulnerable.

Today, we are taking a **Zero Trust** approach. We will use **Cloudflare Tunnel (cloudflared)** and **Docker Compose** to create a secure bridge that allows the world to see your work without ever opening a port on your router.

---

### The Architecture

Before we dive into the terminal, let's look at the flow of traffic:

1. **User** requests domain [`example.com`](http://uptodeploy.com).
    
2. **Cloudflare Edge** receives the request.
    
3. **Cloudflared Connector** (running in your Docker or server) pulls the request through an outbound-only encrypted tunnel.
    
4. **Webserver** serves the static files locally.
    

![Cloudflare Tunnel · Cloudflare One docs](https://developers.cloudflare.com/_astro/handshake.eh3a-Ml1_1IcAgC.webp align="left")

> URL: [https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/)

---

### Prerequisites for our project:

* **Domain:** Managed by Cloudflare (e.g., [`up2runc.com`](http://up2runc.com)).
    
* **Environment:** Docker & Docker Compose installed.
    
* **Access:** Cloudflare Zero Trust dashboard (Free Tier).
    

---

### Step 1: Initialize the Cloudflare Tunnel

We need to create the tunnel identity in the Cloudflare Cloud.

1. Navigate to the **Zero Trust Dashboard** &gt; **Networks** &gt; **Connectors**
    
2. Click **Create a Tunnel** &gt; **Cloudflared &gt; Select Cloudflared**
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1767668222759/73418942-000c-4e08-b449-aa7b31c55bea.png align="center")
    
3. Name it (e.g `website-prod`)
    
4. **Save Tunnel**
    
5. Choose **Docker provider**:
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1767668334963/e12c78eb-fa7a-46a4-8ce9-b0c8c8d7d4e0.png align="center")
    
    **Important:** Never share your token!!
    

---

### Step 2: Provisioning with Docker Compose

We will define our infrastructure as code. This ensures our setup is reproducible.

**File:** `docker-compose.yml`

YAML FILE

```yaml
version: '3.8'
services:
  # Service 1: The Alpine Linux
  web:
    image: nginx:alpine
    container_name: web-server
    restart: unless-stopped
    volumes:
      - .:/usr/share/nginx/html:ro

  # Service 2: The Tunnel Connector
  tunnel:
    image: cloudflare/cloudflared:latest
    container_name: cloudflare-connector
    restart: unless-stopped
    command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
    depends_on:
      - web
```

---

### Step 3: Configure Public Hostnames

Go back to your Cloudflare Tunnel settings and click the **Public Hostname** tab.

* **Public Hostname:** [`up2runc.com`](http://uptodeploy.com)
    
* **Service:** [`http://web:80`](http://web:80)
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1767668742647/c950bd59-858e-495e-9878-1503ef66ed5a.png align="center")

> **Note:** We use `web:80` because Docker Compose creates an internal network where the services can talk to each other by name.

---

### Step 4: Deployment & Verification

Execute the deployment:

Bash

```bash
export TUNNEL_TOKEN=your_token_here
docker-compose up -d
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1767670925971/53bd9349-3795-4db9-bcbe-c2be049e02db.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1767671022636/484b0278-2a76-4d8a-b714-5bb2c7bc1c53.png align="center")

#### Verification Checklist:

* **Status:** Check the tunnel status in the dashboard; it should show **HEALTHY**.
    
* **Connectivity:** Run `curl -I` [`https://up2runc.com`](https://uptodeploy.com) and look for the `Server: cloudflare` header.
    
* **Security:** Confirm your router has **NO** ports forwarded to your machine.
    

---

### Key Takeaways

1. **Outbound Only:** The tunnel only makes outbound connections. This means your firewall stays closed.
    
2. **Identity-Aware:** You can now add Cloudflare Access to require a login before anyone even sees your site.
    
3. **Static Content:** Nginx Alpine is the excelent standard for lightweight, high-performance static hosting.
    

---

### Conclusion

And just like that—simple and efficient—we’ve implemented a robust, Cloudflare-protected solution. We’ve moved from a 'Home Hobbyist' setup to a Professional Zero Trust Architecture, proving that high-level security doesn't have to be over-complicated.

By eliminating the public attack surface and leveraging Docker's immutability, [up2runc.com](http://up2runc.com) is now production-ready.

**Are you ready for the next project?**
