Every NATS account can be configured with a maximum number of subscriptions. When the subscription count reaches 90% or more of that limit, the account is one burst of new connections away from subscription failures. Clients attempting to subscribe past the limit receive errors, and services that depend on dynamic subscriptions — request-reply handlers, inbox listeners, queue groups — silently stop working.
Subscription limits exist to prevent a single account from consuming disproportionate routing resources across the cluster. Each subscription creates an entry in the server’s interest graph, and in clustered or super-cluster deployments, subscription information propagates to every server via routes, gateways, and leaf node connections. An account with unbounded subscriptions can inflate the interest graph across the entire infrastructure.
When an account hits its subscription ceiling, the failure mode is quiet and disruptive. The server rejects new SUB protocol commands for that account, but existing subscriptions continue to work. This means the first clients to connect claim all the subscription slots, and later clients — or clients that reconnect after a network partition — find themselves unable to subscribe. Request-reply patterns break because every request creates a temporary inbox subscription. Service mesh patterns break because new service instances can’t register their endpoints.
The problem compounds in multi-tenant deployments where accounts represent different teams or customers. A single account exhausting its subscription allocation doesn’t affect other accounts, but within that account, the impact is total. Every microservice, every queue group, every wildcard listener competes for the same pool of subscription slots.
Intermittent subscription failures are especially hard to debug because they depend on timing — which clients connected first, which ones reconnected after a rolling deployment, and whether request-reply traffic happened to spike at the wrong moment. By the time someone notices, the root cause (too many subscriptions) is buried under symptoms (timeouts, missing responses, stale data).
Request-reply inbox proliferation. Every nc.Request() call creates a unique inbox subscription. High-throughput request-reply patterns can generate thousands of subscriptions per client. If clients don’t use a shared inbox (old-style inbox-per-request), the subscription count grows linearly with concurrent requests.
Microservice fan-out without consolidation. Each service instance subscribes to its own set of subjects. Twenty instances of order-service each subscribing to 50 subjects creates 1,000 subscriptions. Multiply by a dozen services and the account is saturated.
Wildcard subscription sprawl. Developers subscribe to specific subjects like events.orders.created, events.orders.updated, events.orders.deleted instead of a single events.orders.>. Each specific subscription counts separately against the limit.
Stale subscriptions from leaked clients. Long-running processes that create subscriptions without unsubscribing — especially in development or staging environments sharing a production account — accumulate subscriptions over time. Connection pooling libraries that don’t clean up properly are a common culprit.
Dynamic topic generation. Applications that create per-user or per-session subjects (chat.user.{id}, notifications.{session}) generate unbounded subscription counts proportional to the user base.
Limit set too low for the workload. The default or initially configured limit was appropriate for the original deployment but hasn’t been updated as the number of services or traffic patterns grew.
nats server report accountsThis shows each account’s subscription count and configured limits. Look for accounts where the subscription count is near the maximum.
For a specific account:
nats server account info <account_name>nats server report connections --account <account_name> --sort subsThis sorts connections by subscription count, revealing which clients consume the most subscription slots. Clients with hundreds or thousands of subscriptions are the prime targets for consolidation.
nats server report connections --account <account_name> --sort subs --subject "_INBOX.>"A high count of _INBOX.> subscriptions indicates request-reply inbox proliferation. Modern NATS client libraries use a single multiplexed inbox subscription, but older clients or custom implementations may create one subscription per request.
Use the NATS monitoring endpoint to track subscription counts:
curl -s http://localhost:8222/accstatz | jq '.account_statz[] | select(.acc == "<account_name>") | {subs: .num_subscriptions, conns: .conns}'Track this value over time to determine whether subscriptions grow continuously (leak) or spike during certain operations (burst).
1package main2
3import (4 "fmt"5 "log"6
7 "github.com/nats-io/nats.go"8)9
10func main() {11 nc, err := nats.Connect("nats://localhost:4222",12 nats.UserCredentials("account.creds"),13 )14 if err != nil {15 log.Fatal(err)16 }17 defer nc.Close()18
19 stats := nc.Stats()20 fmt.Printf("Connection subscriptions: %d\n", stats.InMsgs)21
22 // List all subscriptions via server request23 resp, err := nc.Request("$SYS.REQ.ACCOUNT.PING.CONNZ",24 []byte(`{"sort":"subs","limit":10}`), nats.DefaultTimeout)25 if err != nil {26 log.Fatal(err)27 }28 fmt.Printf("Top connections by subs: %s\n", resp.Data)29}1import asyncio2import nats3
4async def main():5 nc = await nats.connect("nats://localhost:4222",6 user_credentials="account.creds")7
8 stats = nc.stats9 print(f"In msgs: {stats['in_msgs']}, Out msgs: {stats['out_msgs']}")10
11 # Request top connections by subscription count12 resp = await nc.request("$SYS.REQ.ACCOUNT.PING.CONNZ",13 b'{"sort":"subs","limit":10}')14 print(f"Top connections: {resp.data.decode()}")15
16 await nc.close()17
18asyncio.run(main())Increase the subscription limit. If the current limit is artificially low relative to the workload, raise it. In operator/JWT mode:
nsc edit account <account_name> --subs -1 # unlimited (use cautiously)# or set a higher explicit limitnsc edit account <account_name> --subs 50000nsc push -a <account_name>In server config (non-JWT mode):
1accounts {2 MY_ACCOUNT {3 users = [{user: "svc", password: "..."}]4 max_subscriptions: 500005 }6}Reload the server configuration:
nats server config reload <server-id>Consolidate overlapping subscriptions with wildcards. Replace multiple specific subscriptions with a single wildcard:
1// Instead of this (3 subscriptions):2nc.Subscribe("orders.created", handler)3nc.Subscribe("orders.updated", handler)4nc.Subscribe("orders.deleted", handler)5
6// Do this (1 subscription):7nc.Subscribe("orders.*", handler)This reduces subscription count by orders of magnitude for services that listen on many related subjects.
Ensure clients use multiplexed inbox subscriptions. Modern NATS client libraries (Go >= 1.10, Python nats.py >= 2.0) use a single _INBOX.> subscription multiplexed across all request-reply calls. If your clients use an older library or custom inbox logic, upgrade:
1// Modern Go client automatically multiplexes inboxes2// Just use nc.Request() — no special configuration needed3resp, err := nc.Request("service.endpoint", payload, 2*time.Second)Remove unused subscriptions. Audit connections for subscriptions that no longer serve active traffic. Services that were deprecated but never decommissioned are a common source.
Use queue groups to share subscription slots. Instead of N instances each subscribing independently, use a queue group so the server treats them as a single logical subscription for routing purposes:
nats sub "orders.>" --queue order-processorsAdopt hierarchical subject naming. Design your subject namespace so that services can subscribe to broad wildcards at the appropriate level. A well-designed hierarchy like {domain}.{entity}.{action}.{version} lets consumers subscribe at the granularity they need without creating per-entity subscriptions.
Implement connection pooling with subscription reuse. For applications that create many short-lived subscriptions (e.g., per-request inboxes), use a connection pool that maintains a fixed set of long-lived subscriptions and multiplexes application requests across them.
Monitor and alert on subscription saturation. Set up monitoring to alert well before the limit is reached.
Synadia Insights evaluates this automatically at every collection interval, flagging accounts at 90% utilization before they hit the ceiling.
Set per-user subscription limits. In multi-user accounts, use per-user limits to prevent a single user or service from consuming the entire account allocation:
nsc edit user <user_name> --subs 1000The server rejects the subscription and returns a permissions error to the client. Existing subscriptions on that connection and on other connections within the account continue to work normally. The client receives a -ERR 'maximum subscriptions exceeded' protocol error. Most client libraries surface this as an error on the Subscribe() call, but some swallow it silently — check your error handling.
Each queue group member’s subscription counts individually against the account limit. Ten instances subscribed to orders.> in queue group processors count as 10 subscriptions, not 1. However, queue groups are still beneficial because they reduce the total subscription count when they replace per-instance unique subscriptions.
Yes. When a client unsubscribes or disconnects, the subscription count decreases immediately. The slot is available for new subscriptions on the next SUB command. There is no delay or cooldown period.
In JWT mode, decode the account JWT: nsc describe account <name>. The subs field in the limits section shows the configured maximum. A value of -1 means unlimited. In server config mode, check the max_subscriptions field in the account block. If neither is set, the account has no subscription limit.
Yes. User-level JWT claims can set a subs limit that is more restrictive than the account-level limit. The effective limit for a connection is the minimum of the user limit and the account limit. This lets you give critical services higher subscription budgets while constraining less important consumers.
With 100+ always-on audit Checks from the NATS experts, Insights helps you find and fix problems before they become costly incidents.
No alert rules to write. No dashboards to maintain.
News and content from across the community