# Unity dedicated server for NetCraftGames

> A copy-paste prompt that makes your AI add a headless Linux server mode to a Unity project that uses Netcode for GameObjects or Mirror, and build it. Copy it, paste it into your AI coding assistant, and read what it changes.

*NetCraftGames (NCG) by Charging Bull Software. Last updated: 2026-10-03. Canonical page: https://netcraftgames.com/can-you-host-my-game/prompts/unity-dedicated/*

## When to use this prompt

Your Unity game has working multiplayer, but there is no server mode that runs without a window, or it has one and needs the health page and clean stop.

If you are not sure this is the right prompt, answer the five questions on [the check](https://netcraftgames.com/can-you-host-my-game/) first: it names the one prompt that fits.

## The prompt


**Status:** Manifest checked by our validator. Not yet tried against a real AI.

**Unity: add a dedicated-server mode**

```text
You are preparing an existing multiplayer game so a hosting service (NetCraftGames) can run its dedicated server on a Linux machine.
Rules for you:
- Make ONLY the changes listed below. Do not redesign game logic. Do not add multiplayer to a game that has none. If something below cannot be done without a redesign, stop and tell me instead of improvising.
- Never put a password, key or token in code or in any file. Read secrets from environment variables.
- Run every proof step at the end and show me the real output. If a proof step fails, fix it or tell me exactly why you cannot.
- If a step needs a tool you do not have, say so and stop at that step. Do not invent a substitute and do not pretend the step passed.

Facts about the host (exact):
- Linux x86-64, glibc 2.39 or older for every compiled program in the upload.
- The server starts as an unprivileged user in /game. Writable places: /game/saves and a 256 MB /tmp. Nothing is installed, downloaded or compiled on the host. The start command is a fixed list of arguments, not a shell string.
- The host (Ubuntu 24.04 LTS) provides these programs: sh, bash, dash, python3, node, node24 (Python 3.12, Node.js 24.21.0). There is no Java, .NET, Deno or Bun on the host unless you ship it inside the upload.
- Nothing is installed or downloaded on the host: packages travel inside the upload, built for Linux x64 on the owner's own computer.
- A game server cannot look up other websites by name (DNS is closed to it on purpose), so do not make the server depend on calling an outside address by name.
- The host does not set a PORT variable. The port your server uses by default must be the port written in the manifest.
- The host stops the server with SIGTERM and waits up to the number of seconds in the manifest (between 1 and 300).
- The upload is one .tar.gz of at most 512 MiB with at most 10,000 files, no symlinks and no setuid files.
- The host checks your server from outside with a 3 second timeout per request.

TASK: Add a dedicated-server mode to this Unity project that runs on Linux with no window, and build it. Use the Unity version already in ProjectSettings/ProjectVersion.txt. If a server mode already exists, check each step below, skip what is already done and say so.
1. Detect which networking this project uses (Netcode for GameObjects or Mirror) and tell me which. Do not switch libraries.
2. Add a server entry point that runs only in a Linux Dedicated Server build: start the server (not a host), listen on 0.0.0.0 on the port from the -port argument (default 7777), create no player object, no UI and no camera work. Use #if UNITY_SERVER or Application.isBatchMode so client behaviour stays unchanged.
3. Add the side listeners in the same build (R2, R3 and R4 below), using System.Net.HttpListener. Count connected clients from the networking library.
4. Shutdown (R5): make sure SIGTERM on Linux leads to a normal quit (handle Application.quitting) and write the save under /game/saves before exit. Do not rely on Application.persistentDataPath.
5. Build: File > Build Profiles > Linux > enable Server (Dedicated Server) > Architecture x86_64 > Build. Do not enable IL2CPP unless the project already uses it. Keep every compiled plugin at glibc 2.39 or older. Do not ship the editor or any *_DoNotShip folder.

Server requirements (all exact):
R1  Listen on 0.0.0.0 (never 127.0.0.1 only, never a random port). The game port comes from the -port argument (or the PORT environment variable) and defaults to 7777.
R2  Health probe: a small side HTTP listener on TCP port 8080. GET /health answers HTTP 200 with the body exactly "ok" within 3 seconds. It must not touch game state. Return 503 if the server loop is stalled or shutting down.
R3  Join probe: GET /join-probe on the same side listener answers 200 with a body that contains "joinable" while a new player could be admitted, otherwise 503 with the body "full" or "stopping". It needs no login and exposes nothing private.
R4  Player count: a second HTTP listener bound to 127.0.0.1 only, port 9090. GET /players answers 200 with JSON {"players": N}, where N is the number of connected players, a whole number from 0 to 10,000, in a body under 4 KB. Do not count a health probe, a join probe or a player who is only in a reconnect grace window.
R5  Shutdown: on SIGTERM (and SIGINT) stop admitting players, tell connected clients, write the save atomically (write a temporary file in the same folder, flush it, then rename it) under /game/saves, close sockets and exit with code 0 within the grace seconds.
R6  Saves and every other file the server writes live under /game/saves only. Read the folder from an environment variable SAVE_DIR and default to /game/saves (use ./saves when running on your own computer).
R7  Log events (join, leave, error), not every tick. The host drops log lines above its rate limit.
Run the listeners on a background thread or task. Read the player count and the "accepting players" flag from values the main game loop updates.

6. Write ncg.json for the library you found, with the server program name from your build in "manifest.command". Netcode for GameObjects:

----- ncg.json (start) -----
{
  "ncg": 1,
  "game": {
    "name": "My Game"
  },
  "version": "0.1.0",
  "build": {
    "dir": "./Build/LinuxServer"
  },
  "manifest": {
    "engine": "unity-ngo",
    "transport": "udp",
    "platforms": [
      "windows"
    ],
    "mode": "dedicated",
    "networkingImplemented": true,
    "os": "linux",
    "arch": "x64",
    "command": [
      "/game/UnityServer.x86_64",
      "-batchmode",
      "-nographics",
      "-port",
      "7777"
    ],
    "ports": [
      {
        "port": 7777,
        "protocol": "udp"
      },
      {
        "port": 8080,
        "protocol": "tcp"
      }
    ],
    "profile": "standard",
    "savePath": "/game/saves",
    "health": {
      "type": "http",
      "port": 8080,
      "path": "/health",
      "expected": "ok"
    },
    "playerProbe": {
      "type": "http",
      "port": 9090,
      "path": "/players"
    },
    "joinProbe": {
      "type": "http",
      "port": 8080,
      "path": "/join-probe",
      "expected": "joinable"
    },
    "shutdown": {
      "signal": "SIGTERM",
      "graceSeconds": 30
    },
    "idleSupported": true
  }
}
----- ncg.json (end) -----

Mirror with the default KCP transport:

----- ncg.json (start) -----
{
  "ncg": 1,
  "game": {
    "name": "My Game"
  },
  "version": "0.1.0",
  "build": {
    "dir": "./Build/LinuxServer"
  },
  "manifest": {
    "engine": "unity-mirror",
    "transport": "udp",
    "platforms": [
      "windows"
    ],
    "mode": "dedicated",
    "networkingImplemented": true,
    "os": "linux",
    "arch": "x64",
    "command": [
      "/game/UnityServer.x86_64",
      "-batchmode",
      "-nographics",
      "-port",
      "7777"
    ],
    "ports": [
      {
        "port": 7777,
        "protocol": "udp"
      },
      {
        "port": 8080,
        "protocol": "tcp"
      }
    ],
    "profile": "standard",
    "savePath": "/game/saves",
    "health": {
      "type": "http",
      "port": 8080,
      "path": "/health",
      "expected": "ok"
    },
    "playerProbe": {
      "type": "http",
      "port": 9090,
      "path": "/players"
    },
    "joinProbe": {
      "type": "http",
      "port": 8080,
      "path": "/join-probe",
      "expected": "joinable"
    },
    "shutdown": {
      "signal": "SIGTERM",
      "graceSeconds": 30
    },
    "idleSupported": true
  }
}
----- ncg.json (end) -----

Mirror with the Telepathy (TCP) transport instead:

----- ncg.json (start) -----
{
  "ncg": 1,
  "game": {
    "name": "My Game"
  },
  "version": "0.1.0",
  "build": {
    "dir": "./Build/LinuxServer"
  },
  "manifest": {
    "engine": "unity-mirror",
    "transport": "tcp",
    "platforms": [
      "windows"
    ],
    "mode": "dedicated",
    "networkingImplemented": true,
    "os": "linux",
    "arch": "x64",
    "command": [
      "/game/UnityServer.x86_64",
      "-batchmode",
      "-nographics",
      "-port",
      "7777"
    ],
    "ports": [
      {
        "port": 7777,
        "protocol": "tcp"
      },
      {
        "port": 8080,
        "protocol": "tcp"
      }
    ],
    "profile": "standard",
    "savePath": "/game/saves",
    "health": {
      "type": "http",
      "port": 8080,
      "path": "/health",
      "expected": "ok"
    },
    "playerProbe": {
      "type": "http",
      "port": 9090,
      "path": "/players"
    },
    "joinProbe": {
      "type": "http",
      "port": 8080,
      "path": "/join-probe",
      "expected": "joinable"
    },
    "shutdown": {
      "signal": "SIGTERM",
      "graceSeconds": 30
    },
    "idleSupported": true
  }
}
----- ncg.json (end) -----

About ncg.json (the project file the ncg tool reads; it is never packed into the upload):
- Write it in the project root with exactly the content shown. Change only: "game.name" (the real name of the game, 1 to 100 characters), "build.dir" (the folder that holds the final Linux server files), "manifest.platforms" (the platforms the game's players really use) and, where a step says so, the program name in "manifest.command".
- Everything inside the build folder becomes /game on the host, so "/game/server" means the file "server" at the top of that folder.
- "ncg pack <folder>" only zips a folder into the upload and checks it. It does not build, bundle or install anything, so every step above that builds something is yours.

Proof (run these and show me the real output):
P1  Start the finished Linux server the way the host will: the exact command from ncg.json, in a Linux shell (WSL or an ubuntu:24.04 container if you are on Windows or Mac), with no environment variables set except PATH, LANG and HOME.
P2  curl -s -w " %{http_code}" http://127.0.0.1:8080/health  must print ok and 200.
P3  curl -s http://127.0.0.1:9090/players  must print {"players":0}. Connect one real client: it must become {"players":1}.
P4  Send SIGTERM (kill -TERM <pid>). The process must exit with code 0 inside the grace seconds and the save file must exist and parse.
P5  If the ncg tool is installed, run "ncg check" in the project folder and fix everything it lists. If it is not installed, say so; do not write a substitute.
```


## After your AI finishes

1. Read what it changed. It was told to change only what the prompt lists and to stop and ask when something needs a redesign.
2. Run the game with two real players. A prompt cannot prove your game plays well.
3. Come back to [the check](https://netcraftgames.com/can-you-host-my-game/) and answer again, or run the local check the prompt ends with when you have the ncg tool.

## Related pages

- [Can NetCraftGames host my game?](https://netcraftgames.com/can-you-host-my-game/): Five plain questions tell you if NetCraftGames can host your multiplayer game: Yes, Almost with the one change to make, or Not yet. Nothing is uploaded.
- [AI prompts to get your game ready for hosting](https://netcraftgames.com/can-you-host-my-game/prompts/): Copy-paste prompts for your AI coding assistant: add a server mode, a health page and a clean stop, and write the settings file NetCraftGames needs.
- [Multiplayer hosting glossary](https://netcraftgames.com/glossary/): Plain-English definitions of dedicated server, headless, UDP, WebSocket, WSS, WebRTC, STUN, TURN, ENet, KCP, manifest, join probe, weighted hour and more.
