Docker Swarm — A Simpler Alternative
Kubernetes is powerful, but it is also a lot to learn and operate. Docker Swarm is a much simpler way to run containers across more than one server. It is built right into Docker, so if you already know docker and docker compose, you already know most of Swarm. This page shows you how Swarm turns a Compose file into a real cluster, and gives you an honest comparison so you can decide when Swarm is the smart, pragmatic choice — and when it is not.
What Docker Swarm is
A cluster is a group of servers that work together and act like one big computer. Orchestration is the job of deciding which container runs on which server, restarting containers that crash, and replacing servers that die. Kubernetes is the most popular orchestrator. Docker Swarm is a lighter one that ships inside Docker itself — there is nothing extra to install.
In Swarm, every server is called a node. Nodes come in two kinds:
- A manager node runs the “brain”: it stores the desired state of your apps and tells the workers what to do. You can have several managers for high availability (so the cluster keeps working if one manager dies).
- A worker node just runs your containers.
A small manager can also run containers itself, so a one-server “cluster” is perfectly valid for learning.
When to use this
Reach for Swarm when you have a handful of servers, a small team, and you want multi-node container scheduling without paying the Kubernetes learning tax. If you are already on a managed Kubernetes service, or you need a rich ecosystem (auto-scaling, service meshes, operators), Swarm is the wrong tool — use Kubernetes instead.
Setting up a Swarm on Ubuntu
These steps assume Docker is already installed on Ubuntu 22.04 or 24.04 LTS. Run the init command on the machine you want to be the first manager.
sudo docker swarm init --advertise-addr 203.0.113.10
The --advertise-addr value is the IP address other nodes will use to reach this manager. Use the server’s real public or private IP.
Output:
Swarm initialized: current node (k9p2x1abc3def) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-49nj1...-8vxv8 203.0.113.10:2377
To add a manager to this swarm, run 'docker swarm join-token manager'.
Now SSH into each worker server and paste the docker swarm join line Docker printed:
sudo docker swarm join --token SWMTKN-1-49nj1...-8vxv8 203.0.113.10:2377
Output:
This node joined a swarm as a worker.
Back on the manager, check that every node is healthy:
sudo docker node ls
Output:
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
k9p2x1abc3def * manager-1 Ready Active Leader
m4f8t2ghi5jkl worker-1 Ready Active
n7q3v6mno8pqr worker-2 Ready Active
Swarm uses ports
2377(cluster management),7946(node communication), and4789(the overlay network for container-to-container traffic). If you run a firewall, open them between your nodes only — never expose2377to the public internet. Withufw:sudo ufw allow from 203.0.113.0/24 to any port 2377 proto tcp.
Services: Compose, but for a cluster
In plain Docker you start one container with docker run. In Swarm you create a service, which is a description of how many copies (called replicas) of a container you want and which image to use. Swarm then keeps that many copies running across the cluster, restarting and rescheduling them automatically.
sudo docker service create --name web --replicas 3 --publish 8080:80 nginx:1.27
This launches three nginx containers spread across your nodes. The --publish 8080:80 part uses Swarm’s routing mesh: a request to port 8080 on any node is forwarded to one of the running copies, even if that copy lives on a different node. Check it:
sudo docker service ls
Output:
ID NAME MODE REPLICAS IMAGE PORTS
8tz1w2x3y4z5 web replicated 3/3 nginx:1.27 *:8080->80/tcp
Scaling up or down is a single command:
sudo docker service scale web=5
Stacks: deploying a whole app from one file
This is where Swarm shines. A stack is a full multi-service app described in a Compose file and deployed to the cluster at once. The file is nearly identical to a normal docker-compose.yml; you just add a deploy: block for Swarm-specific settings.
# stack.yml
services:
web:
image: nginx:1.27
ports:
- "8080:80"
deploy:
replicas: 3
restart_policy:
condition: on-failure
update_config:
parallelism: 1
delay: 10s
api:
image: ghcr.io/acme/api:1.4.0
deploy:
replicas: 2
Deploy it with one command:
sudo docker stack deploy -c stack.yml myapp
Output:
Creating network myapp_default
Creating service myapp_web
Creating service myapp_api
To update the app, change the image tag in stack.yml and run the same stack deploy command again. Thanks to update_config, Swarm does a rolling update — it replaces containers one at a time so the app stays online. List and remove stacks like this:
sudo docker stack services myapp
sudo docker stack rm myapp
Swarm vs Kubernetes — when to use which
| Concern | Docker Swarm | Kubernetes |
|---|---|---|
| Setup effort | Minutes (swarm init) | Hours, or a managed service |
| Learning curve | Low — it is just Compose | Steep — many new concepts |
| Config files | Familiar Compose YAML | New manifest objects |
| Auto-scaling on load | Not built in | Built in (HPA) |
| Ecosystem & add-ons | Small | Huge (Helm, operators, meshes) |
| Managed cloud offerings | Rare | Everywhere (EKS, GKE, AKS) |
| Best for | Small teams, few servers | Large scale, big teams |
The honest summary: Swarm gets a small cluster running in an afternoon and stays out of your way. But its ecosystem is small and largely frozen — most new tooling, hiring, and tutorials target Kubernetes. Choose Swarm for simplicity today; choose Kubernetes if you expect to grow or need its add-ons.
Best Practices
- Run an odd number of managers (3 or 5) for production so the cluster can survive a manager failure; one manager is fine only for learning.
- Pin image tags (
nginx:1.27, notnginx:latest) so every node runs the exact same version. - Use Docker secrets (
docker secret create) for passwords and API keys instead of putting them in the Compose file. - Set
restart_policyandupdate_configin every service so crashes self-heal and deploys roll out safely. - Restrict the Swarm ports (
2377,7946,4789) to your private network withufw; never expose them publicly. - Keep your
stack.ymlin version control — it is the single source of truth for the whole app. - Be realistic about the future: if you outgrow a few nodes, plan a migration to Kubernetes rather than fighting Swarm’s limits.