As promised, I wanted to share my findings.
First of all, I have to agree with @meyay. Using host mode removes the routing mesh overhead and is generally the right choice for production. From my testing, though, once that overhead is removed, the dominant source of latency appears to be the network latency itself (cross-node or cross-region hops), rather than Docker Swarm.
For reference, this is the application I tested:
- Nginx reverse proxy (domain-based routing)
- FrankenPHP + Symfony application
- PostgreSQL cluster (Patroni, 3 nodes: 1 primary + 2 replicas)
- Each application replica only queries its local database replica (no cross-node DB reads)
Almost every request performs at least one database query, since our full-page cache is stored in PostgreSQL.
1. Swarm vs. docker-compose
Running the exact same stack in Swarm (host mode) and with plain docker-compose showed no measurable difference.
We ran extensive load tests (500 requests/sec for 2 minutes from another AWS instance in the same Network) as our project required it. In both cases the average response time overall was around 5 ms.
2. Routing mesh overhead
I also tested a 3-node Swarm cluster using Multipass VMs on my local machine.
Host mode (no routing mesh):
- Min: 3 ms
- Max: 9 ms
- Mean: 4.8 ms
Routing mesh enabled (Nginx + app both accessed through Swarm services):
- Min: 3 ms
- Max: 38 ms
- Mean: 5.8 ms
The maximum latency varied quite a bit between runs, but the average difference stayed around 1 ms. I certainly couldn’t reproduce anything close to a 2× slowdown from todays perspective.
Of course, this was on a local machine, so there was almost no real network latency.
3. Why host mode still matters
The biggest benefit of host mode is network locality.
If your cluster spans multiple regions (for example Berlin, Helsinki and Lisbon), you don’t want a request entering Berlin only to be forwarded by the routing mesh to an application container in Helsinki. That extra network hop will dominate any routing mesh overhead.
With host mode, traffic can stay local (e.g. Nginx → App → DB on the same node). The trade-off is that you have to handle locality and failover yourself.
In our case, we added a health check for the entire node (Nginx + App + DB). If a node is unhealthy, the external load balancer routes traffic to another healthy node. Our dev-op tought us this.
For database writes, however, we intentionally route traffic to the node hosting the PostgreSQL primary. In that case, a little extra latency is acceptable.
Summary
From my testing, I couldn’t reproduce the 2× latency mentioned in this thread. If all nodes are in the same datacenter or AWS Availability Zone, the routing mesh overhead seems quite small (maybe around 1 ms in my tests).
If your nodes are geographically distributed, I would definitely recommend host mode—not because of the routing mesh itself, but because it preserves locality and avoids unnecessary cross-region network hops. In my opinion, those network hops are likely to have a much bigger impact than Swarm’s own overhead.
@meyay If you ever have some spare time to confirm my results, I’d be happy. I fortunately had the time due to the current project I am working on.
Kind regards
Rozbeh Chiryai Sharahi (Roberto)