# Networking

> How the Agent and Manager communicate, which ports and directions matter, and how to reach the Manager from VMs like Multipass during local development.

Source: https://fleetdock.dev/docs/networking


Fleetdock has two distinct network paths: the **agent → Manager** control path and the
**Manager → database** data path. Getting these right is the most common source of setup
friction.

## Agent to Manager (outbound) [#agent-to-manager-outbound]

The agent connects **outbound** to the Manager at `FLEETDOCK_PUBLIC_URL`. It enrolls,
heartbeats, and polls for operations over that connection — the Manager never dials back to
the agent to run operations.

* In production, `FLEETDOCK_PUBLIC_URL` should be an **HTTPS** URL on port `443` behind your
  reverse proxy.
* It must be reachable from **every** server you connect. Do not use `localhost` unless the
  agent runs on the same machine as the Manager.

## Manager to database (direct) [#manager-to-database-direct]

For **live database administration** and **external instances**, the control plane connects
**directly** to the database's host and port:

* **External** instances are reached at their configured host and port.
* **Managed** instances are reached at their server's address (reported automatically by the
  agent) on the instance's **published port**.

Ensure the database port is reachable from the host running the Manager (security group /
firewall rule). Prefer private network paths over the public internet.

## External applications via Fleetdock Gateway [#external-applications-via-fleetdock-gateway]

For applications **outside** your private network, use the **Fleetdock Gateway** (HAProxy)
instead of publishing database ports directly to the internet. The control plane assigns one
public TCP port per database, enforces CIDR allowlists at HAProxy, and keeps the endpoint
stable across migrations.

```text
External app → gateway.example.com:15432 → HAProxy → server.address:instance.port → Database
```

Public access is **disabled by default**. Enable it per database from **Connectivity** on the
database page. See [External database access](/docs/external-access) for setup, application
credentials, and TLS behavior.

## Ports [#ports]

| Component           | Default port                         | Direction                                                               |
| ------------------- | ------------------------------------ | ----------------------------------------------------------------------- |
| Caddy               | `80`, `443`                          | Agents and browsers → Manager. The only public HTTP surface             |
| Control plane       | `8080`, internal only                | Serves both the API and the dashboard in one process                    |
| Metadata PostgreSQL | `5432`                               | Internal to the Manager only — never public                             |
| Database instances  | the instance's **published port**    | Manager → instance (for live admin/backup)                              |
| Fleetdock Gateway   | `15432–15481` (configurable range)   | External apps → gateway → instance (when public access enabled)         |
| Gateway diagnostic  | `15431` (configurable, `0` disables) | Reports the source address the gateway observes, for setting allowlists |

## DNS and HTTPS [#dns-and-https]

* Point a DNS record at the Manager host. The bundled Caddy obtains a certificate
  automatically; without a domain the installer uses an `<ip>.sslip.io` name, which also
  gets a real certificate.
* **One hostname is enough.** The dashboard and API share an origin because one process
  serves both, so there is no API subdomain, no CORS to configure, and nothing about your
  hostname compiled into the dashboard — changing your domain never requires a rebuild.
* The gateway reuses that same hostname; it speaks raw TCP on its own ports and needs no
  record of its own. The exception is a CDN-proxied record (Cloudflare's orange cloud),
  which passes 80/443 only — point a grey-clouded `gw.example.com` at the host and set
  `FLEETDOCK_GATEWAY_PUBLIC_HOST`.

See [Manager installation](/docs/manager#https-and-reverse-proxy) for a Caddy example.

## Firewalls and NAT [#firewalls-and-nat]

* Allow inbound `80` and `443`; deny direct access to `8080` and `5432`. Leaving `8080`
  reachable also lets a client spoof `X-Forwarded-For` and bypass login rate limiting.
* When external database access is enabled, also open the gateway port range.
* Because the agent connects outbound, agents behind NAT generally work without inbound
  rules — but the database ports the Manager needs for live admin/backups must be reachable
  from the Manager.

## Private networks and public IP [#private-networks-and-public-ip]

You do not need a public IP on every database server. What matters is that:

1. Each server can reach `FLEETDOCK_PUBLIC_URL`.
2. The Manager can reach the database ports it administers.

A private network (VPC peering, WireGuard, or a LAN) satisfies both without exposing
database ports to the internet.

## Local development [#local-development]

For LAN/VM development — for example the Manager on your Mac and an agent in an Ubuntu VM —
the agent must be told an address it can reach, not `localhost`. On an installed Manager,
switch it to your computer's LAN address:

```bash
fleetdock domain 192.168.x.x
```

On a [local install](/docs/local) this also makes the Manager listen on every network
interface instead of only on `127.0.0.1`; `fleetdock domain localhost` switches back.

### Multipass and other VMs [#multipass-and-other-vms]

<Callout type="warn">
  Inside a Multipass (or any) virtual machine, `localhost` points to **the VM itself**, not
  to your macOS host. An agent in the VM that is told to reach `http://localhost` will try
  to connect to the VM, not to the Manager running on your host.
</Callout>

Use the **host-reachable IP** instead of `localhost`. Discover an address the VM can reach:

```bash
# On macOS — find your host's LAN IP (often on en0):
ipconfig getifaddr en0

# From inside the VM — confirm the Manager is reachable:
curl -fsS http://<host-ip>/healthz
```

An installed Manager serves on port 80 (or the `--port` you chose); port `8080` is only
used by `make dev`, where the API runs outside Docker. Values like `<host-ip>` are
environment-specific — do not hardcode a fixed gateway address, as it varies between
machines and networks.

## Docker networking [#docker-networking]

Within the Compose stack, services reach each other by service name (`fleetdock`,
`postgres`) on the internal Docker network — that is why a reverse proxy points at
`fleetdock:8080`. Only Caddy publishes ports (80 and 443); `8080` and `5432` stay internal.

To reach a database running on the **same machine** as the Manager, use the host
`host.docker.internal`: inside the container, `localhost` is the container itself.
