WWaSphere
GitHub
WaSphere Teamself-hostdockerwhatsapp-apitutorial

How to Self-Host a WhatsApp API with Docker in 5 Minutes

A practical, copy-paste tutorial to self-host a WhatsApp API with Docker — clone, set secrets, docker compose up, and send your first message in minutes.

How to Self-Host a WhatsApp API with Docker in 5 Minutes

Most WhatsApp API providers want you on their cloud, paying per conversation, with your message data sitting on their servers. You don't have to accept that. WaSphere is a self-hosted WhatsApp automation platform you run on your own box with a single Docker Compose stack — four services, one .env, and a browser tab.

This is the fast path: clone, set your secrets, bring the stack up, and send a message. No reverse proxy and no Redis to wrangle — the default stack is deliberately small.

What you're deploying

The stack is four containers defined in one root docker-compose.yml:

  • postgres — stores accounts, sessions, message history, API keys, webhooks, and the audit log.
  • wa-server (port 3001) — manages WhatsApp connections via Baileys. Each session is its own connection.
  • dashboard-api (port 3000) — the REST API your apps call. Runs database migrations automatically on startup.
  • dashboard-ui (port 3004) — the web dashboard you open in a browser.

There is no bundled Traefik and no Redis. In production you front it with your own nginx, Caddy, or Traefik for TLS — but for the first run, localhost is all you need.

Requirements

  • Docker Engine 24+ and Docker Compose v2.20+
  • 1 vCPU and 1 GB RAM is enough to start; budget ~100–150 MB extra per active WhatsApp session

Verify Docker is ready:

docker compose version
# Docker Compose version v2.27.0

Step 1 — Clone the repository

git clone https://github.com/wasphere/wasphere.git
cd wasphere

Step 2 — Generate your secrets

Copy the example env file:

cp .env.example .env

Every secret should be unique and random. Generate them all at once:

for k in JWT_SECRET ENCRYPTION_KEY WA_TOKEN WEBHOOK_SIGNING_SECRET INTERNAL_WEBHOOK_SECRET POSTGRES_PASSWORD; do
  echo "$k=$(openssl rand -hex 32)"
done

Paste the output into .env, then set the remaining values:

POSTGRES_USER=wasphere
POSTGRES_DB=wasphere
DASHBOARD_UI_URL=http://localhost:3004

A quick reference for what each secret does:

VariablePurpose
JWT_SECRETSigns dashboard auth tokens
ENCRYPTION_KEYEncrypts stored credentials at rest
WA_TOKENAuthenticates Dashboard API → WA Server calls
WEBHOOK_SIGNING_SECRETDefault HMAC-SHA256 webhook signing key
INTERNAL_WEBHOOK_SECRETWA Server → Dashboard API internal events

Never reuse one value for two secrets — each one isolates a different failure mode.

Step 3 — Bring the stack up

docker compose up -d

The first run pulls images and builds (1–3 minutes). Migrations run automatically when the Dashboard API starts — there's no manual Prisma step. Watch it come up:

docker compose logs -f

Confirm all four services are running:

docker compose ps

Step 4 — Register the admin account

Open http://localhost:3004. On first launch the dashboard shows a registration screen — the first account you create becomes the admin. Sign in, then:

  1. Go to Sessions → New Session and give it a name.
  2. Scan the QR code from WhatsApp → Linked Devices → Link a Device.
  3. The status flips to connected.

Use a dedicated number for automation, not your personal account.

Step 5 — Create a scoped API key and send a message

Under API Keys, create a key with the messages:send permission. You can scope it to a single session for tighter isolation. Keys are prefixed wsk_live_... and shown only once.

Now send your first message. The session lives in the URL; the body just carries the recipient and text:

curl -X POST \
  "https://api.your-domain.com/workspaces/{workspaceId}/proxy/api/sessions/{sessionId}/messages/text" \
  -H "Authorization: Bearer wsk_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "447700900123",
    "text": "Hello from my self-hosted WhatsApp API!"
  }'

The to field is the recipient in international format without the +. Locally you can call the API directly on http://localhost:3000; once you put a proxy in front, use your public api.your-domain.com host.

Going to production

For a public deployment, terminate TLS with your own proxy and forward to ports 3004 (UI) and 3000 (API). A minimal Caddy config:

app.your-domain.com {
    reverse_proxy localhost:3004
}
api.your-domain.com {
    reverse_proxy localhost:3000
}

Updating later is two commands — docker compose pull && docker compose up -d — and migrations apply themselves on restart.

Why self-host

You own the database. There are no per-message fees. Webhooks are HMAC-SHA256 signed, API keys carry 12 scoped permissions plus optional session scoping, and every action lands in an audit log. The whole thing is MIT-licensed, so you can read, fork, and run it however you like.

Ready to connect a session and send for real? Follow the Quick Start guide.

Get Started

Ship a self-hosted WhatsApp API today

Clone the repo, set your secrets, and send your first message in minutes — Docker-based, MIT licensed, zero config.

Read the Quick Start