Architecture
How Nebula divides a Unity world between workers, transfers entities, and responds when a process stops.
Read this page to understand the parts of a Nebula mesh and the guarantees they currently provide. For class and member details, use the API reference.
Start with containers
Divide your game world into containers. A container is a box-shaped Unity volume with a stable ID and its own local coordinate space. Nebula assigns each container to one worker at a time. That worker has authority over the entities in the container, which means it controls their game state.
Create containers around areas that make sense for your game, such as rooms, buildings, or sections of an outdoor level. Nebula does not calculate container boundaries from the current load.
Nebula supports two kinds of container:
- Create a static container in a scene. The orchestrator assigns it to a worker through the control plane.
- Add a
Containerto the root of a networked entity to create a dynamic container that moves with that entity. Use this for a ship interior, train car, or lift. By default, the worker that controls the carrier also controls its dynamic container. You can pin the dynamic container to a different worker from the dashboard or API.
Nebula parents an entity under its current container and sends its position in container-local coordinates. This lets a moving container carry its contents without changing their local positions.
For a large world, use world partition. Each cell scene becomes a container. Nebula streams cells and uses a floating origin to keep Unity coordinates near (0, 0, 0).
Run the process roles
Run nebula build to build the Unity player and the standalone .NET services, then nebula start to launch the mesh:
- A worker is a headless Unity process. It simulates the entities in its assigned containers at 60 ticks per second.
- The gateway is a standalone .NET service that accepts client connections, routes input to workers, and sends worker state to clients. A mesh can have several; each client's session is identified by a mesh-wide session ID and can move between gateways.
- The orchestrator is a standalone .NET service that starts workers, assigns containers, and serves the Nebula Dashboard and its HTTP API.
- A client sends input to the gateway, predicts its local player, and displays replicated entities.
- The control plane is hosted by the orchestrator. It stores registrations, heartbeats, container assignments, and settings shared by the mesh, and the orchestrator keeps it in its database (SQLite or PostgreSQL) together with saved entities.
Workers and the gateway mirror the control plane from the orchestrator, but they do not send per-tick entity state through it. Workers exchange nearby entity state and authority transfers directly over UDP. See Roles and wire protocol for the connections and messages.
Assign containers to workers
The orchestrator checks worker heartbeats and container assignments every 500 milliseconds. It distributes containers as evenly as it can while keeping existing assignments when possible.
Each assignment has an epoch, which is a number that increases whenever the assigned worker changes. Each entity also has an authority epoch. The gateway and workers reject entity messages with an older epoch. This prevents an earlier owner from overwriting state after a transfer.
To change the worker count, use the dashboard or its HTTP API. When you remove a worker, the orchestrator marks it as retiring and moves its containers to the remaining workers. The worker stops after it has no assigned containers or authoritative entities, or after the drain timeout expires.
Transfer an entity between workers
Nebula calls a change in the worker that controls an entity a handover.
Before an entity crosses a container boundary, the current worker sends a copy to the worker on the other side. Nebula calls this non-authoritative copy a ghost. A ghost receives state from the controlling worker and uses a kinematic rigidbody, so local raycasts and collisions can detect it. Nebula does not run its NetworkTick or allow it to change NetworkVariable values.
When the entity moves farther than HandoverHysteresis into the next container, the current worker sends an AuthorityTransfer message. The message contains:
- the final pose and velocity;
- the new authority epoch;
- all
NetworkVariablevalues; - pending input that the worker has not simulated;
- a keyframe for each sync component;
- state written by
WriteHandoverState; and - ownership flags used for player, bot, and server-driven entity counts.
The receiving worker applies this state to its ghost, calls ReadHandoverState, and gains authority on the next tick. The previous worker keeps a ghost and forwards input that arrives late.
If one worker controls both containers, it changes the entity's parent and container without sending a network message.
To tune this process, set GhostBandMargin, HandoverHysteresis, and GhostLingerSeconds in NebulaConfig. See Containers and handover for the complete sequence.
Handle interactions across a boundary
A worker can interact with a nearby entity through its ghost. For example, it can include the ghost in a raycast.
Use [AuthorityRpc] when the result must run on the worker that controls the target entity. Nebula calls the method directly when the target is local. When the target is a ghost, Nebula sends the call to the controlling worker.
Nebula does not automatically validate a remote damage claim on the target's worker. Add validation to your game when the result depends on untrusted client input. See RPCs and Prediction for the available hooks.
Understand process failures
Design your game around the current failure behavior:
- If a worker stops, the orchestrator reassigns its containers and starts a replacement. Entities that opted into persistence come back from their last checkpoint when a worker gains the lease of their container. Every other entity on the stopped worker is lost. The gateway requests new player entities for affected clients, and holds their joins with
JoinHoldReason.Recoveringuntil the new pawns arrive; the client raisesLocalPlayerLostwithLocalPlayerLossCause.WorkerLost. A worker that stalls without stopping sends nothing until the orchestrator declares it dead afterWorkerTimeoutSeconds; the client raisesServerStalledafterClientStallSecondswithout state. See What the client sees when its worker fails. - If a gateway stops, its clients lose their connection and reconnect by themselves with their session tokens: through a load balancer to another gateway, or to the same address once a gateway is back. Workers keep the affected pawns for
SessionReclaimSeconds(default 30 s); a client that returns within that time continues with its pawn, and a session nobody reclaimed is despawned. The gateway owns no authoritative entity state. Nebula does not provide the load balancer, and the orchestrator does not restart a gateway that exited. - If the control plane becomes unavailable, connected workers continue simulating with their last known assignments. The orchestrator cannot safely change assignments until the control plane returns.
- If the orchestrator stops, existing workers and the gateway continue running. Automatic worker replacement, reassignment, and dashboard controls remain unavailable until you restart it.
Give an entity a PersistentEntity component when it must outlive the worker simulating it. Nebula checkpoints it into a separate persistence database and restores it with its container. See Persistence.
The control plane stores none of this. Keep accounts, matchmaking records, and anything you must query relationally in your own database.
Account for current network limits
Workers, the gateway, and native clients use LiteNetLib over UDP. Clients from a Unity Web build reach the gateway over WebRTC data channels (see Build for the web). Both provide reliable ordered delivery for messages that must arrive and unreliable sequenced delivery for frequently replaced state such as transforms.
The worker-to-worker link and the UDP client link are not encrypted. WebRTC encrypts a web client's link with DTLS. The gateway identifies each player by an anonymous or OpenID Connect token (see Authentication), and gateways and workers authenticate each other only when the mesh has a mesh token. Run worker links on a trusted private network. Put public client traffic behind infrastructure that supplies the security and traffic protection your game requires.
Nebula announces replicated entities to clients allowed to observe their instance. See instancing for private areas and outward views. The gateway reduces the state update rate for entities farther from the client's pawn, but it does not omit their spawn or state entirely. Configure the distance bands in NebulaConfig, then measure bandwidth and client load with your expected entity count.