[{"data":1,"prerenderedAt":273},["ShallowReactive",2],{"guide-\u002Fguides\u002Fdocker-ufw-published-ports":3,"mdc-pre:Ya6R4cVG7B9XAGwE6cgnJ078TeufC1ZEul8Jl0KM4rQ":272},{"id":4,"title":5,"body":6,"date":255,"description":256,"draft":257,"extension":258,"featured":257,"keyTakeaway":259,"meta":260,"navigation":261,"path":262,"primaryKeyword":263,"seo":264,"service":265,"stem":266,"tags":267,"__hash__":271},"guides\u002Fguides\u002Fdocker-ufw-published-ports.md","Docker and UFW — why published ports ignore your firewall",{"type":7,"value":8,"toc":247},"minimark",[9,13,17,22,30,34,46,49,53,95,98,184,187,191,218,222,231,235,243],[10,11,5],"h1",{"id":12},"docker-and-ufw-why-published-ports-ignore-your-firewall",[14,15,16],"p",{},"For operators running Docker on a Linux VPS who thought UFW was blocking a port — and discovered the internet could still reach it.",[18,19,21],"h2",{"id":20},"key-takeaway","Key takeaway",[14,23,24,25,29],{},"On Linux, Docker often inserts its own firewall rules for published ports. Traffic can reach a container ",[26,27,28],"strong",{},"before"," it hits the UFW chains you configured. Binding app ports to localhost and exposing only a reverse proxy (80\u002F443) is the practical fix for most single-host setups.",[18,31,33],{"id":32},"what-usually-goes-wrong","What usually goes wrong",[14,35,36,37,41,42,45],{},"You enable UFW, allow 22\u002F80\u002F443, and deny everything else. Then you publish a container with ",[38,39,40],"code",{},"-p 8080:80"," or Compose ",[38,43,44],{},"ports: [\"3000:3000\"]",". From outside, port 3000 still answers.",[14,47,48],{},"That is not UFW \"failing\" in the usual sense. Docker manages related iptables\u002Fnftables rules for bridge networking and port publishing. Published container traffic can be diverted in ways that bypass the host INPUT path UFW users expect.",[18,50,52],{"id":51},"practical-pattern-for-one-vps","Practical pattern for one VPS",[54,55,56,63,73,83,89],"ol",{},[57,58,59,62],"li",{},[26,60,61],{},"Public edge:"," reverse proxy only on 80 and 443 (nginx, Caddy, or Traefik).",[57,64,65,68,69,72],{},[26,66,67],{},"App containers:"," no public host ports — or bind to ",[38,70,71],{},"127.0.0.1"," if the proxy runs on the host.",[57,74,75,78,79,82],{},[26,76,77],{},"Databases \u002F admin UIs:"," private Docker networks only; never publish to ",[38,80,81],{},"0.0.0.0",".",[57,84,85,88],{},[26,86,87],{},"SSH:"," still controlled separately (keys, who can log in, ideally source limits).",[57,90,91,94],{},[26,92,93],{},"Cloud firewall:"," if your provider has one, allow only 22\u002F80\u002F443 at that layer too.",[14,96,97],{},"Example Compose idea (illustrative):",[99,100,105],"pre",{"className":101,"code":102,"language":103,"meta":104,"style":104},"language-yaml shiki shiki-themes github-dark","services:\n  app:\n    # reachable only on the Docker network \u002F localhost\n    ports:\n      - \"127.0.0.1:8080:80\"\n  proxy:\n    ports:\n      - \"80:80\"\n      - \"443:443\"\n","yaml","",[38,106,107,120,128,135,143,153,161,168,176],{"__ignoreMap":104},[108,109,112,116],"span",{"class":110,"line":111},"line",1,[108,113,115],{"class":114},"s4JwU","services",[108,117,119],{"class":118},"s95oV",":\n",[108,121,123,126],{"class":110,"line":122},2,[108,124,125],{"class":114},"  app",[108,127,119],{"class":118},[108,129,131],{"class":110,"line":130},3,[108,132,134],{"class":133},"sAwPA","    # reachable only on the Docker network \u002F localhost\n",[108,136,138,141],{"class":110,"line":137},4,[108,139,140],{"class":114},"    ports",[108,142,119],{"class":118},[108,144,146,149],{"class":110,"line":145},5,[108,147,148],{"class":118},"      - ",[108,150,152],{"class":151},"sU2Wk","\"127.0.0.1:8080:80\"\n",[108,154,156,159],{"class":110,"line":155},6,[108,157,158],{"class":114},"  proxy",[108,160,119],{"class":118},[108,162,164,166],{"class":110,"line":163},7,[108,165,140],{"class":114},[108,167,119],{"class":118},[108,169,171,173],{"class":110,"line":170},8,[108,172,148],{"class":118},[108,174,175],{"class":151},"\"80:80\"\n",[108,177,179,181],{"class":110,"line":178},9,[108,180,148],{"class":118},[108,182,183],{"class":151},"\"443:443\"\n",[14,185,186],{},"Exact compose depends on whether the proxy is on the host or in the same Compose file.",[18,188,190],{"id":189},"how-to-check-exposure","How to check exposure",[192,193,194,201,211],"ul",{},[57,195,196,197,200],{},"From another network: ",[38,198,199],{},"nmap"," \u002F browser to unexpected high ports",[57,202,203,204,207,208],{},"On the host: ",[38,205,206],{},"ss -tulpn"," and review Compose ",[38,209,210],{},"ports:",[57,212,213,214,217],{},"Do not assume ",[38,215,216],{},"ufw status"," alone proves a Docker-published port is closed",[18,219,221],{"id":220},"what-a-hardening-pass-usually-adds","What a hardening pass usually adds",[14,223,224,225,230],{},"For one Linux host this often means: agree the public surface, fix published ports, tighten SSH, document recovery, and verify after changes. That is the shape of a ",[226,227,229],"a",{"href":228},"\u002Fserver-hardening","server hardening"," engagement (from £650), not a quick firewall toggle.",[18,232,234],{"id":233},"practical-next-step","Practical next step",[14,236,237,238,242],{},"If Docker and the host firewall disagree on your VPS, start with a scoped hardening pass — or ",[226,239,241],{"href":240},"\u002Fcontact","book a free 15-minute intro call"," to check fit first.",[244,245,246],"style",{},"html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":104,"searchDepth":122,"depth":122,"links":248},[249,250,251,252,253,254],{"id":20,"depth":122,"text":21},{"id":32,"depth":122,"text":33},{"id":51,"depth":122,"text":52},{"id":189,"depth":122,"text":190},{"id":220,"depth":122,"text":221},{"id":233,"depth":122,"text":234},"2026-09-11","Why Docker-published ports can bypass UFW on Linux, how to bind containers safely behind a reverse proxy, and when a hardening project is the right fix.",false,"md",null,{},true,"\u002Fguides\u002Fdocker-ufw-published-ports","docker ufw bypass",{"title":5,"description":256},"hardening","guides\u002Fdocker-ufw-published-ports",[268,269,270,265],"docker","ufw","firewall","7EBE7N1lOv7-Nyl0f-G1dX0dO27xQdmo2H5MJooIwKg","\u003Cspan class=\"line\" line=\"1\">\u003Cspan style=\"--shiki-default:#85E89D\">services\u003C\u002Fspan>\u003Cspan style=\"--shiki-default:#E1E4E8\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\" line=\"2\">\u003Cspan style=\"--shiki-default:#85E89D\">  app\u003C\u002Fspan>\u003Cspan style=\"--shiki-default:#E1E4E8\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\" line=\"3\">\u003Cspan style=\"--shiki-default:#6A737D\">    # reachable only on the Docker network \u002F localhost\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\" line=\"4\">\u003Cspan style=\"--shiki-default:#85E89D\">    ports\u003C\u002Fspan>\u003Cspan style=\"--shiki-default:#E1E4E8\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\" line=\"5\">\u003Cspan style=\"--shiki-default:#E1E4E8\">      - \u003C\u002Fspan>\u003Cspan style=\"--shiki-default:#9ECBFF\">\"127.0.0.1:8080:80\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\" line=\"6\">\u003Cspan style=\"--shiki-default:#85E89D\">  proxy\u003C\u002Fspan>\u003Cspan style=\"--shiki-default:#E1E4E8\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\" line=\"7\">\u003Cspan style=\"--shiki-default:#85E89D\">    ports\u003C\u002Fspan>\u003Cspan style=\"--shiki-default:#E1E4E8\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\" line=\"8\">\u003Cspan style=\"--shiki-default:#E1E4E8\">      - \u003C\u002Fspan>\u003Cspan style=\"--shiki-default:#9ECBFF\">\"80:80\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\" line=\"9\">\u003Cspan style=\"--shiki-default:#E1E4E8\">      - \u003C\u002Fspan>\u003Cspan style=\"--shiki-default:#9ECBFF\">\"443:443\"\u003C\u002Fspan>\u003C\u002Fspan>",1792571462609]