Running the FigNet Server
This guide covers running the full FigNet dev stack - the EntangleServer master and node, the FnVoice module, the Persistence BaaS, and their supporting infrastructure - locally with Docker Compose. It is verified against docker-compose.full-dev.yml and docker/dev/master.ServerConfig.yaml. A more detailed walkthrough is available in docker/dev/README.md in the repository.
Quick start
# From the repo root. Docker Desktop must already be running.
docker compose -f docker-compose.full-dev.yml up -d
docker compose -f docker-compose.full-dev.yml ps # confirm all healthy
After the stack is up, load the FnVoice module (see Loading the FnVoice module) - without it, voice connects but no audio relays.
- Once the
fignet-server:devandfignet-persistence:devimages already exist,up -dtakes about 10 seconds. - Persistence auth is off in dev (
EnablePersistenceAuth: false), so a Unity client (Entangle + FnVoice) connects without obtaining a JWT first. No--env-fileis needed for game/voice testing.
Prerequisites
- Docker Desktop running. Verify with
docker info. - On Windows, Docker Desktop must have file-sharing access to the drive that contains the repo (Settings > Resources > File sharing) - the master container bind-mounts
ServerConfig.yamlandAppKeys.xmlfrom the repo. - Free host ports: 8080, 8090, 8180, 5432, 6379, 9000, 9100, 9200-9201, 5558-5559/udp, 5660/udp, 9001.
- First-ever run only:
cp docker/dev/.env.dev.example docker/dev/.env.dev(needed only for the Persistence-auth path, not for plain game/voice testing).
What comes up (full-dev stack)
| Container | Role | Host ports |
|---|---|---|
fignet-master | EntangleServer Master + Admin UI | 8080 (admin), 5559/udp (Entangle ENet), 9000 (Entangle WSS), 5558/udp (FnVoice ENet), 9001 (FnVoice WSS) |
fignet-node | EntangleServer Node (game) | 8180, 5660/udp, 9100 (no FnVoice ports on the node) |
fignet-persistence-api | BaaS REST + WSS | 8090 (8091 S2S internal, not mapped) |
fignet-persistence-worker | queue consumer | - |
fignet-postgres / fignet-redis / fignet-minio | persistence store / cache / S3 | 5432 / 6379 / 9200-9201 |
Voice runs on the master only - the master is the only container that exposes 5558/udp and 9001. The node is game-only.
Loading the FnVoice module
docker/dev/master.ServerConfig.yaml's Modules: list ships with only EntangleServer (FNELobby) and EntangleLoadBalancer. The FnVoice sockets (5558/9001) are open, but the FnVoiceServer module is not loaded by default. There are two ways to load it:
Option A - admin-panel upload. Open http://localhost:8080 (admin / changeme) and upload the FnVoiceServer module manifest. Caveat: this lands inside the container, so a recreate (down then up, or up --build) loses it and it must be re-uploaded. A stop/start (see Bringing the stack down) preserves it.
Option B - auto-load via YAML (recommended; survives everything). Add to the Modules: list in docker/dev/master.ServerConfig.yaml, then recreate the master:
Modules:
- AssemblyName: EntangleServer
Type: EntangleServer.Modules.Lobby.FNELobby
- AssemblyName: EntangleLoadBalancer
Type: EntangleServer.Modules.LoadBalancer.FNELoadBalancer
- AssemblyName: FnVoiceServer # <-- add for voice
Type: FnVoiceServer.Modules.Lobby.FNELobby
FnVoiceServer.dll is a project reference of the host, so it already ships inside the fignet-server:dev image - no upload or rebuild needed, just recreate the master so it re-reads the YAML:
docker compose -f docker-compose.full-dev.yml up -d --force-recreate fignet-master
Verify it's up
docker compose -f docker-compose.full-dev.yml ps # all (healthy)
curl -s http://localhost:8080/healthz # master: {"Status":"ok",...}
curl -s http://localhost:8090/healthz # persist: {"status":"ok"}
To confirm FnVoice actually loaded, check the master log for the module load line, or the admin UI modules list:
docker compose -f docker-compose.full-dev.yml logs --tail=80 fignet-master | grep -iE "module|fnvoice|listening"
Connect a Unity client
- Game (Entangle) - ENet UDP
127.0.0.1:5559(master) /:5660(node), or WSSws://localhost:9000(master) /:9100(node). - Voice (FnVoice) - ENet UDP
127.0.0.1:5558, or WSSws://localhost:9001(master only). - Persistence (BaaS) -
http://localhost:8090; inFNPConfigsetBaseUrl=http://localhost:8090,AppId=default. - Auth: dev runs with
EnablePersistenceAuth: false, so a client can connect without a JWT. Flip it totruein the master YAML and use the--env-filepath only when testing the BaaS auth bridge.
Bringing the stack down
# Stops the containers but keeps them and their volumes.
# Fast restart with `start`, and an admin-uploaded FnVoice module survives.
docker compose -f docker-compose.full-dev.yml stop
docker compose -f docker-compose.full-dev.yml start # bring it back
# Full down - removes containers (keeps named volumes / DB state).
docker compose -f docker-compose.full-dev.yml down
# Nuke everything incl. Postgres/Redis/MinIO volumes (fresh slate).
docker compose -f docker-compose.full-dev.yml down -v
stop/start vs down/up: stop keeps the container, so an admin-uploaded module persists. down (or up --force-recreate) creates a fresh container, so the module needs to be re-uploaded (or loaded via the YAML approach above).
Rebuilding after server-code changes
The default up -d reuses the existing :dev images. If server-side C# changed (EntangleServer, FnVoiceServer, FigNet*, FigNet.Persistence), rebuild:
docker compose -f docker-compose.full-dev.yml up -d --build
Unity-client-only changes need no server rebuild.
Other compose files
| File | Use |
|---|---|
docker-compose.full-dev.yml | Default - master + node + full Persistence + infra. |
docker-compose.persistence.yml | Just the BaaS (Postgres + Redis + Persistence Api/Worker). |
docker-compose.dev.yml / docker-compose.node.yml / docker-compose.cluster-dev.yml | Single-server / node-only / multi-node cluster topologies. |
docker-compose.yml | Base/prod-ish. |
No Docker? Run locally instead:
cd EntangleServer && dotnet run
This reads EntangleServer/ServerConfig.yaml, which already lists FnVoiceServer in its Modules:, so voice works out of the box locally - unlike the Docker dev YAML.
Gotchas
- No audio though voice "connects" - the FnVoice module isn't loaded (see Loading the FnVoice module). This is the most common cause.
uprecreated the container and an uploaded module is gone - re-upload it, or use the YAML approach above, orstop/startinstead ofdown/up.- Module loads but spams
MissingFieldException(e.g.FigNet.Configuration.<field>) every tick, "isolating from tick" - the admin-uploaded module was built against a different FigNet version than the running:devimage (version skew). Fix by rebuilding the image from current source (up -d --build) and building the module from that same source, or use the in-imageFnVoiceServer.dll(compiled with the image's core, so it can't skew) via the YAML approach. [Fn][Admin][Cfg] config is read-onlyafter a module upload - expected and harmless: the dev config is bind-mounted read-only, so admin module/game changes apply in-memory only and don't persist across restarts. Persist them by adding to the YAMLModules:/Games:list instead, or drop the read-only flag on the compose mount. The server logs this as a warning, not an error.- Master exits with
Cannot load AppKeys.xml- Docker Desktop file-sharing for the repo's drive is off. - Persistence 401s / admin proxy 502s - only relevant when testing BaaS auth; usually a secret misalignment between
.env.devand the*.ServerConfig.yaml. Irrelevant to game/voice testing. - Node never attaches -
FIGNET_CLUSTER_SECRETmust match in the master YAML, the node YAML, and both containers' environment.