Self-Hosting a Dockerized App on WSL2, Exposed with a Cloudflare Tunnel
Description and use
Successfully deployed a Dockerized Node/Express app with PostgreSQL inside WSL2 and exposed it via a Cloudflare Tunnel using a real domain, avoiding external hosting.
Problem
Deploy a small Node/Express to-do app with Postgres persistence on Windows using WSL2, Docker Engine, and expose it via a real domain without paying for hosting.
Outcome
Successfully self-hosted a Dockerized Node/Express to-do app with Postgres inside WSL2 and exposed it at a real domain using a Cloudflare Tunnel, avoiding external hosting costs.
Workflow
- Install WSL2 Ubuntu via
wsl --install -d Ubuntuand restart Windows - Create Unix user in Ubuntu
- Install Docker Engine inside Ubuntu with
curl -fsSL https://get.docker.com | sudo shand add user to docker group - Log out/in to apply group membership
- Set up project: copy .env.example to .env and edit DB credentials
- Choose deployment path: either bare Dockerfile with host Postgres or docker-compose with Postgres container and crond sidecar
- If using docker-compose, run
sudo docker compose up --build -d - Verify app on localhost:3000 with
curl -i http://localhost:3000 - Install cloudflared Windows binary and login:
cloudflared.exe tunnel login - Create tunnel:
cloudflared.exe tunnel create todo-docker-poc - Route DNS:
cloudflared.exe tunnel route dns todo-docker-poc todo-list-docker.gerardoleon.dev - Configure tunnel in
cloudflared/config.ymlwith tunnel ID - Run tunnel from WSL2:
cloudflared.exe tunnel --config "C:\%PROJECT%\todo-docker\cloudflared\config.yml" run todo-docker-poc - Access app via domain
Evidence
A proof-of-concept to-do app — Node/Express and Postgres, running in Docker on WSL2 — reachable at a real domain through a named cloudflared tunnel.
If you already have WSL2 and a Cloudflare-managed domain, expect this to take a few minutes, not hours.
- Record date: 2026-09-21T11:15:41.266Z