Docker
Docker Swarm

Published: October 14, 2026 · 12 min read

Docker Swarm Tutorial — Manager and Worker Nodes on AWS

Every lesson so far has lived on one machine. That machine is also the single thing standing between your app and an outage. This Docker Swarm tutorial fixes that: we take three EC2 instances, join them into one cluster, and let Docker decide where containers run — so a dead server stops being an emergency. You'll learn what a cluster actually is, the difference between a manager node and a worker node, the single AWS security group rule Swarm needs, and the init → token → join flow that ties the machines together.

In this guide, you will:

  • Understand what a cluster is and why one server is a risk
  • Learn the two roles — manager (master) and worker — and what each is allowed to do
  • Launch three EC2 instances with a simple two-security-group setup
  • Open the one port Swarm needs (2377) on the manager, sourced from the worker group
  • Run docker swarm init --advertise-addr and get the join token
  • Join both workers, confirm with docker node ls, then deploy an app with docker service

Docker Swarm cluster diagram comparing one server alone with a three node cluster of one manager and two workers

What Is a Cluster?

the idea in plain terms
# WHAT IS A CLUSTER?

# ONE SERVER:
#   your app runs on a single machine
#   that machine reboots → your app is down
#   that machine fills up → you are stuck

# A CLUSTER = several machines treated as ONE pool of power:
#
#   machine 1  +  machine 2  +  machine 3
#        └───────────┴────────────┘
#                    │
#          looks like ONE big computer
#
# You stop saying "run this container on server 2".
# You start saying "run 3 copies of this app" —
# and the cluster decides where they go.

That shift — from naming the machine to declaring what you want — is the entire point. In Docker Compose you described an app and one machine ran it. A cluster takes the same idea and spreads it across many machines.

Manager Node and Worker Node

Docker Swarm manager node and worker node roles compared side by side with their responsibilities

the manager's job
# MANAGER NODE — the brain (also called the master)

#   holds the cluster's state — what should run, and how many copies
#   decides which node runs which container (scheduling)
#   hands out the join tokens for new machines
#   notices a dead node and recreates its containers elsewhere
#   the ONLY place cluster commands work — you work here

docker node ls        # works here
docker service create # works here

# A manager can run containers too, so it is not
# a wasted machine — it does real work as well.

Memory hook: the manager is the foreman — it holds the plan and hands out jobs but can still swing a hammer. The workers do the jobs they're given and never argue. You only ever give orders to the foreman.

Why Use a Docker Swarm Cluster?

what a cluster gives you
# WHY USE A SWARM CLUSTER?

# 1. HIGH AVAILABILITY
#    a machine dies at 2am → Swarm restarts its containers
#    on a healthy machine. Nobody wakes up.

# 2. SCALING
#    more traffic? ask for more copies:
docker service scale web=10
#    not enough machines? add another worker — one command.

# 3. LOAD BALANCING (built in)
#    traffic arriving on ANY node is spread across
#    every copy of the app, automatically.

# 4. ROLLING UPDATES
#    new image version? Swarm replaces copies one at a time,
#    so the app never fully goes down.

# And it is already inside Docker — nothing extra to install.

Step 1 — Three EC2 Machines and Two Security Groups

Docker Swarm on AWS security groups showing the manager allowing port 2377 from the worker security group

The security groups stay almost empty at this point — the Swarm rule comes later, once we know what to open:

AWS console — launch the instances
# STEP 1 — three Ubuntu EC2 instances

#   manager   →  swarm-manager
#   worker 1  →  swarm-worker-1
#   worker 2  →  swarm-worker-2

# SECURITY GROUPS — keep it simple:
#   worker-sg  → the TWO workers share it.  SSH (22) only.
#   master-sg  → the manager.               SSH (22) for now.

# That is all you need to start. One more rule gets added
# to master-sg later, once the cluster hands us its token.

Step 2 — Install Docker on All Three

Exactly the same 8 commands from the install lesson — just run them three times, once per machine:

terminal — on every node
# STEP 2 — install Docker on ALL THREE machines

sudo apt update
sudo apt upgrade -y
sudo apt install docker.io -y
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER
newgrp docker
docker --version

# Run this on the manager AND on both workers.
# Swarm is part of Docker itself — there is nothing else to install.

Step 3 — Create the Cluster on the Manager

terminal — on the manager
# STEP 3 — create the cluster (run on the MANAGER only)

sudo docker swarm init --advertise-addr 172.31.1.140
#                                       └── the manager's PRIVATE IPv4

# Swarm initialized: current node (czxqi63twivqojiywd4wf7kk3) is now a manager.
#
# To add a worker to this swarm, run the following command:
#
#     docker swarm join --token SWMTKN-1-54bakns6dwfak7v2ggico...-coi09naoi4j339e0luhpywo55 172.31.1.140:2377

# Copy that whole join line — it holds the token AND the address.
# Note the :2377 at the end. That is the port we open next.

Docker Swarm init and join flow showing the manager producing a token that both worker nodes use to join the cluster

Which IP belongs in --advertise-addr is the question everyone gets wrong first:

terminal — private IP
# PRIVATE IP — the right choice on AWS

docker swarm init --advertise-addr 10.0.1.10
#                                  └── the manager's PRIVATE IPv4

# why this one:
#   • all three instances sit in the same VPC
#   • it matches the security-group-as-source rule you wrote
#   • cluster traffic never leaves AWS's network
#   • the address does not change when the instance restarts

# find it on the instance itself:
hostname -I | awk '{print $1}'

Step 4 — Allow Port 2377 on the Manager

Look at the end of the join command the cluster just printed: 172.31.1.140:2377. That is the door the workers will knock on — and AWS has it shut.

AWS console — the one rule to add
# STEP 4 — allow port 2377 on the MANAGER

# EC2 Console → Security Groups → master-sg → Edit inbound rules → Add rule

#   Type         Custom TCP
#   Port         2377
#   Source       worker-sg      ← the workers' security group

# Why only this one port, and only here:
#   the WORKERS call the MANAGER to join, on 2377.
#   Nothing calls the workers, so worker-sg stays SSH-only.

# Source = worker-sg means only your own worker machines
# can reach 2377 — not the rest of the internet.
⚠️

Set the rule's Source to worker-sg, not an IP address. Then every machine in the worker group can join, no machine outside it can, and adding a third worker later needs no rule change at all.

Step 5 — Join the Workers

terminal — on each worker
# STEP 5 — join the workers (run on EACH worker)

docker swarm join --token SWMTKN-1-54bakns6dwfak7v2ggico...-coi09naoi4j339e0luhpywo55 172.31.1.140:2377

# This node joined a swarm as a worker.

# Run the SAME line on worker 2 — one token works for every
# worker, it is not one token per machine.

# Lost it? Print it again on the manager:
docker swarm join-token worker

Step 6 — Verify the Cluster

terminal — on the manager
# STEP 6 — confirm the cluster (on the MANAGER)

docker node ls

# ID          HOSTNAME         STATUS   AVAILABILITY   MANAGER STATUS
# abc123 *    swarm-manager    Ready    Active         Leader
# def456      swarm-worker-1   Ready    Active
# ghi789      swarm-worker-2   Ready    Active

# the * marks the machine you are typing on
# Leader = this manager is in charge of the cluster
# three nodes Ready = the cluster is live

# On a worker the same command is refused — by design:
#   "This node is not a swarm manager."

Three nodes Ready and Active on the manager means the cluster is formed. Now give it something to run.

Step 7 — Run an App on the Cluster

This is where the cluster stops being theory. On a single machine you used docker run; on a cluster you use docker service — you describe what you want and the manager places it:

terminal — on the manager
# STEP 7 — run an app on the cluster

docker service create --name react-app --replicas 1 srinudev645/react-app:v.1.2.3
#                     │                │            └── the image from Docker Hub
#                     │                └── how many copies you want
#                     └── the service name used by every other command

# docker run  → starts ONE container on THIS machine
# docker service → hands the job to the CLUSTER: the manager
#                  picks a node, starts it there, and keeps it alive
terminal — check the service
# CHECK ON THE SERVICE (on the manager)

docker service ls              # list services
docker service ps react-app    # which node/container it's running on
docker service logs react-app  # logs

# "docker service ps" is the interesting one —
# it shows WHICH NODE each copy landed on.

Docker Swarm replicas spread across nodes and self-healing when one worker node crashes

One copy is a start. The three commands below are the payoff of the whole lesson — scaling, updating without downtime, and removing, all from the manager:

terminal — scale, update, remove
# SCALE, UPDATE, REMOVE

docker service scale react-app=3
#   run 3 replicas, swarm load-balances port 4173

docker service update --image srinudev645/react-app:1.0.1 react-app
#   rolling update to a new tag — copies are replaced one at
#   a time, so the app never fully goes down

docker service rm react-app
#   stop and remove the service from every node

docker service scale react-app=3 does not ask you which machines. You state the number you want; Swarm picks the nodes, starts the copies, and load-balances traffic across them. That declarative style is the real difference between a cluster and three separate servers.

The Docker Swarm Commands

terminal — swarm reference
# THE SWARM COMMANDS YOU USED

docker swarm init --advertise-addr <private-ip>   # create the cluster (manager)
docker swarm join --token <token> <ip>:2377       # add a machine (on that machine)
docker swarm join-token worker                    # show the join command again
docker node ls                                    # list the cluster (manager only)
docker swarm leave                                # a worker leaves the cluster
docker swarm leave --force                        # the manager shuts the cluster down

# SERVICES — the apps running on the cluster (manager only)
docker service create --name <name> --replicas 1 <image>
docker service ls                                 # list services
docker service ps <name>                          # which node each copy is on
docker service logs <name>                        # logs
docker service scale <name>=3                     # change the number of copies
docker service update --image <new-tag> <name>    # rolling update
docker service rm <name>                          # remove the service

# docker node ls is refused on a worker, on purpose:
#   "This node is not a swarm manager."

Docker Swarm Quick Reference

RuleValue
TypeCustom TCP
Port2377
Sourceworker-sg (the workers' security group)
Added tomaster-sg — the manager only
TaskCommandRun on
Create the clusterdocker swarm init --advertise-addr <private-ip>manager
Show the join command againdocker swarm join-token workermanager
Add a machinedocker swarm join --token <token> <ip>:2377the new node
List the clusterdocker node lsmanager
Deploy an appdocker service create --name <n> --replicas 1 <image>manager
See which node runs each copydocker service ps <n>manager
Change the number of copiesdocker service scale <n>=3manager
Update to a new image tagdocker service update --image <tag> <n>manager
Remove the appdocker service rm <n>manager
Remove a nodedocker swarm leavethat node

What's Next

You now have a real cluster: three machines, one inbound rule for port 2377 on the manager, a manager holding the cluster state and two workers taking orders — all verified with docker node ls — and an app deployed across it with docker service. The mental shift is the valuable part: you stopped placing containers on machines by hand and started declaring how many copies you want, leaving placement, failover and load balancing to Swarm. The official Swarm documentation (opens in a new tab) covers what comes next — overlay networks across nodes, secrets, and rolling updates — and the service create reference (opens in a new tab) lists every option for the services you deploy.

← Back to Docker Compose

Continue to Docker Multi-Stage Builds →


Frequently Asked Questions

What is a cluster in Docker Swarm?

A cluster is a group of servers that behaves like one big computer. Instead of saying "run this container on server 2", you say "run three copies of this app" and the cluster decides which machines they land on. Docker Swarm is the clustering tool built into Docker itself, so there is nothing extra to install — you turn a set of machines into a cluster with docker swarm init on one of them and docker swarm join on the others. The benefit is that no single machine is critical any more: if one goes down, the work moves to the machines that are still healthy.

What is the difference between a manager node and a worker node?

The manager node (often called the master) is the brain: it holds the cluster's state, decides which node runs which container, hands out the join tokens, and recreates containers elsewhere when a node dies. It is also the only place cluster commands work — docker node ls on a worker is refused with "This node is not a swarm manager." A worker node is the muscle: it runs the containers the manager assigns, reports its health back, and makes no decisions of its own. A manager can run containers too, so in a three-machine cluster all three do useful work.

Which port must be open to form a Docker Swarm cluster on AWS?

One: TCP 2377 on the manager node. That is the port printed at the end of the join command (for example 172.31.1.140:2377), and it is the port each worker connects to when it runs docker swarm join. Add it as an inbound rule on the manager security group with the workers security group as the Source, so only your own worker machines can reach it. The workers themselves need nothing beyond SSH, because nothing calls them — they call the manager. If 2377 is not open, docker swarm join appears to hang and then times out, because AWS is silently dropping the packets.

Why does only the manager node need a port opened?

Because the traffic only flows one way when a cluster is formed: each worker runs docker swarm join and calls the manager on port 2377. Nothing calls the workers, so their security group can stay as it was — SSH only. That is why you add a single inbound rule, on the manager security group, for TCP 2377. Setting its Source to the worker security group rather than an IP address means every machine in that group is allowed in, nothing else on the internet is, and launching another worker into the same group later works immediately with no rule changes.

What does docker swarm init --advertise-addr do?

It creates the cluster and turns the machine you run it on into the first manager. The --advertise-addr value is the address other nodes should use to reach this manager, which on AWS should be the instance's private IPv4 address when all nodes are in the same VPC — that keeps traffic inside AWS's network and matches a security-group-as-source rule. After it runs, Docker prints a ready-made docker swarm join command containing the token and the manager's address; you copy that whole line and run it on each worker. If you lose it, docker swarm join-token worker prints it again.

What is the Docker Swarm join token and where do I get it?

The join token is the cluster's password — a long string beginning SWMTKN- that proves a machine is allowed to join. It is printed by docker swarm init as part of a complete join command, and you can print it again at any time on the manager with docker swarm join-token worker. The same worker token is used by every worker; it is not one token per machine. Treat it as a credential, because anyone who has it and can reach port 2377 can add their own machine to your cluster. Managers have a separate token shown by docker swarm join-token manager.

How do I run an application on a Docker Swarm cluster?

With docker service instead of docker run. docker run starts one container on the machine you type it on; docker service create --name react-app --replicas 1 your-image hands the job to the cluster, and the manager decides which node runs it and restarts it if that node fails. Check it with docker service ls, see which node each copy landed on with docker service ps react-app, and read output with docker service logs react-app. From there docker service scale react-app=3 changes the number of copies, docker service update --image your-image:1.0.1 react-app does a rolling update one copy at a time, and docker service rm react-app removes it everywhere. All of these run on the manager.

How do I check that my Docker Swarm cluster is working?

Run docker node ls on the manager. It lists every machine in the cluster with its STATUS and AVAILABILITY — three rows all showing Ready and Active means the cluster is live. The row with an asterisk is the machine you are typing on, and the manager shows Leader under MANAGER STATUS. Running the same command on a worker fails on purpose with "This node is not a swarm manager", which confirms the role split. The row with the manager also shows Leader, which means it is the node holding the cluster state.