---
id: ports-and-networking
title: Ports and networking
sidebar_label: Ports and networking
---

## Control plane ports

| Port    | Service     | Who connects                     | Expose publicly |
| ------- | ----------- | -------------------------------- | --------------- |
| `3000`  | web         | Browsers, via your reverse proxy | Yes, behind TLS |
| `8080`  | server REST | The web app                      | No              |
| `9090`  | server gRPC | Agents                           | **Yes**         |
| `4822`  | guacd       | The server                       | No              |
| `27017` | MongoDB     | The server                       | No              |
| `6379`  | Redis       | The server                       | No              |

## Direction of travel

```mermaid
flowchart LR
    B["Browser"] -->|HTTPS| P["Reverse proxy"]
    P --> W["web :3000"]
    W --> S["server :8080"]
    A["Agent on a managed server"] -->|"gRPC/TLS :9090, outbound"| S
    S --> G["guacd :4822"]
    G -->|"relayed over the :9090 stream"| A
    A -->|"SSH / RDP / VNC, loopback"| T["Target machine (same host as agent)"]
```

Two things are worth reading off that diagram.

**Agents connect outbound.** No inbound rule is needed on a managed server, and
NAT is not an obstacle. The only requirement is that the machine can reach
`GRPC_HOST`.

**The console rides the agent's connection too.** guacd never dials the target
directly; the server pushes a command down the agent's existing outbound gRPC
stream on `9090`, and the agent relays the protocol traffic from its own
loopback. No route from the control plane to the target's address is needed,
and no new inbound port opens on the target — the same connection that carries
key sync carries console traffic. This is what makes the console work for a
machine behind NAT on a private subnet, as long as its agent is online.

## What to open

### On your firewall, inbound to the control plane

- Your web port, from wherever people are.
- `9090`, from every network holding managed machines.

### Outbound from the control plane

- `gitea.hostxtra.co.uk`, for agent releases and version checks.
- Anything a server-run [monitor](../vantage/monitors.md) checks.
- SMTP, if you use an SMTP notification channel.

No route to the machines you intend to console is needed — that traffic rides
the agent's existing outbound `9090` connection instead.

### Outbound from a managed machine

- `GRPC_HOST`.
- `gitea.hostxtra.co.uk`, for install and self-update.
- Its package mirrors, for OS updates.

## TLS

Terminate TLS for the web UI at your reverse proxy.

gRPC on `9090` is reached directly by agents with `tls: true`, so that port needs
a valid certificate for the name in `GRPC_HOST`. If you proxy it, the proxy must
speak HTTP/2 end to end many do not by default, and the symptom is agents that
register and then fail to hold the command stream.

## Reverse proxy notes

- Point the proxy at `web:3000`. The web app reaches the REST API internally, so
  `8080` does not need publishing.
- The console uses a **WebSocket** at `/api/console/tunnel`. A proxy that does
  not forward upgrade headers breaks the console and nothing else.
- Workflow log streaming is a long-lived response. A short proxy read timeout
  truncates live logs while the run itself continues.

## Air-gapped and restricted networks

The control plane needs outbound access to fetch agent releases. Managed
machines need it too, unless you distribute the agent binary yourself and write
the config by hand the install script's only job is to do those two things.

Licence verification is entirely local, so a licensed install works with no
outbound access to HQ at all.
