Skip to main content
Runtimes

Containers

Run any container, such as a Unity or Godot dedicated server or a batch job, as a Rivet Actor without writing RivetKit code.

Rivet can run any container as a Rivet Actor. The rivet-container-runner binary is an adapter you set as your image’s entrypoint: it speaks the control plane’s protocol, launches your server as a child process, and proxies traffic to it. Your code never links against RivetKit. Use it for workloads such as Unity or Godot dedicated game servers and batch jobs like FFmpeg transcoding.

Steps

Prerequisites

  • A containerized server (a Unity or Godot dedicated server, a plain Node process, or any HTTP/WebSocket server)
  • Access to Rivet Cloud or a self-hosted control plane
  • Docker running locally

Install in Your Container

Download the static binary from Rivet’s release artifacts in your Dockerfile and set it as the entrypoint, passing your server’s launch command after --:

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

# Install the Rivet container runner.
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

# Your server binary and assets.
COPY build/ /game/
WORKDIR /game

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

Artifacts are published for x86_64-unknown-linux-musl and aarch64-unknown-linux-musl. The binaries are fully static, so they run in any Linux base image, including scratch. Pin a version by replacing latest with a release version, for example https://releases.rivet.dev/rivet/2.3.3/container-runner/rivet-container-runner-x86_64-unknown-linux-musl.

Deploy

Deploy the image to Rivet Cloud with the CLI. For game servers, configure the pool with one actor per instance and keep running instances alive across version upgrades:

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

Create Actors and Connect Clients

Create actors against the pool’s runner (default) and connect clients through the gateway. WebSocket clients connect at wss://api.rivet.dev/gateway/<actor_id>@<pk_token>/; with the public token in the path, no WebSocket subprotocol is required.

Dedicated Game Server Guides

Engine-specific setup, including which WebSocket transport to use and how to read PORT:

How It Works

  1. The control plane cold-starts your container and calls POST /api/rivet/start on the port it injects as RIVET_PORT.
  2. The runner spawns your server as a child process with PORT set to the child port, waits for the port to open, and reports the actor as running.
  3. Gateway traffic for the actor arrives over Rivet’s tunnel and is proxied to 127.0.0.1:<child port>. WebSocket clients connect at /gateway/<actor_id>@<pk_token>/; the child sees a plain WebSocket handshake at the remaining path. Raw HTTP reaches the child under the /request/* prefix on the actor surface (the prefix is stripped before proxying); other paths are reserved for the runtime’s own endpoints.
  4. Child stdout and stderr are re-emitted with an [actorId=... key=...] prefix so actor logs are attributed in the dashboard.
  5. When an actor stops, the runner sends its child SIGTERM, escalates to SIGKILL after a grace period, and exits the process once no actors remain.

Configuration

All flags can also be set through environment variables:

FlagEnvironment variableDefaultDescription
--portRIVET_PORT / PORT8080Serverless front-door HTTP port. Rivet Cloud injects RIVET_PORT automatically.
--child-portCHILD_PORT7770First local child port; each actor’s child gets the next free port at or above this, exported to the child as PORT.
--actor-nameRIVET_ACTOR_NAMEgameActor name this runner serves.
--runner-versionRIVET_RUNNER_VERSION1Version reported to the control plane, used to drain old workers on deploy.
--base-pathRIVET_SERVERLESS_BASE_PATH/api/rivetBase path the control plane calls for serverless start.
--stop-grace-secsRIVET_STOP_GRACE_SECS25SIGTERM to SIGKILL grace period when stopping the child. Capped to a few seconds when the platform itself is reclaiming the instance, so shutdown fits inside the platform’s own kill window.
--readiness-timeout-secsRIVET_READINESS_TIMEOUT_SECS30How long to wait for the child’s port to open before failing the start.

Per-Actor Input

The actor’s input payload can override the launch spec per actor. All fields are optional and fall back to the entrypoint command. RivetKit clients pass this object directly; when creating actors through the raw control plane API, encode it as CBOR before base64-encoding the input field:

{
	"command": ["./GameServer", "-batchmode"],
	"args": ["-extra-flag"],
	"env": { "MATCH_MODE": "ranked" }
}

command replaces the entrypoint command template, args are appended to it, and env adds environment variables for the child.

Source and Examples

The adapter and a full end-to-end example, including a Unity FishNet demo project and a local test harness, live in the Rivet repository under container-runner/.