Nebula

Running locally

Build the player, run the whole mesh on one machine, iterate in the Editor without a build, watch the mesh on the dashboard, and stop it.

Run the commands on this page from a Unity project where you have already run nebula init. You can also run the same operations from the Nebula menu in the Unity Editor.

Build

nebula build
nebula build --linux
nebula build --web

To build the game for your current platform, run nebula build. The command reads the required Unity version from ProjectSettings/ProjectVersion.txt. It finds that version through Unity Hub, NEBULA_UNITY, or the path saved by nebula config unity.

On Windows, the default output is Builds/Win64/Nebula.exe. Set executable in nebula.json to change the base name. To build a Linux dedicated server, run nebula build --linux. This command writes the server to Builds/Linux64 and creates Builds/nebula-linux.tar.gz for deployment. To build a client that runs in a browser, run nebula build --web. It writes Builds/Web, which the local gateway serves. See Build for the web.

You can keep the project open in the Unity Editor while you build. The CLI copies the project to ~/.nebula-cli/scratch/<project>, builds the copy, and writes the result to your project's Builds/ folder.

The CLI prints compiler errors and lines tagged [nebula]. To read the complete build log, open Builds/unity-build.log. If a running mesh has locked the previous build, run nebula build --stop-mesh to stop it before building.

After a wire-protocol change, rebuild and restart the mesh as well as any standalone or web clients. Compiling scripts in the Editor does not update running services. A gateway warning such as client protocol 12 != 11; disconnecting means the client and gateway versions differ. See Rebuild after a protocol change.

Start

nebula start --open-ui
nebula start --workers 2 --bots 2

When you run nebula start, the CLI:

  1. Launches the orchestrator from the last build with its database at Library/Nebula/nebula.db (mesh.database in nebula.json changes it). The orchestrator hosts the control plane on the dashboard port, launches the gateway and the number of workers set by --workers, and assigns each container to a worker.
  2. Launches --bots bot clients.
  3. Waits for the dashboard and prints how to join.

Saved entities stay in the database across restarts; add --reset-persistence to start from an empty world.

Set local defaults in the mesh section of nebula.json. To build before starting, add --build.

Joining

To join from the Unity Editor, enter Play mode. NebulaBootstrap.EditorRole uses Client by default. See Choosing the Editor's role to run a worker in the Editor instead.

To join from a standalone build on Windows, run:

Builds\Win64\Nebula.exe -nebula-role client -nebula-name Player

If your scene includes NebulaTitleScreen, enter the gateway address and a display name on the title screen, then select Connect. Use 127.0.0.1:7000 for a local mesh. Press Escape during the game to disconnect or choose another gateway. Nebula > Set Up Active Scene adds the title screen for you.

To join from a browser, run nebula build --web, then open http://127.0.0.1:7000/.

Without NebulaTitleScreen, a client started without connection arguments stays idle until your code calls NebulaClient.ConnectTo. See Connecting clients.

To connect without any UI, pass -nebula-gateway <address:port> or -nebula-connect. Bot clients also connect at startup. The title screen stays hidden for these clients.

Test with bots and server-driven entities

Use bots to test the complete client path. Use server-driven entities to test worker simulation without starting more client processes.

BotServer-driven entity
Start it withnebula start --bots NNebulaWorker.SpawnServerDriven in your game code
What it isA headless client that connects through the gateway, predicts, and corrects its state like a playerA networked entity with no owning client, simulated by its authoritative worker
Runtime costOne Unity player processOne simulated entity
Use it forTesting the complete client connection, prediction, and reconciliation pathTesting server-side entity load

Your game must provide bot input and server-driven behavior. Nebula does not provide an NPC implementation. See Spawning for the server-driven API.

Choosing the Editor's role

NebulaBootstrap.EditorRole sets the roles the Editor starts when you press Play. The Editor has no command line, so this field takes the place of -nebula-role. On its own, it does not let you skip nebula build and nebula start:

  • A process is a client or a worker, never both. If EditorRole includes both, Nebula drops the worker role and logs an error. Whichever half the Editor does not run must come from a build, or from a second Editor process. See Iterate without building.
  • The orchestrator launches processes from a build. It starts the gateway and every worker as a separate player process. In the Editor there is no player executable to reuse, so an Editor orchestrator logs executable not found unless NebulaConfig.WorkerExecutable points at a build.
  • Workers and the gateway need an orchestrator. They mirror the control plane from the orchestrator at NebulaConfig.ControlPlaneUrl (default http://127.0.0.1:7080/). nebula start is what starts it.
EditorRoleStart firstUse it for
Client (default)nebula build, then nebula startPlaying and debugging client code: input, prediction, UI.
Workernebula build, then nebula start --workers 0Debugging worker code (the game mode, Simulate on the authority, RPC handlers) with breakpoints. Join with a client build.
Worker, Gateway, Orchestrator with UseLocalControlPlanenebula build for the client onlyA one-worker server in a single process, without a standalone orchestrator.

The Editor as a worker

nebula build
nebula start --workers 0

With EditorRole set to Worker, press Play. The Editor worker uses the defaults for a worker without command-line switches: ID w1, index 1, port WorkerBasePort + 1 (7101). If the environment the Editor started from sets PORT, the Editor worker listens on that port instead. Starting the mesh with --workers 0 keeps a launched worker from already using that ID and port. The orchestrator sees the Editor worker register and gives it every container.

Join with Nebula > Mesh > Launch Extra Client Window or Builds\Win64\Nebula.exe -nebula-role client -nebula-name Player. To test handovers, select Add worker on the dashboard: the orchestrator launches w2 from the build, and the containers are split between the two.

When you exit Play mode, the Editor worker stops sending heartbeats. After WorkerTimeoutSeconds the orchestrator treats it as dead and releases its containers to any remaining worker. It does not relaunch the Editor worker; press Play again.

Everything server-side in the Editor

Set EditorRole to Worker, Gateway and Orchestrator, and enable UseLocalControlPlane on NebulaConfig. The three roles share an in-process LocalControlPlane, so nothing else needs to run. Because nothing outside the process can see that control plane, the orchestrator does not launch the gateway or workers. The in-process worker gets every container and the in-process gateway listens on GatewayPort. Join from a client build as above.

This mode has limits:

  • There is exactly one worker, so there are no handovers, ghosts or cross-worker RPCs to test.
  • Persistence in auto mode writes to a local file under Application.persistentDataPath.
  • UseLocalControlPlane is saved in the config asset that builds also load. Turn it off again before running nebula build for nebula start or a deployment. Otherwise, the built orchestrator also uses a local control plane and never launches the gateway or workers.

Iterate without building: Multiplayer Play Mode

To play your game in the Editor without running nebula build and nebula start, use Unity's Multiplayer Play Mode. It runs a virtual player: a second Editor process that shares your project. With NebulaConfig.EditorRunMode set to MultiplayerPlayMode, the virtual player hosts the server and the main Editor is a client. Each process still has one side: the virtual player is the worker, and the main Editor is the client.

Before you start

  • Use Unity 6.6 (6000.6) or later. Earlier Unity 6 versions can work if the Multiplayer Play Mode package provides its player API. If Nebula cannot read it, it runs as Mesh and logs a warning.
  • Add the com.unity.multiplayer.playmode package with the Package Manager.
  • Give the server a virtual player. On Unity 6.6 and later, set up a Play Mode Scenario so that Play starts it for you. Otherwise, open Window > Multiplayer > Multiplayer Play Mode and enable one virtual player before each session. To add more players, see Add more players.

Start the server with a Play Mode Scenario

A Play Mode Scenario is an asset that lists the Editor instances Unity starts when you press Play. With a scenario that starts one extra Editor instance tagged Server, Play launches the server's virtual player itself, so nobody has to enable one in the Multiplayer Play Mode window first. Scenarios are part of the Editor from Unity 6.6.

To create the scenario, choose Nebula > Dev Loop > Create Scenario. It creates Assets/Settings/PlayMode/Nebula Dev Loop.asset with two instances:

  • The main Editor, with no tag, so it is the client.
  • Player 2, an extra Editor instance tagged Server, with Stream Logs to Main Editor on, so the server's log also appears in the main Editor's Console.

To test with more players, add more Editor instances to the scenario and leave them untagged. They join as clients. Keep the Server instance in the lowest player slot (Player 2): an untagged instance in a lower slot would host the server instead.

It also turns on Keep Active for Player 2, so the server's Editor stays open between Plays and the next Play only has to enter Play mode. Keep Active is a per-user setting kept in UserSettings, so it applies only to you. Commit the scenario asset so that everyone on the project gets it.

To create the scenario by hand instead:

  1. Open Window > Play Mode > Scenarios and create a scenario.
  2. Leave the main Editor untagged.
  3. Add an Editor instance and give it the tag Server. If the tag list doesn't have Server, create it.
  4. Turn on Keep Active and Stream Logs to Main Editor for that instance.
  5. Commit the scenario asset.

When EditorRunMode is MultiplayerPlayMode, the active scenario is Default, and no virtual player is running, Nebula makes that scenario the active one. It checks when the Editor loads, after each Play, and when NebulaConfig or a scenario asset changes. If several scenarios have an instance tagged Server, Nebula picks the first by asset path and logs a warning. A scenario you choose in the dropdown next to the Play button stays active. Nebula doesn't switch while a virtual player from the Multiplayer Play Mode window is running, because making a scenario active stops those players.

To start the server's Editor instance before you press Play, choose Nebula > Dev Loop > Start Server Player. The server opens in its own Editor window. The first launch imports the project and can take a minute or two. Press Play once it's open. A script or an automated tool can run this menu item to prepare a session, so that a person only has to press Play.

With a scenario active, enter Play with the Play button or from a script with PlayModeScenarioManager.Start(). If a script sets EditorApplication.isPlaying instead, Unity switches back to the Default scenario for that Play and starts no extra Editor instances. Nebula makes the scenario active again after Play stops.

Turn it on

  1. Select NebulaConfig (Nebula > Select Config).
  2. Set Editor Run Mode to MultiplayerPlayMode.
  3. Press Play.

The virtual player runs the orchestrator, the gateway and one worker in one process. The control plane stays in memory, and autoscaling is off. The main Editor waits until that server is listening, then connects to it. The title screen stays hidden. Each process logs the side it took, for example:

[nebula] editor run mode: Multiplayer Play Mode (this virtual player hosts the server)

The setting affects only the Editor. Builds ignore it. The settings it implies are applied to the Editor's copy of the config, never to the asset. So you do not have to change anything back before nebula build. A command-line switch such as -nebula-role or -nebula-workers still wins.

Add more players

To add a player, enable another virtual player in the Multiplayer Play Mode window (Player 3, Player 4, and so on). Exactly one virtual player hosts the server, and every other one joins it as a client:

  • With no tags, the main Editor is a client, the enabled virtual player with the lowest number hosts the server, and every other enabled virtual player is a client. If you disable Player 2 and enable Player 3 and Player 4, Player 3 hosts.
  • Tags override this. A virtual player tagged Client never hosts. When any enabled player is tagged Server, untagged virtual players are clients.
  • Run only one server. If several enabled players are tagged Server, only the one with the lowest number hosts. Each of the others logs an error and joins as a client instead of starting a second server on the same ports and save file. Remove the extra Server tags.

Each client has its own identity, so two clients are two players. The main Editor keeps the identity it has always used. A virtual player's client saves its own identity, keyed by the virtual player, and its default player name ends with that name, for example jesse (mppm98673e00). A -nebula-name switch still sets the name.

Nebula reads which players are enabled from Library/VP/SystemData.json. If it can't read that file, or the virtual player isn't listed in it, Nebula logs a warning and an untagged virtual player hosts the server. In that case, tag one virtual player Server and the others Client.

On Unity 6000.6, a virtual player can't render URP, so a client's Game view may be black. The client still connects, spawns and moves, and it receives input when you focus its window. See The server renders nothing.

Saved entities

NebulaConfig.EditorPersistence chooses what the server does with persistent entities:

ValueBehavior
DevSaveFile (default)Keeps them in Library/Nebula/DevSaves/world.bin from one Play to the next. The file is separate from the database that nebula start uses.
InMemoryStarts every Play from an empty world.

To start from an empty world once, stop Play and choose Nebula > Dev Loop > Reset Dev Saves. This deletes the dev save file.

Ports

Both sides add NebulaConfig.EditorPortOffset (default 50) to GatewayPort, WorkerBasePort and DashboardPort. With the default ports, the gateway listens on 7050, the worker on 7151, and the dashboard on http://localhost:7130/. So a mesh started with nebula start can run at the same time. Set the offset to 0 to use the configured ports.

To run the dev loop in two copies of one project at once, such as two git worktrees, give each copy its own offset. Put a whole number in UserSettings/NebulaEditorPortOffset.txt at the copy's root, for example 1050 in one and 2050 in the other. The file replaces EditorPortOffset for that copy only. UserSettings isn't committed, so the shared NebulaConfig stays unchanged. Choose offsets whose ports don't overlap: with the default ports the gateway and worker ports are only 100 apart, so an offset 100 above another copy's puts its gateway on that copy's worker port. Offsets 1000 apart are safe. If your game runs its own local services next to the dev loop, EditorDevPaths.PortOffset returns the offset in use so you can move their ports by the same amount.

A client in this mode connects only to the server its own Play session started. The server records its port, or the reason it could not start, in Library/Nebula/dev-session.json. If another process holds the server's port, the client connects to nothing and logs the server's error, for example:

The Editor-hosted server could not start (could not bind udp/7050: another process is using it, ...). A client of this session does not join any other server. Stop the other mesh (nebula stop) or change NebulaConfig.EditorPortOffset, then press Play again.

If no server starts within 20 seconds, the client logs how to start one, then keeps waiting.

Environment variables

The server reads your game's environment variables from .env.nebula at the project root, the same file nebula start uses. Nebula loads it each time you press Play. See Pass settings and secrets to workers.

What it does not test

The server runs exactly one worker, so this mode cannot test anything that needs two:

  • Container boundaries between workers
  • Handovers
  • Ghosts and cross-worker RPCs
  • Recovery from a failed worker

To test these, run nebula build and nebula start with more than one worker, or add a worker from the dashboard.

The server renders nothing

The server has nothing to show, so it renders nothing. It turns off every camera, and when a virtual player hosts it, Nebula also closes that player's Game view while it plays. A Nebula Server tab takes the Game view's place. Read the server's log in the virtual player's Console.

This also works around a known problem. On Unity 6000.6, a virtual player cannot build the Universal Render Pipeline (URP). A Game view builds the render pipeline every time it draws, even with every camera off, so an open Game view in the server's virtual player logs shader errors every frame. Its log can grow by gigabytes in one session. For the same reason, a virtual player that joins as a client in a URP project may not render. It still connects and plays; only its Game view is affected.

Nebula does not change the render pipeline settings to avoid this. A virtual player shares the main Editor's ProjectSettings folder, so a change there would reach the main Editor. When a virtual player that hosted the server later plays as a client, its Game view comes back and the Nebula Server tab closes.

Troubleshoot the dev loop

The client logs that no Editor-hosted server has started. No virtual player hosted the server during that Play. Check the scenario dropdown next to the Play button: the active scenario needs an Editor instance tagged Server. If it shows Default, see Start the server with a Play Mode Scenario, or enable a virtual player in Window > Multiplayer > Multiplayer Play Mode. If a virtual player did start, read its Console for the server's error.

An Editor script fails only in the server's virtual player. A virtual player runs from a copy of your project in Library/VP/<id>. The copy has no Packages/manifest.json of its own, doesn't run the Package Manager, and can't write to the asset database. A third-party [InitializeOnLoad] script that installs packages or writes assets can fail there after every domain reload. For example, a package's setup helper might offer to install a package each time and fail. Make such a script return early outside the main Editor:

using UnityEditor;

[InitializeOnLoad]
static class PackageSetupCheck
{
    static PackageSetupCheck()
    {
        if (!IsMainEditor()) return;
        // The script's own work.
    }

    static bool IsMainEditor()
    {
        try
        {
            return Unity.Multiplayer.PlayMode.CurrentPlayer.IsMainEditor;
        }
        catch (System.Exception)
        {
            // Multiplayer Play Mode isn't available, so this is the only Editor.
            return true;
        }
    }
}

CurrentPlayer is part of the Editor on Unity 6.6 and later. On earlier versions, it comes from the Multiplayer Play Mode package.

Test with simulated latency

A local mesh and the Editor loop run at about 0 ms round-trip time (RTT), so prediction and lag compensation never face a real connection. To test them on one machine, make a client behave as if its link to the gateway were slower. The simulation works in the Editor and in development builds. A release build ignores it and logs a warning if a setting is present.

To start a client with 100 ms of added round trip, run:

Nebula.exe -nebula-role client -nebula-sim-latency 100

These options apply to one client's link to the gateway:

OptionNebulaConfig fieldEffect
-nebula-sim-latency <ms>SimulatedLatencyMsAdds this much to the round trip. Half delays what the client sends and half delays what it receives, so a value of 100 raises NebulaClient.RttMs by about 100.
-nebula-sim-jitter <ms>SimulatedJitterMsVaries each packet's delay by up to half this value in either direction.
-nebula-sim-loss <percent>SimulatedLossPercentLoses this percentage of packets in each direction, from 0 to 100.

A command-line option overrides the NebulaConfig field. In the Editor, set the fields on your NebulaConfig asset under Simulated network (development only). The client logs the setting once when it starts.

The server and the other clients are not affected. The simulation delays packets in your game's code, so it adds to the link's real latency rather than replacing it. Held packets are released once per tick, so expect the measured RTT to be within one tick (about 17 ms) of the value you set.

Jitter can reorder packets. A sequenced packet, such as a position update, that a newer one overtakes is dropped, as it would be on a real link. Reliable packets stay in order. A lost reliable packet is not dropped for good: it arrives one round trip late, as a retransmission would, and delays the reliable packets behind it.

Change the simulation while playing

To change the simulation from your game's console, call NebulaClient.SetNetworkSimulation:

// 150 ms round trip, 20 ms jitter, 1% loss.
client.SetNetworkSimulation(150f, 20f, 1f);

// Turn it off.
client.SetNetworkSimulation(0f, 0f, 0f);

The call returns false and changes nothing in a release build and in a web client. Read the current values from NebulaClient.NetworkSimulation. While a simulation is on, the title screen and NebulaDebugOverlay show it next to the client's RTT.

Watch

nebula status
nebula logs w1 --follow
nebula logs gateway -n 200

Run nebula status to list workers, their assigned containers, entity counts, and recent orchestrator events.

Run nebula logs <role> to read a log. Use orchestrator, gateway, w1 through wN, or bot1 and later bot numbers as the role. The CLI stores logs in the build's Logs/ folder.

To inspect a local mesh in a browser, open http://localhost:7080/. The dashboard shows the gateway address, workers, entity counts, tick times, heartbeat ages, container assignments, settings, and recent events.

Use the dashboard controls to add a worker, drain and remove a worker, stop a worker to test recovery, or rebalance containers. You can also perform these operations through the dashboard HTTP API.

If your scene includes NebulaDebugOverlay, press F3 to show or hide it. The overlay displays the tick, round-trip time (RTT), input lead, current container, current worker, observed handovers, and container assignments.

Stop

nebula stop

Run nebula stop to stop every process from the build: the orchestrator, the gateway, the workers, and any bots. The database file stays where it is.

From the Editor

Use the Nebula menu in the Unity Editor to run the same development tasks:

  • Use Mesh to start or stop a local mesh from the latest build, open the dashboard, or launch another client.
  • Use Build to create a Windows player or Linux dedicated server.

On this page