The Problem
If you've built a WhatsApp bot with whatsapp-web.js, you know the pain:
- Random disconnects every 2-6 hours
- QR re-authentication breaking at 3am
- Memory leaks from the embedded Chromium instance
- Multi-instance? Forget it — each instance eats 500MB+ RAM
I ran this stack in production for 8 months. It worked... until it didn't. The breaking point was when I needed multi-tenant support: one server handling WhatsApp sessions for multiple businesses.
The Migration
I moved from whatsapp-web.js (browser automation) to WAHA (a proper REST API that wraps WhatsApp Web internally). The difference:
| whatsapp-web.js | WAHA | |
|---|---|---|
| Architecture | Your code spawns Chromium | Standalone Docker container |
| Memory per session | ~500MB | ~80MB |
| Multi-session | Manual, fragile | Native REST endpoint |
| Session persistence | File-based, breaks | Built-in |
| Reconnection | You build it | Automatic |
| API style | Event-driven JS | REST + webhooks |
What Changed in the Code
The biggest architectural shift: instead of your bot being the WhatsApp process, your bot becomes a webhook consumer.
Before (whatsapp-web.js):
const client = new Client();
client.on("message", async (msg) => {
// Your bot IS the WhatsApp process
// If this crashes, WhatsApp disconnects
const response = await generateAIResponse(msg.body);
await msg.reply(response);
});
client.initialize(); // Spawns Chromium
After (WAHA webhook):
// WAHA runs separately as a Docker container
// Your bot receives webhooks
app.post("/webhook/message", async (req, res) => {
const { from, body, session } = req.body;
const response = await generateAIResponse(body, session);
// Send via REST API
await fetch(`${WAHA_URL}/api/sendText`, {
method: "POST",
body: JSON.stringify({
session,
chatId: from,
text: response
})
});
res.sendStatus(200);
});
The separation means:
- Bot crashes don't kill WhatsApp sessions
- You can restart your bot without re-authenticating
- Multiple bots can share the same WAHA instance
- Scaling is just adding more webhook consumers
The AI Agent Layer
Once the WhatsApp connection was stable, I built the actual agent runtime on top: SARA (github.com/Alessandro114/sara).
SARA handles:
- Tool-calling loop on Groq/Cerebras/Mistral (free tiers)
- 20 industry-specific knowledge packs (restaurants, clinics, retail...)
- Session memory that persists across conversations
- Multi-tenant isolation — each business gets its own agent persona
The entire stack runs self-hosted via Docker Compose:
services:
waha:
image: devlikeapro/waha
ports:
- "3000:3000"
sara:
build: .
environment:
- WAHA_URL=http://waha:3000
- GROQ_API_KEY=${GROQ_API_KEY}
Results After Migration
- Uptime: 99.7% over 3 months (vs ~92% with whatsapp-web.js)
- Memory: 80MB per session (vs 500MB)
- Deploy time: restart bot in 2s without losing sessions
- Multi-tenant: 5 businesses on one $10/mo VPS
Try It
SARA is open source (AGPL-3.0): github.com/Alessandro114/sara
If you're stuck on whatsapp-web.js and hitting the same problems, the WAHA migration is worth the effort. The codebase is ~3K lines of TypeScript.
I'm the author of SARA. Questions about the migration or architecture welcome in the comments or on GitHub.
Top comments (0)