# Private names
Services in the same project can talk to each other over a private network, using names rather than addresses. Nothing here leaves the region, and nothing here is billed. This page is the reference for those names; the [Minecraft network guide](/guides/minecraft-network-on-workers) uses them end to end.

## Two kinds of name

For a Workload (a [Worker](/guides/minecraft-network-on-workers), a Web App, anything that runs units) with the slug `lobby`:

| name | reaches |
|---|---|
| `lobby` | **every ready unit**. DNS answers with all of their addresses, so a client that picks one spreads over them. Use this for normal requests. |
| `<key>.lobby` | **one specific unit**, by its key: `u1.lobby`, `u2.lobby`, ... for units the scaling policy makes, or a name you chose, such as `duel-77.lobby`, for units your code creates. |

A unit's `key` and its full `private_name` are both fields of the unit in the [Units API](/guides/units-api/overview), so a program never has to build a name by hand:

```json
{ "key": "u2", "private_name": "u2.lobby", "state": "running" }
```

The key is stable for as long as the unit lives. When a unit is scaled away its name stops resolving, and a later unit can take the same key.

## The rules

1. **Same project only.** Every project is its own isolated network. A unit can reach any other unit of its own project on any port, and nothing in another project. A name never resolves outside the project that owns it, and there is no cross-project name.
2. **The slug must start with a letter.** The name is a DNS label, and DNS requires a service name to begin with a letter (a DNS-1035 label). A Workload whose slug starts with a digit, for example `1api`, simply gets no private name: its units have `private_name: null`. Rename it to `api1` or `lobby` and the name appears. Renaming moves the name, which replaces the units through the normal rolling rollout.
3. **Only ready units answer.** A unit that is starting, crashing or stopped is left out of `lobby`, and `<key>.lobby` does not resolve until that unit is ready.
4. **One name per slug.** The slug is unique inside a project, so the name is too. If a product's own template already uses the slug as the name of a network service, the Workload keeps that one and gets no private name.

## A name does not publish a port

A private name is only a **name**. It does not open, forward or load-balance any port: you connect to the name on the port your program listens on, for example `u1.lobby:25565`, exactly as if it were a host name in a LAN. Two consequences:

- You never declare a port to make a private name work. Any port the unit listens on is reachable from the same project.
- It is **not** a public address. To let the internet in, use an [endpoint](/units/endpoints), which is a separate, billed object.

## When to use which

- Another service calling your API: use `api`. When units come and go, the name always points at the ones that are ready.
- Something that must talk to one particular copy, for example a coordinator telling unit 3 to drain, or a game proxy sending players to a known server: use `u3.lobby`.
- A name does not keep a request on the same unit: two connections to `lobby` can reach two different units, so do not rely on one unit remembering the previous request.

## Examples

```bash
# from a shell in any unit of the same project
getent hosts lobby          # one line per ready unit
getent hosts u2.lobby       # that unit's address
nc -vz u2.lobby 25565       # reachable on any port the unit listens on
```

```ts
// Node: connect to one specific backend by its private name
import net from "node:net";
const socket = net.connect({ host: "u2.lobby", port: 25565 });
```

```python
import socket
# any ready unit of the Workload `lobby`
sock = socket.create_connection(("lobby", 25565), timeout=3)
```

In [code mode](/units/code-control), units you create from code get the keys you choose, such as `duel-77.lobby`.
