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 --webTo 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 2When you run nebula start, the CLI:
- Launches the orchestrator from the last build with its database at
Library/Nebula/nebula.db(mesh.databaseinnebula.jsonchanges 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. - Launches
--botsbot clients. - 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 PlayerIf 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.
| Bot | Server-driven entity | |
|---|---|---|
| Start it with | nebula start --bots N | NebulaWorker.SpawnServerDriven in your game code |
| What it is | A headless client that connects through the gateway, predicts, and corrects its state like a player | A networked entity with no owning client, simulated by its authoritative worker |
| Runtime cost | One Unity player process | One simulated entity |
| Use it for | Testing the complete client connection, prediction, and reconciliation path | Testing 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
EditorRoleincludes 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 foundunlessNebulaConfig.WorkerExecutablepoints at a build. - Workers and the gateway need an orchestrator. They mirror the control plane from the orchestrator at
NebulaConfig.ControlPlaneUrl(defaulthttp://127.0.0.1:7080/).nebula startis what starts it.
EditorRole | Start first | Use it for |
|---|---|---|
Client (default) | nebula build, then nebula start | Playing and debugging client code: input, prediction, UI. |
Worker | nebula build, then nebula start --workers 0 | Debugging worker code (the game mode, Simulate on the authority, RPC handlers) with breakpoints. Join with a client build. |
Worker, Gateway, Orchestrator with UseLocalControlPlane | nebula build for the client only | A one-worker server in a single process, without a standalone orchestrator. |
The Editor as a worker
nebula build
nebula start --workers 0With 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
automode writes to a local file underApplication.persistentDataPath. UseLocalControlPlaneis saved in the config asset that builds also load. Turn it off again before runningnebula buildfornebula startor 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
Meshand logs a warning. - Add the
com.unity.multiplayer.playmodepackage 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:
- Open Window > Play Mode > Scenarios and create a scenario.
- Leave the main Editor untagged.
- Add an Editor instance and give it the tag
Server. If the tag list doesn't haveServer, create it. - Turn on Keep Active and Stream Logs to Main Editor for that instance.
- 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
- Select
NebulaConfig(Nebula > Select Config). - Set Editor Run Mode to
MultiplayerPlayMode. - 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
Clientnever hosts. When any enabled player is taggedServer, 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 extraServertags.
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:
| Value | Behavior |
|---|---|
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. |
InMemory | Starts 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 100These options apply to one client's link to the gateway:
| Option | NebulaConfig field | Effect |
|---|---|---|
-nebula-sim-latency <ms> | SimulatedLatencyMs | Adds 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> | SimulatedJitterMs | Varies each packet's delay by up to half this value in either direction. |
-nebula-sim-loss <percent> | SimulatedLossPercent | Loses 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 200Run 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 stopRun 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.