Skip to content

Default Ports

Every port MFCloud opens, by component.

Master Console (single-node)

Ports opened by docker-compose up on the console host. None of the container ports below are restricted to localhost by the bundled compose file — firewall everything except 80/443 to trusted/admin subnets only, or bind them to 127.0.0.1: in docker-compose.yml if nothing else needs direct access.

Port Proto Component Purpose
22 TCP sshd (host) Admin access, and outbound from here to every compute/storage host — see Outbound access below.
80 TCP Caddy HTTP → HTTPS redirect.
443 TCP Caddy → FastAPI The web console, JSON API, and noVNC/WebSocket VM console proxy — all share this one port.
8000 TCP uvicorn (app) Direct FastAPI access, bypassing Caddy. Exposed so Caddy can reach it uniformly across HA hosts; not needed for a browser.
5432 TCP PostgreSQL The console database. Reachable from any host that can route to this port unless firewalled — restrict to the console host itself / admin subnet.
6379 TCP Redis Celery broker + WebSocket task-broadcast pub/sub. Same exposure caveat as PostgreSQL.
3370 TCP LINSTOR Controller Only if LINSTOR/DRBD storage is configured — see Storage below.

RabbitMQ — only for Agent Trust API onboarding

5672 (AMQP) and 15672 (management API) are not part of the bundled docker-compose.yml. They only apply if you stand up a separate RabbitMQ instance to use the Agent Trust API onboarding path (RMQ_ADMIN_USER/RMQ_ADMIN_PASS/RMQ_API_URL in .env) — the default onboarding paths (Imaged, SSH-Push) and the Celery task queue (Redis, port 6379 above) don't need it.

HA Console (3-node cluster)

Additional ports on every control-plane host once HA is enabled, on top of the single-node table above. Cluster-internal ports (everything but the VIP's 80/443) should be restricted to the control-plane subnet.

Port Proto Component Purpose
5432 TCP HAProxy PostgreSQL entry point — routes writes to the current Patroni primary.
5433 TCP Patroni Each host's actual PostgreSQL (behind HAProxy; 5432 is taken by HAProxy on that same host).
8008 TCP Patroni REST Role/health API (/patroni, /primary) — used by HAProxy's health checks and the HA status widget.
2379, 2380 TCP etcd Client API / peer replication (cluster quorum).
7000 TCP HAProxy stats Load-balancer status page (/stats).
9444 TCP mfcloud-haman HA Manager out-of-band UI — restrict to operator subnets.
9445 TCP mfcloud-mcp MCP Server — Streamable HTTP endpoint for external AI clients (Claude Desktop, Chatbox, etc). Standalone container, not part of the compose stack; optional.
VRRP keepalived VIP advertisement (IP protocol 112, unicast between the 3 hosts — not TCP/UDP, must be explicitly allowed if the firewall filters by protocol).

Also required between the three control-plane hosts: TCP 22 (root SSH mesh — HA Manager peer control and code shipping) and the etcd/Patroni/HAProxy ports above must reach all three hosts' real LAN IPs, not just localhost.

Outbound access the console needs

The console host also needs to reach out, which is easy to miss when only inbound rules are audited:

Destination Port Proto Why
Every compute node 22 TCP SSH-push onboarding, provisioning, VM/host management commands.
Every compute node 16509/16514 TCP libvirt RPC — VM lifecycle, live migration source/destination.
Every compute node 8443 TCP Health checks against each host's Emergency UI/agent.
K8s cluster API VIP 6443 TCP kubectl against managed Kubernetes clusters — see Managed Kubernetes.
Ceph monitors 3300, 6789 TCP Ceph client traffic (datastore/image operations), if Ceph is used.
LINSTOR satellites 3366 TCP LINSTOR/DRBD storage operations, if LINSTOR is used.
NFS datastores 2049, 111 TCP/UDP Mounting/reading NFS-backed datastores, if configured.
LDAP/AD server (customer) 389 or 636 TCP LDAP/AD integration, if enabled — port is whatever the customer's directory uses.
Identity provider (customer) 443 TCP OIDC/SSO token/userinfo calls, if enabled.
Alert webhook URL (customer) 443 TCP Discord/Slack-style CPU/RAM threshold alerts, if configured.
registry.mfcloud.io 443 TCP Pulling console/agent container images (registry-pull deployments).

Compute Node (Hypervisor)

Port Proto Component Purpose
22 TCP sshd SSH access.
8000 TCP mfcloud-agent (HTTP) Reserved for plain-HTTP fallback if TLS is mis-configured.
8443 TCP mfcloud-agent (TLS) Emergency Host UI + agent JSON API.
5900–6500 TCP QEMU VNC Per-VM noVNC console proxy targets.
16509 TCP libvirtd libvirt RPC (used for live migration).
16514 TCP libvirtd (TLS) libvirt RPC over TLS.
49152–49215 TCP QEMU live migration TCP migration data channel.
9100 TCP node-exporter Prometheus metrics scraping.

VNC (5900–6500) is unauthenticated — it must be firewalled to the master

QEMU exposes each VM's console with listen='0.0.0.0' and no VNC password (the RFB handshake advertises security type 1 = None). The console's per-session token is enforced by the Caddy/websocket proxy on the master, not by the VNC port itself — so anything that can reach 5900–6500 on a hypervisor gets full, passwordless keyboard/mouse/screen access to whatever VMs are running there.

There is no in-product control for this — OVN security groups don't cover it, because VNC lives on the host's management interface, outside the tenant data path. The firewall is the only thing protecting it. On every compute node, restrict 5900–6500 to the control-plane (master) hosts only and drop everything else:

# Allow VNC only from the control-plane hosts (the console proxy's source IPs).
# Use each control-plane host's real management IP; add the console VIP too if
# the app egresses through it. Confirm the source with: ss -tnp | grep 5900
for src in <cp-host-1-ip> <cp-host-2-ip> <cp-host-3-ip>; do
  firewall-cmd --permanent \
    --add-rich-rule="rule family=ipv4 source address=$src port port=5900-6500 protocol=tcp accept"
done
firewall-cmd --permanent \
  --add-rich-rule='rule family=ipv4 port port=5900-6500 protocol=tcp drop'
firewall-cmd --reload

Verify from a host that is not a control-plane node — the probe must now time out (filtered), not connect:

nc -zv -w3 <node-ip> 5900     # expect: timed out / no route, NOT "succeeded"

Storage

Port Proto Component Purpose
3300, 6789 TCP Ceph monitors MON quorum / cluster map.
6800–7300 TCP Ceph OSDs OSD ↔ OSD and client traffic.
3366, 3370 TCP LINSTOR Controller (3370) and satellite (3366).
7000–7999 TCP DRBD Per-resource replication.
2049 TCP NFS NFS data path (datastore mounts).
111 TCP/UDP rpcbind NFS portmapper.

Networking (OVN)

Port Proto Component Purpose
6641 TCP OVN Northbound DB Master ↔ northbound writes.
6642 TCP OVN Southbound DB Chassis ↔ southbound subscriptions.
4789 UDP VXLAN Overlay tenant traffic (legacy).
6081 UDP Geneve Overlay tenant traffic (default in OVN).

Managed Kubernetes (KaaS clusters)

Ports used by the VMs that make up a deployed Kubernetes cluster (k3s or RKE2). These are opened inside the tenant/VM network, between the cluster's own control-plane and worker VMs — plus one path back to the master, which drives every cluster over SSH and runs kubectl against the API. The cluster VMs are ordinary MFCloud VMs, so their host-level firewall (or an OVN security group) is what gates these.

Port Proto Scope Component / Purpose
6443 TCP CP + kube-vip VIP Kubernetes API server. Workers, the kubeconfig, and the master's kubectl all target the VIP:6443 on HA clusters.
9345 TCP CP nodes RKE2 only — supervisor/registration port workers join through (server: https://<cp>:9345). k3s reuses 6443.
10250 TCP all nodes Kubelet API — kubectl logs/exec, metrics-server.
2379–2380 TCP CP nodes (HA only) Embedded etcd client/peer — CP↔CP quorum on 3-CP HA clusters.
8472 UDP all nodes Flannel/Canal VXLAN overlay (the pod network). Must be open between every node.
7946 TCP+UDP all nodes MetalLB speaker memberlist (L2/ARP mode) — LoadBalancer IP election.
30000–32767 TCP all nodes NodePort Service range (Kubernetes default).

kube-vip and MetalLB are ARP-based (L2 mode)

Neither adds a listening port of its own. kube-vip carries the control-plane VIP on 6443 and MetalLB announces LoadBalancer service IPs — both via gratuitous ARP on the local segment. So the requirement isn't a port, it's that the VIP and the MetalLB pool IPs are routable and answer ARP on the cluster's L2 segment, and that 6443 (VIP) and each Service's port are reachable. The API VIP must come from a different IPAM pool than the MetalLB pool.

Outbound from the master to a cluster: TCP 22 (SSH — clusters are provisioned and managed entirely over SSH-CLI) and TCP 6443 (kubectl against the API VIP). Both are per-VM paths already covered by Outbound access, but 6443-to-the-VIP is the one easy to miss.

Quick firewall snapshot

Console host, single-node — a public/edge-facing firewall should only need:

$ firewall-cmd --list-ports
80/tcp 443/tcp

Everything else in the Master Console table (8000, 5432, 6379, 3370) is for host-to-container or admin access and belongs on an internal/admin-only rule, not the edge:

firewall-cmd --zone=internal --add-port=8000/tcp --add-port=5432/tcp --add-port=6379/tcp --permanent
firewall-cmd --reload

Console host, HA cluster — add the HA Console ports, all restricted to the other two control-plane hosts:

firewall-cmd --zone=internal --add-port=5433/tcp --add-port=8008/tcp --add-port=2379-2380/tcp \
  --add-port=7000/tcp --add-port=9444/tcp --add-protocol=vrrp --permanent
firewall-cmd --reload

Freshly-imaged hypervisor you should see something like:

$ firewall-cmd --list-ports
8000/tcp 8443/tcp 16509/tcp 3366/tcp 3370/tcp \
7000-7999/tcp 6789/tcp 3300/tcp 6800-7300/tcp 6641-6642/tcp \
4789/udp 6081/udp 49152-49215/tcp 16514/tcp 9100/tcp

VNC (5900–6500) is deliberately not in that blanket list

It must be a source-restricted rich-rule (master hosts only), never an open --list-ports entry — see the VNC danger callout above. Check it separately:

firewall-cmd --list-rich-rules | grep 5900

If that comes back empty, VNC is wide open — fix it before the node carries tenant VMs. A disabled firewall (firewall-cmd --statenot running) means every port above, VNC included, is exposed to anything that can route to the node.

If a non-VNC port is missing and a feature using it doesn't work, open it with:

firewall-cmd --add-port=<port>/tcp --permanent
firewall-cmd --reload