Running a Localhost-Only Service on Docker Swarmchain link icon indicating a link to a heading

I have been using Docker Swarm to run services on my home server in order to nicely have containers start at boot. My Swarms all only contain one node, and, early on, I had difficulty getting the Swarm ingress routing mesh working, so I have been using mode: 'host' for all exposed ports.

The host-mode networking simply binds to ports on the host, but, unfortunately, always binds to 0.0.0.0 (all available addresses). It is not possible to change the interface that a Swarm service binds to:

Like lots of little features in Docker, there are years-old open issues and basically no interest in fixing them. I could spend the time to implement this functionality, but from reading those issues, I don’t have high hopes of a pull request being accepted anyway.

So, here’s the hacky solution that gets me what I need. Unless configured not to, the Docker daemon adds a DOCKER-USER chain to iptables. I’ve always used UFW on my systems because I didn’t need complicated firewalling, but this seems to coexist with UFW peacefully.

  1. Install iptables-persistent: apt install iptables-persistent
  2. Create /etc/iptables/rules.v4 with the following content, replacing the value after --dport with whichever port should only be accessible from the localhost:
*filter
:DOCKER-USER - [0:0]
-I DOCKER-USER ! -i lo -p tcp -m tcp --dport 5000 -j DROP
COMMIT
  1. Flush and start netfilter-persistent, reload UFW, and restart docker: netfilter-persistent flush; netfilter-persistent start; ufw reload; systemd restart docker.service

I don’t love this for several reasons. It feels fragile to have two separate things managing my persistent firewall rules. It decreases my opinion of the Docker project to leave small but important issues like this sitting for years. But, this works for now.