tools/purrlobby/nakama.md
Nakama by Heroic Labs is an open-source game server offering authentication, lobbies, matchmaking, chat, leaderboards, storage and more. It runs anywhere: your laptop, your own infrastructure, or Heroic Cloud.
It is also the most capable backend PurrLobby ships with, and the one to reach for when you want your lobby layer and the rest of your live game to share a single server.
Heroic Labs is a PurrNet sponsor. If you are choosing a backend, Nakama is well worth a look: the server is Apache 2.0 licensed and free to self-host, so you can build and ship on your own infrastructure with no vendor lock-in, and move to their managed Heroic Cloud later if you would rather not run it yourself.
Requires Nakama Unity.
Testing locally
Nakama runs locally in Docker, which is the fastest way to develop against it.
- Install Docker Desktop.
- Make a folder for the server and drop in a
docker-compose.yml. Heroic Labs publishes ready-made PostgreSQL and CockroachDB compose files in their Docker install guide. - From that folder, start it:
docker compose upThe server comes up on 127.0.0.1:
| Port | Purpose |
|---|---|
| 7350 | Client API, which is what PurrLobby connects to |
| 7351 | Developer console |
| 7349 | gRPC |
Open http://127.0.0.1:7351 for the console and sign in with admin / password. The console is useful while building a lobby flow: you can watch matches appear and disappear, inspect accounts created by device login, and read server logs as players join.
Connecting PurrLobby to it
Preset assets ship in Assets/PurrLobby/Providers/Nakama/Preset, including a pre-filled orchestrator. Drag Orchestrator.Nakama onto your LobbyManager and you are done: the shipped Nakama Config already points at this local server, so a fresh Docker instance needs no changes on the Unity side.
Press play and the menu will authenticate against your container. Watch the console at 127.0.0.1:7351 to see the account appear.
The local defaults are development values. Change the server key before exposing an instance to anything beyond your own machine, and use HTTPS in production. See Is the server key safe to ship? below.
Pointing at a server
The endpoint lives on a NakamaConfig asset (Create → PurrLobby → Nakama → Config), which NakamaSessionProvider references:
| Field | Default | Notes |
|---|---|---|
Scheme |
Http |
Switch to Https for any remote server |
Host |
127.0.0.1 |
Hostname or IP |
Port |
7350 |
Client API port. Usually 443 over HTTPS |
Server Key |
defaultkey |
Must match your server's configured key |
For Heroic Cloud or your own hosted instance, set Scheme to Https, Host to the address from your Heroic Labs dashboard, Port to 443, and Server Key to the key configured on that instance.
Keeping separate config assets for local and hosted, and swapping which one the session provider references, is an easy way to move between them without editing fields.
Is the server key safe to ship?
Yes. The server key is designed to live in your client. Every Nakama client SDK takes it as a constructor argument, so it ends up in your build where a determined player can read it. Its privileges are deliberately narrow: it gates only the authentication and session-refresh endpoints, and every other API call requires a session token. Someone holding just the key can create an account on your server, not act as an existing player.
Two rules still apply:
- Change it from
defaultkeybefore you go live, and useHttpsso it is not readable in transit. Heroic Labs recommends regeneratingserver_key,session.encryption_keyandruntime.http_keyserver-side, all to different random values. - Never ship Nakama's
http_keyor console credentials. The runtimehttp_keybypasses session authentication and would expose every server RPC.NakamaConfighas no field for either, so it cannot carry them into a build by accident.
Real protection comes from server-side logic, not the key. If you add runtime RPCs, guard each one by checking for a user id in the context, since Nakama has no middleware-style policy layer.
What PurrLobby uses
| Role | Asset | Backed by |
|---|---|---|
| Session | NakamaSessionProvider |
Nakama device authentication |
| Lobby | NakamaLobbyProvider |
Nakama relayed matches |
| Matchmaking | NakamaMatchmakingProvider |
Nakama's ticket matchmaker |
| Game allocation | NakamaGameAllocator |
Nakama relayed match as the transport |
Settings
| Asset | Settings |
|---|---|
NakamaConfig |
Scheme, Host, Port, Server Key |
NakamaSessionProvider |
Config, Session PlayerPref Key |
NakamaLobbyProvider |
Session Provider, Max Players, Snapshot Timeout Ms (4000), Query Limit (100) |
NakamaMatchmakingProvider |
Min Count (2), Max Count (4) |
NakamaGameAllocator |
Game Scene, Wait For Game Start Flag (off) |
Session PlayerPref Key is where the device session token is cached so returning players skip the login step. Snapshot Timeout Ms is how long a joining player waits for the lobby owner's first state snapshot before failing the join. Query Limit caps how many matches the browser lists. Wait For Game Start Flag makes the host wait on a lobby metadata flag before connecting; leave it off unless your flow needs it, since most pre-load the scene and connect immediately.
Choosing a game allocator
NakamaGameAllocator is not meant for fast-paced games. It routes gameplay through Nakama's relayed match socket, which is a WebSocket carrying a custom message protocol. That is a good fit for turn-based games, card and board games, and anything else where a little latency does not hurt. It is the wrong tool for shooters, fighting games, racing, or anything else that needs tight networking.
This is not a problem, because the orchestrator's four slots are independent. Nakama is excellent at session, lobby and matchmaking, so keep it for those and swap only the game allocator for a lower-latency path:
Instead of NakamaGameAllocator |
Gameplay runs over |
|---|---|
PurrTransportGameAllocator |
PurrTransport relay |
SteamGameAllocator |
Steam relay sockets |
| Your own allocator | Whatever transport you configure, for example direct UDP |
A Nakama session, Nakama lobbies, the Nakama matchmaker, and PurrTransport for the actual match is a perfectly normal setup. Nothing in the menu flow changes; only the transport the game scene connects with does.
See Custom providers if none of the shipped allocators fit.
Capabilities, and how to lift them
The Nakama provider advertises CreateLobby, JoinLobbyById, JoinLobbyByCode and QueryLobbies.
These limits are ours, not Nakama's. Nakama is fully capable of rich lobby listings, private lobbies and random join. Doing so needs a custom server module written against its Go, Lua or TypeScript runtime, and this provider deliberately targets a stock Nakama server so it works with no server-side setup and no extra dependencies.
The trade-off: relayed matches expose no name, metadata or max size to the browser, so entries show the match id, every match is publicly listed, and query filters are ignored.
To lift any of it, write a server RPC that returns the listing data you want and override QueryLobbies in a subclass to call it. Nakama's server framework docs cover writing runtime modules. Matchmaking is unaffected either way and uses Nakama's real ticket-based matchmaker, so quick-play works fully on a stock server.