Skip to main content
Dedicated Game Servers

Unity

Run a Unity dedicated server as a Rivet Actor with the container runner and connect clients over WebSockets.

Rivet runs a Unity dedicated server build as a Rivet Actor: one actor per match, cold-started on demand and shut down when the match ends. The server ships in a container with the container runner as its entrypoint. Your Unity project does not link against RivetKit.

The container runner proxies HTTP and WebSocket traffic only, so your netcode needs a WebSocket transport. This guide uses FishNet with the Bayou WebSocket transport, which is what the Unity example in the Rivet repository uses.

Steps

Prerequisites

  • Unity 6 with the Linux Dedicated Server Build Support module installed
  • Docker running locally
  • A Rivet Cloud namespace with a secret token (sk_) and a public token (pk_)

Configure the Server

Add FishNet and Bayou to your project, then make the server read its port from the PORT environment variable. The container runner sets PORT for each actor it launches. Add a bootstrap component next to your NetworkManager:

using FishNet.Managing;
using UnityEngine;

public class ServerBootstrap : MonoBehaviour
{
    private void Start()
    {
        var nm = GetComponent<NetworkManager>();
        var portEnv = System.Environment.GetEnvironmentVariable("PORT");
        ushort port = ushort.TryParse(portEnv, out var p) ? p : (ushort)7770;

        nm.TransportManager.Transport.SetPort(port);
        nm.ServerManager.StartConnection();
    }
}

Set Bayou as the active transport on the TransportManager. The server side needs nothing else: the container runner connects to it at ws://127.0.0.1:<PORT>/ with a plain WebSocket handshake.

Configure the Client

Clients reach an actor through the Rivet gateway at a URL that carries the actor ID and your public token in the path:

wss://api.rivet.dev/gateway/<actor_id>@pk_xxxxx/

Stock Bayou always connects to /, so it cannot address a specific actor. The Unity example patches Bayou to accept a client path and WSS. Copy the Packages/com.firstgeargames.fishnet.bayou package from the example into your project, then set the path before connecting:

var uri = new System.Uri(gatewayUrl);
bayou.SetUseWSS(uri.Scheme == "wss");
bayou.SetClientPath(uri.PathAndQuery);
nm.TransportManager.Transport.SetClientAddress(uri.Host);
nm.TransportManager.Transport.SetPort((ushort)(uri.IsDefaultPort ? 443 : uri.Port));
nm.ClientManager.StartConnection();

Build the Server

Build a Linux x86_64 dedicated server (Build Profiles, Linux Server platform) into Builds/ServerLinux/. The output is the GameServer executable, GameServer_Data/, and UnityPlayer.so. Unity builds can also be scripted; see setup-and-build.sh linux in the example.

Write the Dockerfile

Wrap the build with the container runner. -logFile - sends Unity logs to stdout so they appear in the Rivet dashboard.

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && rm -rf /var/lib/apt/lists/*

RUN curl -fsSL https://releases.rivet.dev/rivet/latest/container-runner/rivet-container-runner-x86_64-unknown-linux-musl \
        -o /usr/local/bin/rivet-container-runner \
    && chmod +x /usr/local/bin/rivet-container-runner

COPY Builds/ServerLinux/ /game/
WORKDIR /game
RUN chmod +x /game/GameServer

ENTRYPOINT ["rivet-container-runner", "--", "./GameServer", "-batchmode", "-nographics", "-logFile", "-"]

If the server logs a missing shared object at startup, add that library to the apt-get install line.

Deploy

npx @rivetkit/cli deploy \
	--token "$RIVET_CLOUD_TOKEN" \
	--instance-request-concurrency 1 \
	--drain-on-version-upgrade false \
	--dockerfile Dockerfile

--instance-request-concurrency 1 runs one match per container. --drain-on-version-upgrade false lets running matches finish when you deploy a new version.

Create a Match and Connect

Your backend or matchmaker creates one actor per match with the secret token, then hands the actor ID to players:

curl -X POST "https://api.rivet.dev/actors?namespace=my-namespace" \
	-H "Authorization: Bearer sk_xxxxx" \
	-H "Content-Type: application/json" \
	-d '{"name":"game","key":"match-1234","runner_name_selector":"default","crash_policy":"destroy"}'

Players connect to wss://api.rivet.dev/gateway/<actor_id>@pk_xxxxx/. The first connection cold-starts the container; the gateway holds the connection until the server’s port opens.

Next Steps

  • Containers for runner flags, per-actor launch overrides, and how traffic is proxied
  • Unity example with the patched Bayou transport, build script, and Dockerfile
  • Authentication for public and secret tokens