Skip to main content
This guide shows you how to pair Epic Online Services (EOS) lobbies with PlayFlow game-server hosting. Players group up in an EOS lobby; when the lobby owner starts the match, a dedicated server is launched on PlayFlow and its address is shared back through EOS Lobby Properties so everyone can connect. The PlayFlow side is a handful of REST calls, so this works with any engine — Unreal, Unity, or custom — that can call the EOS lobby APIs and make HTTP requests.
Never ship a PlayFlow server key (pf_...) inside a game client. A client holding it could start unlimited servers on your account. Route the “start the server” call through a trusted service you control (your backend, or a serverless function). See Keeping the API key safe below for the exact options PlayFlow gives you.
Before getting started, we expect that:
  • You have a working EOS lobby flow (players can create/join a lobby and read lobby & member properties).
  • You have a PlayFlow project and an uploaded dedicated Linux server build. See Docker builds.

How it works

Step 1 — Choose a region

Edgegap places servers from a list of player IPs. PlayFlow instead takes an explicit region (one of 13 regions) — there is no IP-based auto-placement. Pick one of these approaches:
  • Simple: use a fixed region per lobby (for example, a region the players chose in a menu).
  • Geo-aware: have each member store a coarse location (from their platform or a client-side lookup) as an EOS member property, then have your service average them and pick the nearest PlayFlow region. The nearest-region helper from the PlayFab Bridge guide works unchanged here.
region is required on POST /v3/servers/start. If you can’t determine a good region, default to the one closest to most of your player base.

Step 2 — Start the server when the owner starts the match

When the lobby owner decides the match is ready, your trusted service starts a PlayFlow server. PlayFlow allocates the network ports up front, so the response already contains the address — you just have to wait for the server to finish booting before handing it out.
Start the server
curl -X POST "https://api.computeflow.cloud/api/v3/servers/start" \
  -H "api-key: pf_your_server_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "lobby-<EOS_LOBBY_ID>",
    "region": "us-east",
    "version_tag": "your-build-name",
    "ttl": 3600,
    "environment_variables": {
      "EOS_LOBBY_ID": "<EOS_LOBBY_ID>"
    }
  }'
Response (abridged)
{
  "instance_id": "a1b2c3d4-...",
  "status": "launching",
  "region": "us-east",
  "network_ports": [
    {
      "name": "game",
      "internal_port": 7777,
      "external_port": 30412,
      "protocol": "udp",
      "host": "203.0.113.10",
      "tls_enabled": false
    }
  ]
}
Pass a ttl (seconds) as a safety net so an abandoned lobby can’t leave a server running forever. version_tag is the name of the build you uploaded to PlayFlow.

Step 3 — Wait for running, then publish the address

The server starts as launching. Poll its status until it reports running — that’s your “ready” signal — then read the address for your named port from network_ports and store it as EOS Lobby Properties.
Poll until ready
curl "https://api.computeflow.cloud/api/v3/servers/a1b2c3d4-..." \
  -H "api-key: pf_your_server_key"
# repeat every ~2s until "status": "running"
From the port named game, take host and external_port and set them as EOS Lobby Properties (for example SERVER_HOST and SERVER_PORT). EOS notifies every member of the property change.
For UDP ports (most game netcode), host is a proxy IP and external_port is the allocated port. For TCP ports, host is a PlayFlow relay hostname. Always read both from the response — never hardcode a port; it is allocated per server.

Step 4 — Members connect

When members receive the EOS property-changed notification for SERVER_HOST / SERVER_PORT, they connect their netcode transport to that address and join the match. Because you only publish the address once the server is running, members never receive a dead endpoint.

Step 5 — End the match (and rematch)

When the match is over, your service clears the EOS Lobby Properties (signalling everyone to disconnect gracefully) and terminates the PlayFlow server:
Terminate the server
curl -X DELETE "https://api.computeflow.cloud/api/v3/servers/a1b2c3d4-...?shutdown_reason=MATCH_ENDED" \
  -H "api-key: pf_your_server_key"
If the players stay in the lobby, repeat from Step 2 to run a rematch with the same group. You can also let the server terminate itself — every PlayFlow server is handed PLAYFLOW_API_KEY, PLAYFLOW_API_URL, and PLAYFLOW_INSTANCE_ID, so it can call DELETE /v3/servers/$PLAYFLOW_INSTANCE_ID when the last player leaves.

Keeping the API key safe

The security concern is the same as any host: a client that can start servers can be abused. PlayFlow gives you two key types:
KeyPrefixUse
Server keypf_Full access. Keep it only in a trusted server-side context (your service).
Client keypfclient_Safe to ship in clients. Can use player-facing lobby/read endpoints; cannot touch project settings or builds.
By default a client key can start/stop servers. If you want a trusted service to be the only thing that can launch servers, set client_key_server_control to false in your project settings — server lifecycle calls then require a server key, closing the door on client-triggered starts entirely. Recommended: the lobby owner’s client asks your trusted service to start the match; the service holds the server key and makes the PlayFlow calls. The client never sees pf_....
Prefer not to run your own service? PlayFlow’s native lobbies and matchmaking launch the server for you and deliver connection details over a real-time channel — no external key handling required.