Run Chatwoot + Dify on Cloudflare + your own VPS
Cloudflare handles DNS, WAF and Tunnel; the VPS runs the core services. From zero to live in about 90 minutes.
Cloudflare + VPS (Hetzner / DigitalOcean / Vultr) 90 min
Prerequisites#
- A 4 vCPU / 8 GB VPS (Ubuntu 24.04)
- A domain on Cloudflare (e.g.
support.yourdomain.com,dify.yourdomain.com) - An LLM provider API key (OpenAI / Anthropic / Qwen / etc.)
Step 1 — Install Docker & Compose#
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
Step 2 — Expose services via Cloudflare Tunnel#
Skip opening 80/443 directly — route everything through Cloudflare Tunnel:
docker run -d --name cloudflared --network host \
cloudflare/cloudflared:latest tunnel --no-autoupdate run --token <YOUR_TOKEN>
In Cloudflare Zero Trust, add public hostnames for support.yourdomain.com → http://localhost:3000 and dify.yourdomain.com → http://localhost:5001.
Step 3 — Bring up Dify#
git clone https://github.com/langgenius/dify.git
cd dify/docker
cp .env.example .env
# Edit .env — set CONSOLE_WEB_URL and APP_API_URL to https://dify.yourdomain.com
docker compose up -d
Open https://dify.yourdomain.com, register the admin, configure a model, create a Chatbot app, upload knowledge, publish, copy the API key.
Step 4 — Bring up Chatwoot#
git clone https://github.com/chatwoot/chatwoot.git
cd chatwoot
cp .env.example .env
# Edit .env — FRONTEND_URL=https://support.yourdomain.com
docker compose -f docker-compose.production.yaml run --rm rails bundle exec rails db:chatwoot_prepare
docker compose -f docker-compose.production.yaml up -d
Open https://support.yourdomain.com, register, create an account and an inbox.
Step 5 — Wire Dify as Chatwoot’s Agent Bot#
Settings → Agent Bots → New:
- Outgoing URL —
https://dify.yourdomain.com/v1/chat-messages - Header —
Authorization: Bearer app-xxxxxxxx
Then in your target inbox, set this bot as the fallback.
Step 6 — Verify#
Send a message through the website widget; the bot should reply, and Dify’s logs should show the call.
Common pitfalls#
- Chatwoot can’t send mail — use Postmark / SES SMTP;
MAILER_SENDER_EMAILmust match the verified sender domain - Slow Chinese embeddings — switch to
bge-m3or use SiliconFlow’s free tier - Cloudflare Tunnel returns 502 — make sure containers listen on
0.0.0.0, not127.0.0.1