NATS multi-tenancy isn’t about sandboxing processes; it’s about logically partitioning a single NATS server cluster into independent virtual networks that can’t see or interact with each other by default.

Let’s see this in action. Imagine we have a NATS server running, and two clients, client-a and client-b. Without any multi-tenancy configuration, if client-a publishes to updates.service.a and client-b subscribes to updates.service.a, client-b will receive the message.

Now, let’s introduce accounts. We’ll configure NATS to have two accounts: account:a and account:b.

First, we need a NATS server configuration file (e.g., nats-server.conf):

listen: 4222
accounts {
  # Define account 'a'
  account:a {
    users {
      # User 'user-a' in account 'a'
      user:a {
        password: "password-a"
        permissions {
          # User 'user-a' can publish to subjects starting with 'service.a'
          publish {
            allow { "service.a.>" }
          }
          # User 'user-a' can subscribe to subjects starting with 'service.a'
          subscribe {
            allow { "service.a.>" }
          }
        }
      }
    }
  }
  # Define account 'b'
  account:b {
    users {
      # User 'user-b' in account 'b'
      user:b {
        password: "password-b"
        permissions {
          # User 'user-b' can publish to subjects starting with 'service.b'
          publish {
            allow { "service.b.>" }
          }
          # User 'user-b' can subscribe to subjects starting with 'service.b'
          allow { "service.b.>" }
        }
      }
    }
  }
}

We start the NATS server with this configuration: nats-server -c nats-server.conf.

Now, client-a connects to the NATS server using credentials for user:a in account:a. The connection string would look something like this, often passed via an environment variable or directly in client code:

nats://user:a:password-a@localhost:4222

And client-b connects with credentials for user:b in account:b:

nats://user:b:password-b@localhost:4222

If client-a publishes a message to service.a.heartbeat, the message will be sent. If client-b attempts to subscribe to service.a.heartbeat, the subscription will fail because user:b in account:b does not have permission to subscribe to subjects outside its account. Similarly, if client-b publishes to service.b.status, client-a will not receive it.

The NATS server is a single process, but the accounts configuration logically divides the subject space. Each account has its own isolated set of subjects. Users defined within an account are scoped to that account and can only interact with subjects and other users within their own account, based on the permissions defined. This is the core of NATS multi-tenancy: logical separation enforced by the server based on user credentials and account definitions.

The problem this solves is isolation. In a shared NATS cluster, without accounts, any client could publish to any subject, potentially overwhelming or interfering with other services. Accounts prevent this by creating distinct "namespaces" for subjects. A publisher in account:a sending to service.a.foo will never reach a subscriber in account:b unless explicit bridging is configured.

Internally, when a client connects, the NATS server authenticates the user and associates them with a specific account. All subsequent subject operations (publish, subscribe, request) are then checked against the permissions defined for that user within their assigned account. The server maintains internal mappings of which subjects belong to which accounts and enforces these boundaries at the connection and message routing layers. The allow and deny clauses within the permissions block are the exact levers you control to define what a user can do and where.

The most surprising thing about NATS accounts is that they don’t inherently prevent a user in one account from seeing the subjects published by another account if they are using the same NATS server. Instead, they enforce access control based on the defined permissions. A user in account:a can’t subscribe to account:b’s subjects, and a user in account:b can’t subscribe to account:a’s subjects, because their permissions are restricted. This is a subtle but crucial distinction: it’s not about invisibility, but about entitlement.

The next concept you’ll likely encounter is how to enable communication between accounts, which involves setting up account import/export configurations.

Want structured learning?

Take the full Nats course →