Docker публикует порты в обход UFW
Всё, что указано в ports: у compose, Docker пробрасывает собственными правилами iptables, и они срабатывают раньше цепочек UFW. В итоге ufw status показывает «deny», а порт открыт всему интернету.
Что с этим делать:
ports: - "127.0.0.1:8080:8080" # доступно только с самого сервера
Публиковать наружу только то, что действительно должно быть доступно, а остальное вешать на localhost или держать во внутренней сети compose без ports:.
Ещё одна ловушка: ss -tlnp на хосте не показывает соединения внутри контейнера. Смотреть их удобнее так:
nsenter -t $(docker inspect -f '{{.State.Pid}}' app) -n ss -tni