
That is the point. Mode is an enum, it can be “vip” or “dnsrr”.
As one easily guess, “vip” does not stand for Very Important Person.
Why should you have the option to assign a Virtual IP or load balance the traffic by DNS Round Robin policy?
I asked this question myself while developing smgoservice (a Service that does Service Manager and is written in Golang), and use the docker swarm API for instanciate a service, see https://docs.docker.com/reference/api/engine/version/v1.39/#tag/Service/operation/ServiceCreate
A simple answer would be that solving via VIP operates on OSI L4, while DNSRR operates at L7, the application level.
But really the question is which are the use case for which L4 works better, and those for which the L7 LB fit better.
Technically there are specific acronyms for those 2 kinds of Load Balancer. So this is the map for docker swarm Service case:
- L7 Application Load Balancer (ALB) is implemented by Service.EndpointSpec.Mode=dnsrr
- L4 Network Load Balancer (NLB) is implemented by Service.EndpointSpec.Mode=vip
I asked myself if it is really the case. Unfortunately DNS by itself, as protocol, does not “load balance” traffic, it just lists all IPs, so any external client trying to connect the service must rely on a list of
possible IPs and just select one of those.
Details are explained in https://docs.docker.com/engine/swarm/ingress/#configure-an-external-load-balancer
On Kubernetes Service
Well, that is not the exact match. In fact K8S Service is an additional level of abstraction over a Deployment
(most of the time, if it is not a Daemonset or other)
A Deployment usually has an associated DNS name at cluster level, so everything is working by default on a DNSRR like policy.
This is a big difference with docker swarm default, that is VIP and a default load balancer is implemented natively.
Anyway, Kubenetes providers provide their own load balancer, that operates at level 4 or 7,and this info is something one must guess by definition, for example here:
https://cloud.google.com/kubernetes-engine/docs/concepts/service-load-balancer-parameters
is the list of load balancer options allowed in GKE, some are likely to be L7, some operates at L4.
On this field, Kubernetes has the role of a “standard steering committee”, allowing the provider to release their own options and technology, based on the infrastructure size and power.
Kubenetes documentation point some platform specific load balancers: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer
Someway Kubernetes standard fits the need of an on-premise hosting VMs over a Proxmox, and also the needs of a big tech company with multiple datacenter geographically distributed. But still allowing software developer to write code once, and move between providers, based on their market.
Scale your app
The main advance of developing your app natively on Kubenetes is to have a docker image that wrap all the required code and dependency, and has an immutable build process.
Another requirement is to be cross-architecture (this is provided by cross-architecture building and manifest on docker image repository, see https://smartango.com/2021/07/building-multi-arch-docker-image-from-linux-host/).
By default docker swarm provide internal load balancer just of VIP, not for dnsrr, that is supposed to be handled outside the mesh. How can it be handled elsewise? See below.
Real use case
Going back to my smgoservice use case, it was supposed to start a service with Labels whose in turn are read from Traefik to route trafic to the service as backend.
What does traefik expects about a backend? Really, it just refers to it by DNS name, but the point is that I was using it as handly way to publish API Gateway with advanced features. (For details see https://smartango.com/2025/04/micro-api-manager-for-docker-swarm/)
Well, given those advanced feature (like multistage pipelined request, cached result on GET, REST payload translation, etc.), it is really difficult to decide if it makes sense to have multiple instances of a given service (acting as an “API Group”), or just a single instance that proxy requests and possibly use a cached result.
The point is that the right choice depends on the specific definition of the service:
- if a single instance is enough, then using DNSRR has no difference and spare an IP address
- if multiple instance can improve performance, then VIP would deploy the builtin LB provided by Docker Swarm
Ingress IP exhaustion
This problem may seems very unlikely to happen. Well, it is so easy to reach exhaustion that you must consider to design your network carefully before you start.
In fact there is no way to modify a network definition “on-the-fly”, the required procedure is to dump the service list definition (complete and detailed), remove all services, remove network, create network with the same name and good CIDR definition with a class level big enough to support all services.
There are some tricky staff to take into account (for example the simple docker comamnd would dump the network id, together with its name, and it should be ignored during the restore stage).
How to reach the limit:
- every microservice with multiple instance has N+1 IPs, one for each instance, plus one for the Service.
- every API group published by the mean of traefik has a backend and a frontend: backend has N+1 IPs, frontend has no ip: all is managed by traefik internally.
- each Observability tools, SIEM tools, Message Broker, Redis, etc. has, at very least 2 IP assigned.
The (default) limit in fact is 254, with a medium of 4 IPs per services, this means you reach it with near 64 services.
This number is very small if you are planning to define a good distributed application.
You can handle this number of services by yourself, even coding personally, in few months.
My hint is just start with a bigger pool of IPs, and if things are going bad:
- select mini-services (and those that for some reason has single instance) and redefines those as DNSRR
- prepare a good procedure for dump – redefine net – restore, and test it with realistic service def
Handle all dnsrr internally
Just a quick idea to spare IPs is to have a single dns router, routing all request, as an internal service.
I do not want to go deeply on this idea, but conceptually I am thinking about a service with a single IP, talking “some kind of protocol” … but man, one must be specific! Say it is http, and say it is a reverse proxy. Well, that routes requests to all service defined as dnsrr (using an active and periodic API requests to obtain the Service list and its definition).
This is a discrete amount of work, just for sparing 63 IPs (if the math above were correct)
What Traefik is really doing
Just for curiosity, this catch my attention:

Clearly some loadbalancer is implicit, but is it handled by traefik itself, or it just rely on docker swarm builtin staff?
Note
This article was written as a “discovery” by itself, some assumptions were wrong at the begin, and probably the article still contains error. If you like this style send me some good vibes.