Skip to content

Vibe Coding 05

How to Publish Your Application Online Using Cloudflare Tunnel

From local dev to live URL — using AI-assisted deployment with Docker, Nginx, and Cloudflare Zero Trust

Author: Lâm Trịnh Chí Tài (Gyn) | May 2026

Docker Cloudflare Claude AI Nginx Tutorial
01

Prerequisites & Domain Preparation

Prepare your tech stack and secure your domain before diving in.

Tech Stack Overview

Before diving into how to publish an application online, you need to prepare your tech stack and secure your domain. Here is a quick overview of the environment we will be using:

  • Server: Ubuntu 22.04.5 LTS @ YOUR_SERVER_IP
  • Stack: Docker Compose, nginx:alpine, cloudflare/cloudflared, Cloudflare Named Tunnel

Pre-requisites

  • An active Cloudflare account (the Free-tier works perfectly).
  • A product application ready for deployment.
  • A separate server (a VPS is highly recommended for consistency). Ensure your local device can communicate with this server.

Domain Transfer to Cloudflare

If you purchased your domain from a different domain registrar, your first step is to transfer the domain to Cloudflare.

Navigate to https://dash.cloudflare.com and log in to your Cloudflare account. Once logged in, look to the left panel and navigate to Domains > Transfers.

Cloudflare Dashboard left panel showing Domains and Transfers navigation option
Cloudflare Dashboard — Domains > Transfers navigation

If the page appears blank, simply click the Get started or Onboard a domain button to begin the domain transfer process.

Transfers page showing no eligible domains detected with onboard prompt
Transfers Page — No eligible domains detected

Input your desired domain name into the "Domain name" field and click Continue:

Transfer your domain page with domain name input field and Continue button
Transfer your domain — Input form

Once completed, select the Free-tier option by clicking the Select Plan button.

Select a plan page with Free Tier option highlighted and Select Plan button
Select a plan — Free Tier selected

Click Continue to Activation to proceed.

Continue to Activation button on the plan confirmation page
Continue to Activation

Follow the step-by-step guidelines on this page to properly forward your domain from your original registrar to Cloudflare. When finished, click the I updated my nameservers button.

Update your nameservers page with instructions and I updated my nameservers confirmation button
Update your nameservers to activate Cloudflare

Now, patiently wait for Cloudflare to officially authenticate and propagate your domain.

Cloudflare domain overview showing pending propagation status from registrar
Overview — Waiting for registrar to propagate
02

Server Access & Initial Preparation

Establish SSH access to your server with Claude AI assistance.

Establishing SSH Access

With your domain ready, the next step is establishing SSH access to your server. We will be using Claude AI to streamline this process.

Prepare a credentials and information .md file for Claude to use as a baseline for logging into the server. Your file should follow this template format:

Server's IP address:
Username:
Password:

Next, open the Claude Code CLI, Claude Desktop, or your preferred IDE equipped with the Claude Code extension, and prompt it to connect to your server.

Claude Code attempting to access the server using paramiko and sshpass libraries
Claude attempting to access the server via paramiko/sshpass
Behind the Scenes: SSH & Server Prep

Issue 1: sshpass fails with "Permission denied"

Cause: There is no /dev/tty available in a non-interactive shell, which means sshpass cannot successfully send the password.
Fix: Replace sshpass with the Python paramiko library for all SSH and SFTP operations.

Issue 2: sudo commands fail with "a terminal is required"

Cause: The sudo command requires a TTY by default, and paramiko sessions do not have a PTY.
Fix: Pipe the password to sudo via standard input using: echo PASSWORD | sudo -S <command>

⚠ Security Note: Piping passwords via echo PASSWORD | sudo -S is a non-interactive workaround only. For production environments, use SSH key-based authentication and configure NOPASSWD in your sudoers file for safer, passwordless privilege escalation.

Install Docker on the Server

Our initial Ubuntu server had no Docker installed. We installed it using the official Docker deployment script:

curl -fsSL https://get.docker.com | sh
03

Application Deployment via Cloudflare Zero-Trust

Let Claude AI orchestrate the full deployment sequence.

Deploying with cloudflared

Now, request the AI agent to publish the application using cloudflared (Cloudflare Zero-Trust).

Claude assessing project files and asking deployment configuration questions before proceeding
Claude assessing project files and asking configuration questions
Behind the Scenes: Deployment Sequence

Here is the complete sequence Claude executed to publish the application:

  1. Step 1: Prepare Local Source Files. The project consists of standard web assets: index.html, style.css, and various JavaScript modules.
  2. Step 2: Create Server Directory & Upload Files. We created the directory /opt/docker/snake-game/app/ on the server and uploaded the workspace files.
  3. Step 3: Create Cloudflare Named Tunnel. We generated a new Cloudflare tunnel with a unique tunnel ID.
  4. Step 4: Route DNS to Tunnel. We successfully linked a CNAME record to snake.gynlam328.com.
  5. Step 5: Upload Configuration. We created the config.yml file and uploaded the necessary credentials JSON.
  6. Step 6: Deploy. Finally, we created the docker-compose.yml file and executed docker compose up -d.

The application is now successfully published to the internet using Cloudflare Zero-Trust!

Claude confirming the Cloudflare tunnel is fully connected and running
Claude confirming tunnel is fully connected
Cloudflare Dashboard showing tunnel status as HEALTHY with green indicator
Cloudflare Dashboard — Tunnel Status: HEALTHY
Browser successfully loading the Snake Game application at snake.gynlam328.com
Browser loading the game at snake.gynlam328.com
04

Troubleshooting & Verification

Real issues encountered — and how Claude AI resolved them.

DNS Record Conflict

Even with automated deployments, issues can arise. Here is how we handled troubleshooting.

If the agent mentions that a DNS record already exists, you need to clear it out manually. Navigate to Cloudflare > Domain > DNS > Records and delete the conflicting existing record.

Cloudflare DNS management panel showing existing snake CNAME record from a previous tunnel
Cloudflare DNS — existing snake CNAME record
Deleting the stale CNAME record in Cloudflare DNS management interface
Deleting the stale CNAME record
Behind the Scenes: Stale DNS Error

Cause: A stale CNAME from a previously deleted tunnel was lingering in the Cloudflare DNS configuration. The --overwrite-dns flag refused to update it because the record appeared to already belong to "your account."
Fix: We manually deleted the stale DNS record directly from the Cloudflare dashboard, and then re-ran the routing command using the tunnel UUID.

Once cleared, prompt Claude to retry the connection:

User prompting Claude to re-run the tunnel routing command after manually deleting the DNS record
Prompting Claude to re-run after DNS deletion

After fixing the DNS and tunnel configuration, Claude will confirm that the application is successfully routed to the internet.

Claude routing DNS via Tunnel ID and updating the docker-compose configuration file
Claude routing DNS via Tunnel ID and updating docker-compose

However, what happens if Claude confirms the app is published, but it still remains inaccessible?

Browser showing HTTP ERROR 503 service unavailable after tunnel configuration
Browser showing HTTP ERROR 503
Behind the Scenes: HTTP 503 Error

Cause: The cloudflared service was initially started in --token mode. Token mode requires your ingress rules to be configured entirely within the Cloudflare Zero Trust dashboard UI, meaning any local config.yml is ignored and not read.
Fix: We switched to config-file mode by removing the --token flag and mounting a config.yml file containing explicit ingress rules.

Playwright Verification

Ask Claude to implement this fix and utilize Playwright MCP to test the live page continuously until the 503 issue is entirely resolved.

Claude running Playwright browser tests on the live URL and fixing the favicon 404 error
Claude running Playwright browser tests and fixing favicon
Behind the Scenes: Playwright & Analytics Errors

Issue: favicon.ico 404 error

Fix: We added an inline data URI for the favicon directly into the index.html.

Issue: Cloudflare analytics beacon error during Playwright testing

Cause: The Playwright testing sandbox naturally blocks external network calls. No actual fix is needed for this, as it resolves itself in real-world browsers.

Application Live

With those final tweaks, the web page is officially up, running, and playable.

Final Snake Game successfully rendering and fully playable in the browser at snake.gynlam328.com
Snake Game live and fully functional at snake.gynlam328.com

Technical Reference

Key deployment details and server structure for reference.

Final Server Directory Layout

/opt/docker/snake-game/
├── app/                          # Static site files
│   ├── index.html
│   ├── style.css
│   ├── game-constants.js
│   ├── game-state.js
│   ├── game-logic.js
│   ├── game-sprites.js
│   ├── game-fx.js
│   ├── game-audio.js
│   ├── game-render.js
│   └── game-main.js
├── cloudflared/
│   ├── config.yml
│   └── YOUR_TUNNEL_ID.json
└── docker-compose.yml

Key Deployment References

  • Tunnel name: snake-game
  • Tunnel ID: YOUR_TUNNEL_ID
  • DNS CNAME target: YOUR_TUNNEL_ID.cfargotunnel.com
  • Live URL: https://snake.gynlam328.com
  • Server: YOUR_USERNAME@YOUR_SERVER_IP (Ubuntu 22.04.5)
  • Deploy path: /opt/docker/snake-game/

Get In Touch

Complex infrastructure challenges deserve elegant solutions. Let's realize it together.

Email

gynlam328@gmail.com

Send email

Phone

+84-83314-1685

Call now

Location

Ban Co Ward - Previously known as District 3, HCMC

Connect with me

© 2025 - 2026 Lâm Trịnh Chí Tài (Gyn)

All rights reserved