Deploy Multi-Client WhatsApp API — Self-Hosted Evolution

Run many WhatsApp numbers from one Evolution API — per-instance keys

Deploy Multi-Client WhatsApp API — Self-Hosted Evolution

/var/lib/postgresql/data

Just deployed

Just deployed

/data

Deploy and Host Evolution API on Railway

Evolution API turns WhatsApp numbers into REST endpoints, and one deployment runs many of them. Each client, brand or region is its own instance with its own QR login, webhook target and API key — no Meta verification, no per-message billing. This template deploys it with Postgres, Redis and a session volume, configured for the case quickstarts skip: more than one number on one box.

What This Template Deploys

ServicePurpose
evolution-apiREST API and Manager UI on port 8080. The only public service.
PostgresInstance records, contacts, chats and messages for every number.
RedisSession cache and instance state.
Volume/evolution/instances — one WhatsApp auth directory per connected number.

Everything stateful is split: Postgres holds application data, while each number's WhatsApp credentials are files on the volume under its instance name. You need both, together.

About Hosting

Running one number is straightforward. Running twenty for twenty clients changes which mistakes matter, and the defaults are built for one.

Instances share a deployment, not an isolation boundary. AUTHENTICATION_API_KEY is global: it authenticates every request to every instance on the box. Hand it to a client so they can integrate and you have given them the ability to read, send from and delete every other client's number. Generate a per-instance key for each, and keep the global key for administration only. That one decision separates a usable multi-tenant deployment from a liability.

Memory scales with connected numbers, not traffic. Each instance holds an open socket and its session state inside the same Node process. A deployment sized for one number falls over at ten, and the symptom is instances dropping offline rather than an obvious out-of-memory error. Budget per number.

One volume means one service, so no horizontal scaling. Session directories live on a Railway volume, and a volume attaches to one service — replicas cannot share them. Growth is a bigger box, not more boxes; plan the ceiling before you sell past it.

DEL_INSTANCE can delete a client while you are not looking. Set to a number of minutes, it removes instances disconnected that long. Tidy on a single-user box; on an agency box it destroys a client's session because their phone was off, and reconnecting means a fresh QR scan with them on the line. Set it to false.

Every instance needs its own webhook target. Inbound messages are useless if twenty clients' events land on one endpoint. Configure the webhook per instance at creation, subscribing only to events you consume.

One deployment, one reputation. Numbers are banned individually, so one client's bulk sending does not take the others down directly — but they share your server and egress address, and patterns across numbers are visible. Agree in writing what clients may send, because you cannot enforce it technically from here.

Typical cost: ~$15–30/month for the API, Postgres and Redis at a handful of numbers, on rates of $10/GB/month RAM, $20/vCPU/month CPU and $0.15/GB/month volumes. Memory dominates, and it rises with each connected number.

How It Compares

Evolution APIMeta Cloud APITwilioOne deploy per client
Numbers per deploymentMany instancesPer WABA setupPer projectOne
Isolation between clientsLogical, by keyAccount-levelAccount-levelFull
Cost at ten clientsOne serverPer conversationPer messageTen servers

The honest edge: one deployment per client gives real isolation, and for regulated work or clients who will ask, that is the correct answer and worth the extra servers. The Cloud API is the supported path if they can wait on verification. This wins on economics and simplicity at small scale — one box, one upgrade, one thing to monitor — and the price is that isolation is a key you issue, not a boundary the platform enforces.

Deploy in Under 5 Minutes

  1. Click Deploy and pick a workspace. The API, Postgres and Redis come up wired, with a global AUTHENTICATION_API_KEY generated.
  2. Confirm the volume is mounted at /evolution/instances and set DEL_INSTANCE=false before connecting anyone.
  3. Open /manager, sign in with the global key, and create your first instance named for the client.
  4. Generate a per-instance key for that client and give them only that one.
  5. Set the instance's webhook to the client's endpoint, scan the QR, and send a test message.

Verify before you rely on it: connect two instances, then redeploy. Both should reconnect without new QR scans. If only one does, the volume layout is wrong and you will discover it at the worst time with a client on the phone.

Common Use Cases

  • Agency client management — one deployment, one instance per client, each with its own key and webhook.
  • Multi-brand support — separate numbers for separate brands or regions, with inbound events routed to different inboxes.
  • Staging beside production — a test number on the same box as live ones, isolated by instance rather than by another deployment.

Configuration

VariableRequiredDescription
AUTHENTICATION_API_KEYGeneratedGlobal admin key. Authenticates every instance — never give it to a client.
SERVER_URLRequiredPublic HTTPS domain. Webhook callbacks and media URLs are built from it.
DATABASE_PROVIDERRequiredpostgresql. Selects the Prisma schema applied at boot.
CACHE_REDIS_ENABLED, CACHE_REDIS_URIAutoRedis holds instance and session state.
DEL_INSTANCERecommendedfalse. A number of minutes here deletes disconnected clients automatically.
Storage volumePre-set/evolution/instances — one auth directory per number.

Issue per-instance keys to clients. The global key is not scoped to an instance; anyone holding it controls every number on the deployment.

Back up the volume and the database together. The database knows an instance exists; the volume holds the credential that keeps it logged in. Restore one alone and you get twenty instances all asking for a QR scan.

Dependencies for Evolution API Hosting

  • Railway account — ~$15–30/month at a handful of numbers; memory rises with each one connected.
  • Bundled services — Postgres for application data, Redis for instance state, both wired over private networking.
  • Volume — required at /evolution/instances. Each number's WhatsApp auth lives in its own directory there.
  • Optional — S3 or MinIO for media offload, and RabbitMQ or WebSocket if you want events streamed rather than posted.

Deployment Dependencies

Implementation Details

The API runs on a pinned tag behind Railway's HTTPS edge on port 8080, Manager UI at /manager on the same domain. Postgres and Redis are reached by private hostname through reference variables, and Prisma applies the schema on first boot — which is why DATABASE_PROVIDER must be set before the container starts.

Multi-instance is a naming convention rather than a tenancy model, and that shapes how you operate it. Every instance is a row in Postgres and a directory on the volume keyed by its name, served by one process under one global key. There is no per-tenant resource limit, no process-level isolation, no database partition. What you get is per-instance keys and per-instance webhooks, enough to keep clients out of each other's data provided the global key never leaks. Name instances for the client from the start; renaming later means reconnecting.

Capacity planning is simple because one variable dominates. Each connected number costs memory continuously whether busy or not, so your ceiling is memory divided by per-number cost and barely moves with message volume. The volume grows slowly with session files and quickly with media — offload media to S3 before that becomes a problem. Outgrowing one box means a second deployment with clients moved across, and moving an instance means a new QR scan.

Frequently Asked Questions

Can one deployment run many WhatsApp numbers? Yes. Each is an instance with its own QR login, webhook and API key, sharing one API process, one database and one volume.

Is one client isolated from another? Logically, by API key. There is no process or database partition, and the global key controls every instance — so key hygiene is the boundary.

How many numbers can one box handle? Memory is the limit and it scales per connected number, not per message. Watch usage as you add instances rather than assuming a figure.

What happens when a client's phone goes offline? The instance disconnects. With DEL_INSTANCE set to a duration it is eventually deleted along with its session; set it to false so reconnection does not need a new QR scan.

Can I scale this across replicas? No. The session volume attaches to one service, so growth is a larger instance or a second deployment.

Why Deploy Evolution API on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying Evolution API on Railway you get the configuration multi-number use actually needs — a session volume so every instance survives a redeploy, auto-deletion turned off so a disconnected client is not erased, Postgres and Redis on the private network, and a global key you keep rather than hand out.


Template Content

More templates in this category

View Template
Swarm
Named LLM bots join channels, take jobs, run routines on your Railway box

mcmax
3
View Template
Telegram JavaScript Bot
A template for Telegram bot in JavaScript using grammY

Agampreet Singh
294
View Template
Cobalt Tools [Updated Oct ’26]
Cobalt Tools [Oct ’26] (Media Downloader, Converter & Automation) Self Host

shinyduo
302