The installer configures ufw with default deny incoming, but that does not cover Docker. Docker publishes container ports in the nat table, which is evaluated before the INPUT chain ufw uses — so any port a container publishes on 0.0.0.0 is reachable from the internet regardless of your firewall rules. See Docker: packet filtering and firewalls.
By design this stack publishes almost nothing: every service is reached through Caddy on ports 80/443 only. To audit what is actually exposed on your server:
docker ps --format '{{.Names}}\t{{.Ports}}' # the reliable check
ss -ltnp # host listenersTrust docker ps here: with "userland-proxy": false the listener is owned by dockerd rather than a docker-proxy process, so ss output is easy to misread even though the port is published.
Anything showing 0.0.0.0:<port>-> is internet-reachable if your server has a public IP.
make doctor runs the same check and warns about every container that publishes a port on all interfaces, apart from Caddy's 80/443 (and 7687 while neo4j is active). With supabase or dify active, the warnings for the ports listed below are expected: restrict those ports at your cloud provider's firewall.
- Neo4j Bolt (
7687) — Caddy publishes0.0.0.0:7687(TLS, proxied to Neo4j for Neo4j Browser) only while theneo4jprofile is active, viadocker-compose.neo4j.yml. Before 1.16.2 it was published on every install; the nextmake updateormake restartcloses it when Neo4j is not selected. - Supabase API gateway — bound to
127.0.0.1:8000by default. Caddy still reaches it over the Docker network, and host-local tooling (curl http://localhost:8000) keeps working. To expose it deliberately, setAPI_GW_HTTP_PORTin.env(e.g.API_GW_HTTP_PORT=8000for all interfaces, orAPI_GW_HTTP_PORT=192.168.1.10:8000for one LAN address) and runmake restart.make doctorwarns if the gateway is bound to all interfaces. - Supabase database and pooler — Supabase's upstream compose still publishes
0.0.0.0:5432and0.0.0.0:6543from itssupavisorservice (containersupabase-pooler). If you run thesupabaseprofile on a public-IP server, restrict these at your cloud provider's firewall or security group, which is enforced outside the host and therefore not bypassed by Docker. You can close the pooler port yourself by settingPOOLER_PROXY_PORT_TRANSACTION=127.0.0.1:6543insupabase/docker/.env— that is the file Compose interpolates for the Supabase stack, and the variable is only used for the port mapping. Editing the root.envhas no effect here: the installer copies it tosupabase/docker/.envonce and afterwards only appends keys that are missing there, and onlyAPI_GW_HTTP_PORT/KONG_HTTP_PORT/KONG_HTTPS_PORTare force-synced. Re-apply your edit after any upstream change that recreates that file. The5432mapping cannot be handled the same way:POSTGRES_PORTis reused as a bare numeric port throughout Supabase's own connection strings, so it cannot take an address prefix. - Dify — Dify's upstream compose publishes its
nginxon0.0.0.0:8080and0.0.0.0:9443(DIFY_EXPOSE_NGINX_PORT/DIFY_EXPOSE_NGINX_SSL_PORT) and itsplugin_daemondebugging port on0.0.0.0:5003. Caddy reaches Dify over the Docker network, so none of them is needed from outside unless you debug plugins remotely; restrict them at your cloud provider's firewall on a public-IP server.