Lesson 9: Server Build and Headless Test - Batchmode Smoke and Logging
If your multiplayer slice only runs when someone hosts from the editor, you do not yet have a reliable server pipeline.
This lesson helps you move from ad-hoc local host tests to a repeatable headless server smoke test loop with actionable logging.
Lesson objective
By the end of this lesson, you will have:
- A server build target that runs without graphics
- A headless startup command you can repeat in scripts
- A smoke test checklist for connection, scene load, and teardown
- Log signals that clearly indicate pass or fail
Why this matters
Lesson 8 made player session entry reliable. Now you need confidence that backend session runtime is stable in non-editor conditions.
Headless testing gives you:
- early detection of startup regressions
- cleaner reproduction for networking bugs
- better preparation for dedicated-host deployment decisions
Step-by-step implementation pass
Step 1 - Define server bootstrap assumptions
Write these down before building:
- startup scene for server process
- network transport config
- expected port and bind behavior
- minimum startup success log line
If these assumptions are implicit, automation breaks quickly.
Step 2 - Create a batchmode server build command
Use Unity batchmode to produce a deterministic server build artifact.
Typical command structure includes:
-batchmode-quit-projectPath- your build method entry point
- output path argument
Store this command in a script file, not only in team memory.
Step 3 - Launch a headless runtime smoke instance
Run server process with:
- no graphics window
- explicit port
- log output path
Then connect one or two lightweight clients and validate:
- server boot success
- client handshake
- lobby/session transition
- clean disconnect and process shutdown
Step 4 - Add pass/fail log markers
Pick a small set of required log markers:
SERVER_BOOT_OKCLIENT_CONNECTEDMATCH_SCENE_LOADEDSERVER_SHUTDOWN_OK
If one marker is missing, fail the smoke test.
This is much faster than manually reviewing full logs every run.
Step 5 - Capture one known-good baseline run
Save:
- build command used
- runtime launch arguments
- sample successful log excerpt
That baseline becomes your comparison when regressions appear.
Example pattern - lightweight server startup args
using UnityEngine;
public class ServerBootstrap : MonoBehaviour
{
[SerializeField] private ushort defaultPort = 7777;
private void Start()
{
var portArg = GetArgValue("-port");
var port = ushort.TryParse(portArg, out var parsed) ? parsed : defaultPort;
Debug.Log($"SERVER_BOOT_OK port={port}");
// Initialize transport and host start here.
}
private static string GetArgValue(string key)
{
var args = System.Environment.GetCommandLineArgs();
for (int i = 0; i < args.Length - 1; i++)
{
if (args[i] == key) return args[i + 1];
}
return string.Empty;
}
}
Keep bootstrap output consistent so scripts can parse it reliably.
Mini challenge
Create server-smoke-checklist.md with:
- build command
- run command
- four required pass markers
- one known failure signature to watch
Run the checklist twice in a row. If both passes are identical, your flow is stable enough for team-wide use.
Pro tips
- Use separate log files per run with timestamped names.
- Keep server startup scene minimal to reduce noise during diagnostics.
- Add one quick retry for flaky local network setup, but never hide hard failures.
Common mistakes
- Treating editor-host play mode as equivalent to headless runtime
- Logging too much without clear pass/fail markers
- Shipping server build scripts that depend on local machine state
Troubleshooting
"Server process starts then exits immediately."
Check missing scene references, invalid transport config, or unresolved command-line assumptions.
"Clients connect locally but fail in scripted smoke test."
Validate startup timing and ensure server readiness marker appears before client connect attempts.
"Logs are too noisy to diagnose failures."
Add concise structured markers for critical lifecycle events, then filter around those markers first.
FAQ
Do I need dedicated servers before this lesson is useful?
No. Headless validation helps even when you are still using host-authoritative patterns for production.
Should smoke tests include full gameplay simulation?
Not initially. Start with connection and scene lifecycle checks, then add gameplay assertions in later lessons.
How often should we run headless smoke tests?
At minimum on each multiplayer-related change and before every external playtest build.
Recap
You now have a server-first validation loop that reduces "works on my machine" networking surprises.
Next lesson teaser
Lesson 10 covers cheat and abuse surface triage so your slice stays fair and resilient as multiplayer sessions scale.
Related links
- Lesson 8: Lobby or Join Code UX
- Unity Netcode for GameObjects Documentation
- Unity Guide - Multiplayer Networking Chapter
Save this smoke workflow now. It becomes the foundation for CI and for reliable multiplayer release gates.