| name | cloudflare-container-gateway-autostart |
| description | Fix Cloudflare Workers Container gateway not starting automatically for Discord/Telegram
bots behind Cloudflare Access. Use when: (1) /api/status returns "not_running" but
/sandbox-health returns "ok", (2) Discord or Telegram bot stops responding after
deploy, (3) Bot only works after visiting the web UI in browser, (4) Container is
running but internal process (gateway) hasn't started. Applies to Cloudflare Sandbox
containers with Durable Objects.
|
| author | Claude Code |
| version | 1.0.0 |
| date | "2026-02-07T00:00:00.000Z" |
Cloudflare Container Gateway Auto-Start
Problem
When running a bot (Discord, Telegram, Slack) inside a Cloudflare Workers Container that's
protected by Cloudflare Access, the bot goes offline because the gateway process only starts
when an authenticated request comes in. Since bot messages are outbound connections (not
inbound HTTP), no request triggers the startup.
Context / Trigger Conditions
- Bot stops responding to Discord/Telegram messages
/api/status returns {"ok": false, "status": "not_running"}
/sandbox-health returns {"status": "ok"} (container is up, but process isn't)
- Bot works after visiting the web UI in browser (which authenticates via CF Access)
- This happens after deploys or when the container restarts
- Using Cloudflare Workers Containers (Sandbox) with Durable Objects
Root Cause
The container lifecycle:
- Container starts when any request hits the Durable Object
/sandbox-health touches the container but doesn't call ensureMoltbotGateway()
- Protected routes (behind CF Access) call
ensureMoltbotGateway() to start the process
- No authenticated requests = gateway never starts = bot stays offline
The Discord/Telegram connections are outbound from the container, so they don't trigger
the gateway startup flow that expects inbound authenticated requests.
Solution
1. Add a Public /start Endpoint
Create a public endpoint that starts the gateway without requiring authentication:
import { ensureMoltbotGateway } from '../gateway';
publicRoutes.post('/start', async (c) => {
const sandbox = c.get('sandbox');
try {
console.log('[start] Starting gateway...');
await ensureMoltbotGateway(sandbox, c.env);
return c.json({ ok: true, status: 'started' });
} catch (err) {
console.error('[start] Failed to start gateway:', err);
return c.json({
ok: false,
status: 'error',
error: err instanceof Error ? err.message : 'Unknown error'
}, 500);
}
});
2. Make Cron Job Start the Gateway
Modify the scheduled handler to ensure the gateway is running:
async function scheduled(event, env, ctx) {
const sandbox = getSandbox(env.Sandbox, 'moltbot', options);
console.log('[cron] Ensuring gateway is running...');
try {
await ensureMoltbotGateway(sandbox, env);
console.log('[cron] Gateway is running');
} catch (err) {
console.error('[cron] Failed to start gateway:', err);
}
await syncToR2(sandbox, env);
}
3. Configure Cron in wrangler.jsonc
{
"triggers": {
"crons": ["*/5 * * * *"]
}
}
Verification
- Deploy the changes:
npm run deploy
- Check status:
curl https://your-worker.workers.dev/api/status
- Should return
{"ok": false, "status": "not_running"}
- Start gateway:
curl -X POST https://your-worker.workers.dev/start
- Should return
{"ok": true, "status": "started"}
- Verify running:
curl https://your-worker.workers.dev/api/status
- Should return
{"ok": true, "status": "running", "processId": "..."}
- Wait 5 minutes, check status again - should still be running (cron keeps it alive)
Example: Debugging Flow
curl https://moltbot.workers.dev/sandbox-health
curl https://moltbot.workers.dev/api/status
curl -X POST https://moltbot.workers.dev/start
curl https://moltbot.workers.dev/api/status
Notes
- The
/start endpoint is public (no auth) which is intentional - it only starts what
should already be running, doesn't expose any data
- Cron interval of 5 minutes means worst case the bot is offline for ~5 minutes after
a crash, not indefinitely
- Consider adding alerting if the gateway fails to start repeatedly
- The
keepAlive: true sandbox option keeps the container alive, but not the process
inside - they're separate concerns
Architecture Insight
Request Flow:
┌─────────────────────┐
Browser ──CF Access──> Worker ──>│ ensureMoltbotGateway│──> Gateway starts
└─────────────────────┘
Discord ──────────────────────────────> Gateway (already running)
↑
Must pre-start!
Solution:
Cron ──────────────────> Worker ──> ensureMoltbotGateway ──> Gateway stays alive
↓
POST /start ──────────> Worker ──> ensureMoltbotGateway ──> Manual recovery
References