How to interact with Nakama from headless servers
If you run headless game servers, whether through a service like GameLift or Edgegap or on your own hardware, you may need to communicate with Nakama directly. This guide walks through how to authenticate and call Nakama, both without a player session and in the context of a specific player.
If you’re looking for how Nakama integrates with server orchestration platforms, see the GameLift and Edgegap guides. Here, we cover the reverse direction: how a server instance communicates with Nakama.
What this guide covers:
- Two ways to call Nakama
- Call Nakama without player context
- Call Nakama in a player context
- Common use cases
Two ways to call Nakama #
A headless server can call Nakama in one of two contexts: without a player identity, or in the context of a specific player.
Without player context, with the HTTP key #
The server authenticates with the HTTP key and calls an RPC. Nakama receives the request without a user context.
Use this for tasks such as fetching match configuration, maps, and other game-scoped data. It can also handle player-context work when passing that player’s ID to a custom RPC. We’ll cover this later.
In a player’s context, with a session token #
The server can mint a player session token through an HTTP-key-authenticated RPC. It can then use the session token for a specific player and can call Nakama using the same SDK methods as a game client.
Use this for server-side access to persist player data, such as progression, inventory, and scores.
Let’s look at both approaches in practice. The first example covers server-to-server communication. If you need to access player data, skip ahead to the player context example.
Call Nakama without player context #
Let’s use an example where you want to fetch match configurations from a headless server. You’ll use Nakama’s HTTP key for server-to-server communication, following these steps:
Configure the HTTP key. Set
runtime.http_keyin your Nakama configuration. The default value isdefaulthttpkey, but you should change it for a production deployment.Create an RPC. Register a
get_match_configRPC that returns the configuration the headless server needs to start the match. Add a guard so the RPC only runs for server callers. If a request includes a user ID, reject it.Call the RPC at server boot. The headless server calls the RPC using the HTTP key:
1POST /v2/rpc/get_match_config?http_key=...&unwrapThe
unwrapparameter returns the RPC response as JSON rather than as a JSON-encoded string.
We’ve listed the steps here, for more details on server-to-server RPCs, including a complete RPC implementationsee, see Server-to-Server.
Call Nakama in a player context #
When the server needs to work with a specific player’s data, you have two options:
- Option 1: Mint a session token for that player
- Option 2: Pass the player ID to a custom RPC
Option 1. Mint a player session token #
Here’s how the flow works step by step:
1. Create an RPC to mint the token
Create an RPC, such as mint_player_token, that takes a player ID and returns a session token.
The headless server calls it using the HTTP key:
| |
The RPC uses nk.AuthenticateTokenGenerate() to issue a real Nakama session token for that player:
| |
2. Call the RPC and use the token with the Nakama SDK
The headless server calls the RPC with the HTTP key, passing the player ID. Nakama returns a session token for that player. The headless server can now use the Nakama SDK, in this example the C# SDK, with the player’s session. The SDK methods are the same ones you would use from a Unity client.
| |
A couple of things to note:
- Set the token expiry to cover the expected gameplay session. Your headless server keeps the token for each player and reuses it throughout the play session, such as a match.
- Keep the HTTP key server-side. Make sure the HTTP key is only supplied to the server build.
Option 2. Pass the user ID in the server-to-server RPC #
Another option is to keep the headless server authenticated with the HTTP key and pass the player ID to a custom RPC.
How it works:
- The headless server calls the RPC using the HTTP key.
- The server includes the player ID in the request payload.
- Nakama runs the custom RPC without a player session.
- The RPC passes the player ID to the Nakama runtime function that performs the operation.
- Nakama performs the operation for that player.
This works because Nakama runtime functions that act on a player take that player’s ID as an explicit parameter. The parameter name varies by function:
WalletUpdate(ctx, userID, changeset, ...)AccountUpdateId(ctx, userID, username, ...)NotificationSend(ctx, userID, subject, ...)StorageList(ctx, callerID, userID, collection, ...)LeaderboardRecordWrite(ctx, id, ownerID, ...)
For example, the server can send a score and player ID to an RPC:
| |
The RPC then passes that userId to LeaderboardRecordWrite:
| |
Here’s what the custom RPC could look like:
| |
The tradeoff is that you have to expose each operation through your own RPC. A wallet update, inventory read, XP grant, or friends list would each require its own RPC and payload contract.
For example:
| |
Each RPC adds another piece of server code to maintain.
Common use cases #
Besides the examples above, other scenarios where your headless server may need to make calls to Nakama include:
- Signal instance lifecycle. An instance scaling down calls an RPC with the HTTP key and no player context, telling Nakama to stop routing new players to it, then shuts down after existing matches finish.
- Batch writes for multiple players. For example, when a match ends, the server may send results for all players in one RPC, including kills, XP, currency and so on. Make the write idempotent so the server can safely queue and retry it if Nakama is unavailable.
- Ops tooling. Maintenance scripts use the HTTP key to call the same server-side RPCs as your game servers. For privileged operations, isolate this traffic with a dedicated Nakama node and key.
- Satori flags, live events, and server events. If you’re looking to query Satori from your dedicated server, for example to fetch feature flags or live events, have it query Nakama first. Nakama handles authentication with Satori internally and returns the data to your server. The same applies for sending server-side analytics events: send your events to Nakama, and Nakama publishes them to Satori with
ServerEventsPublish.
