Ports and firewall
Clouduler does not open ports on your host firewall. If you run ufw, firewalld or nftables, you add the rules yourself. This page lists every port, who uses it, and which ones must be open between your nodes versus to the internet. It also describes the iptables chains Clouduler manages for container traffic, so you know what not to touch.
The short version
- Between nodes (internal network only): TCP 7373, 7374, 7375 and UDP 4789. Never expose these to the internet. Put the internal network on a VPN (WireGuard, Tailscale, Headscale) or an isolated VLAN.
- To your users: TCP 80 and 443 for published apps, and 8443 for the dashboard (can be limited to admin networks).
- Only if you use the feature: TCP 2049 (served drives), port 53 (cluster DNS server), a UDP port for each WireGuard tunnel, and the ports of any raw
exposeentries. - Outbound: HTTPS to Let's Encrypt, Cloudflare, GitHub (self-update) and your image registries.
Port table
| Port | Protocol | What uses it | Bound on | Who connects | Open to |
|---|---|---|---|---|---|
7373 (rpc_port) | TCP | Raft, forwarded writes, CLI traffic | The node's internal IP | Other nodes, and the CLI on a manager | Other nodes only |
7374 (rpc_port + 1) | TCP | Drive transport: volume sync, served-drive handoff, folder import | Internal IP | Other nodes | Other nodes only |
7375 (rpc_port + 2) | TCP | Relay: connections to containers that run on another node | Internal IP | Other nodes | Other nodes only |
| 4789 | UDP | VXLAN overlay for stack networks | Internal IP | Other nodes | Other nodes only |
8443 (web.port) | TCP, HTTPS | Dashboard and API | All addresses | Admin browsers, clouduler init --join, MCP clients | Your admin networks |
8444 (web.backend_port) | TCP, HTTP | Proxy to dashboard (bound only when the dashboard is served through the proxy) | 127.0.0.1 only | The local proxy | Nobody (loopback) |
80 (proxy.http_port) | TCP, HTTP | Reverse proxy: redirects, ACME HTTP-01, tls: false routes | All addresses | Clients of published hostnames | Clients, and the internet for public apps |
443 (proxy.https_port) | TCP, HTTPS | Reverse proxy | All addresses | Clients of published hostnames | Clients, and the internet for public apps |
| 2049 | TCP | NFSv4, served drives only | Exported to the internal subnet | Nodes that mount a served drive | Other nodes only |
53 (proxy.dns_server.port) | UDP and TCP | Cluster DNS server, only when proxy.dns_server.enabled | Node address on each listed network | LAN clients | Your LAN |
| Your choice, 51820 in examples | UDP | WireGuard tunnel, wireguard networks only | The wg-<network> interface | Remote peers | The tunnel's peers |
| Your choice | TCP or UDP | x-clouduler.expose published ports | 0.0.0.0 on every node | Whoever you intend | See the warning below |
rpc_port is configurable per node. A node with rpc_port: 7373 occupies 7373 to 7375, so those three must be free. The VXLAN port, the relay offset and the address pools are fixed constants and cannot be changed.
Open the ports
Replace the subnets with yours. The examples assume the internal network is 10.10.0.0/24, your LAN is 192.168.1.0/24, and SSH stays open.
- Web UI
- CLI / config
The web UI cannot change your host firewall. Use the tabs in the CLI / config view. To check which ports your own cluster uses, open Settings > Network (web.port, proxy ports, DNS server port) and Nodes (each node's Cluster RPC port).
- Arch Linux
- Debian
- Raspberry Pi OS
Arch has no firewall by default. With nftables:
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif lo accept
tcp dport 22 accept
# Between nodes, internal network only
ip saddr 10.10.0.0/24 tcp dport { 7373, 7374, 7375 } accept
ip saddr 10.10.0.0/24 udp dport 4789 accept
# ip saddr 10.10.0.0/24 tcp dport 2049 accept # served drives only
# Users
tcp dport { 80, 443 } accept
ip saddr 192.168.1.0/24 tcp dport 8443 accept
# Optional: cluster DNS server for the LAN
# ip saddr 192.168.1.0/24 udp dport 53 accept
# ip saddr 192.168.1.0/24 tcp dport 53 accept
}
}
sudo systemctl enable --now nftables
With firewalld:
sudo firewall-cmd --permanent --new-zone=cluster
sudo firewall-cmd --permanent --zone=cluster --add-source=10.10.0.0/24
sudo firewall-cmd --permanent --zone=cluster --add-port=7373-7375/tcp
sudo firewall-cmd --permanent --zone=cluster --add-port=4789/udp
sudo firewall-cmd --permanent --zone=public --add-service=http --add-service=https
sudo firewall-cmd --permanent --zone=public --add-port=8443/tcp
sudo firewall-cmd --reload
Outbound access
Each node needs outbound TCP 443 to: Let's Encrypt (when proxy.acme.enabled), the Cloudflare API (when cloudflare is set), GitHub (when self_update is set), and your container registries. Git backup uses SSH to your repository host. The default registry is registry-1.docker.io over HTTPS 443; git backup uses SSH (port 22 unless your URL says otherwise).
The CLDR chains Clouduler manages
Clouduler keeps its own iptables chains for container traffic. They are applied with iptables -w and replaced atomically, and the jumps into them are checked every 10 seconds so they survive a Docker restart. Only chains with the CLDR- prefix belong to Clouduler. Your own rules are not touched, but a hand edit to a CLDR- chain is overwritten within about 10 seconds.
| Chain | Table and hook | What it does |
|---|---|---|
CLDR-IN | filter, INPUT position 1 | Stops containers from reaching the host's own listeners (dashboard API, raft, drive and relay ports). For packets from the overlay, egress and nat address pools it accepts: established and related traffic, DNS (port 53) to the overlay pool, the proxy's HTTP and HTTPS ports, every x-clouduler.expose port published on this node, and ICMP echo. Everything else is dropped. Docker -p publishes are not affected. |
CLDR-OVL-SNAT | nat, POSTROUTING | Masquerades overlay traffic going to another node, only for TCP to the proxy ports and ICMP echo, so the remote proxy sees a node address and the network gate passes. |
CLDR-NAT-<bridge> | nat, POSTROUTING | Masquerades an egress or nat network's bridge out of its host interface. |
CLDR-FWD-<bridge> | filter, DOCKER-USER (else FORWARD) | Egress kill-switch: accept bridge to interface, drop the rest from the bridge. |
CLDR-WGIN-<iface> | filter and nat | WireGuard ingress: default deny for new packets from the tunnel, accept only the opened ports. Ports that differ from their target get a DNAT to the node's internal address. |
CLDR-ALLOW | filter, DOCKER-USER first | Per-stack allowed enforcement for routed packets. Exists only when some stack has an allowed list. |
CLDR-ALLOW-NAT | nat, PREROUTING first | Redirects proxy traffic from a restricted stack to another node to this node's own proxy, so the proxy can tell the stack apart by source address. |
What this means for you:
- A container can reach the proxy (80/443) on any node, DNS on its overlay gateway, and
exposeports on any node. It cannot reach 8443, 7373 to 7375 or any other host listener. - A container reaching another node's address is only NATed for TCP to the proxy ports and ICMP echo. Its other traffic there is dropped.
- You can inspect the rules with
sudo iptables -S CLDR-INandsudo iptables -t nat -S CLDR-OVL-SNAT. Do not edit them.