September 3, 2026

Docker Swarm Security Hardening: Socket, mTLS, Secrets, Images and Runtime (2026)

The Docker Swarm security checklist in one place: never expose the daemon, mTLS and identity in front of it, secrets and autolock, encrypted overlays and firewall ports, image scanning, SBOMs and signing, and runtime hardening for every service.

Docker Swarm Security Hardening: Socket, mTLS, Secrets, Images and Runtime (2026)

Docker Swarm secures the parts it owns. Every node talks to every other over mutually authenticated TLS with certificates the managers issue and rotate; the Raft log that carries secrets is encrypted at rest; an overlay network becomes IPsec with one flag. What it does not secure is everything you put around it: the daemon socket that is root on the host, the ports you publish, the images you pull, the processes that run as root inside containers, and the people who can reach any of it. This is the checklist for that, in the order an attacker would meet it.

1. The daemon socket is root

/var/run/docker.sock, or the same API on a TCP port, is root on the host, and on a manager it is root on the cluster. Docker's own documentation says it plainly: "Anyone with the keys can give any instructions to your Docker daemon, giving them root access to the machine hosting the daemon."

  • Never bind the daemon to tcp://0.0.0.0:2375. That is the unauthenticated port, and it is scanned for continuously.
  • Do not mount the socket into containers that do not need it. A container with the socket is a root shell on the host with extra steps. If a tool needs it (a reverse proxy watching labels, a monitoring agent), give it a read-only socket proxy that exposes only the endpoints it uses.
  • Keep the group that can read the socket to the people who are already root anyway.

2. Remote access: mTLS, then identity

For one operator, Docker's documented procedure is enough: a CA you control, a server certificate per manager, a client certificate per person, and the daemon started with --tlsverify on port 2376. The step-by-step, with the openssl commands and the client-side environment, is in Docker mTLS for Swarm.

mTLS answers "who may connect". It has no opinion on what they may do, it does not tell you afterwards who did it, and revoking one person means reissuing certificates. The moment there is a second person or a CI runner, put something in front of the daemon that maps a certificate to a user and a role. The SwarmCLI RBAC proxy does that: it terminates mTLS itself on 2376, runs its own certificate authority, maps each client certificate to a named user, checks the user's role against every request, writes an audit entry, and forwards to the daemon over the local socket. The daemon is never exposed.

What that gives you beyond the certificate:

  • Onboarding without openssl. swcproxy user add alice prints a one-time link; Alice downloads a bundle and imports it as a Docker context. swcproxy user delete alice revokes her immediately.
  • Roles. Viewer, operator and admin, applied across the swarm. Operator deploys and updates stacks and services but has no delete verb, so a deploy identity cannot tear one down; exec and port-forward are separate permissions on top of it.
  • An audit log. swcproxy audit ls shows who did what, when, from where.
  • Infrastructure guards. The proxy refuses destructive calls against the resources the cluster depends on, such as removing the agent network, which is the class of accident a tired admin makes at midnight.
  • Two listeners. A plain listener on 127.0.0.1:2375 inside the container for the proxy's own administration, and the mTLS listener on 0.0.0.0:2376 for everyone else. The proxy's users, roles, tokens and audit rows live in a SQLite store, with PostgreSQL as the option for a highly available deployment.

:bootstrap in SwarmCLI deploys the proxy stack, seeds the first admin from PROXY_SEED_USERNAME, creates a managed Docker context that carries the TLS material, and on a licensed swarm adds the licence-renewer. The proxy's own documentation covers configuration and backup.

3. Secrets, and the store that holds them

Use Docker secrets for every credential, through the _FILE convention, never through environment:. Rotate by creating a versioned secret and rolling the service; lock the Raft store with docker swarm update --autolock=true so a copied manager disk is not a copied secret store, and keep the unlock key where you keep the root password. The full procedure, including inspection and the mistakes worth naming, is in Docker Swarm secrets.

Two things people forget: managers hold every secret, so a manager's disk and its backups are sensitive; and a secret mounted into a global service reaches every node, so scope references to the services that read them.

4. Networks and ports

Between swarm nodes, and only between them: TCP 2377 (management, to the managers), TCP and UDP 7946 (gossip), UDP 4789 (VXLAN). None of these belongs on a public interface. If nodes talk over a network you do not own, encrypt the overlay:

Terminal
docker network create --driver overlay --opt encrypted app-net

That turns the VXLAN traffic into IPsec at a CPU cost that is fine for application traffic and noticeable for bulk data, so encrypt the networks that carry credentials and user data, and measure before encrypting the one that carries backups.

Published ports are open on every node through the routing mesh, whichever node runs the task. Publish only what a reverse proxy needs (80 and 443), let the proxy route to services by name on an internal overlay, and keep databases, caches and internal APIs unpublished. When a service must bind on the node it runs on, use mode: host and a placement constraint, and know that you have just made that node special.

5. Images: scan, list, sign

An image is code you did not write, running as whatever user its author chose. Three controls, all in CI before docker stack deploy:

  • Scan every image you build and every base image you pull. Trivy and Grype are the usual pair; Docker Scout is the built-in option. Fail the pipeline on critical and high findings:

    Terminal
    - name: Scan with Trivy
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: 'registry.example.com/app:${{ github.sha }}'
        exit-code: '1'
        severity: 'CRITICAL,HIGH'
    
  • List what is in it. An SBOM per deployed version is what a compliance question and a new CVE both need. docker buildx build --sbom=true attaches one at build time; Syft generates one for any image, and trivy sbom scans it later without pulling the image again.

  • Sign what you deploy, with cosign, and verify the signature in the deploy step. That is what stops a registry compromise, or a mistyped tag, from reaching the swarm.

Pin images by digest in stack files for production, so a deploy is a decision rather than whatever :latest resolves to today.

6. Runtime: the container's own privileges

Every service in the stack file, not only the ones facing the internet:

Terminal
services:
  api:
    image: registry.example.com/api@sha256:…
    user: '10001:10001'
    read_only: true
    cap_drop: [ALL]
    tmpfs: [/tmp]
    deploy:
      resources:
        limits: { cpus: '1.0', memory: 512M }
    logging:
      options: { max-size: '10m', max-file: '3' }
  • Non-root. Build the image with a user and set it; a root process in a container is one kernel bug from a root process on the node.
  • Read-only root filesystem, with a tmpfs for the paths that must be writable.
  • Drop capabilities and add back only the one or two a service actually needs.
  • No new privileges is not available to services: docker stack deploy ignores security_opt with a warning, so remove setuid binaries from the image instead of relying on the flag.
  • Resource limits on everything, because a container without a memory limit is a denial of service waiting for a leak.
  • Log rotation on every node, in the stack file or in daemon.json, because a full disk takes the node's other services with it.

7. Two things people ask for that do not apply

Rootless Docker removes the root daemon and is worth using on a developer machine. It does not run Swarm mode: overlay networking and the manager role need the privileges rootless mode gives up, so a swarm node runs the root daemon and the controls above are how you contain it.

Docker Bench for Security does apply. docker-bench-security checks a node against the CIS Docker Benchmark, daemon configuration, files, images and running containers included, and prints a pass, warn or info per check. Run it on every node after the first hardening pass and after every engine upgrade; its warnings are the to-do list for that node.

8. Nodes and people

  • Managers on a private network, reachable by SSH from a bastion with keys, never by password. Do not run application workloads on managers if you can avoid it; a compromised app container on a manager is a compromised cluster.
  • Keep Docker Engine current, managers first, one at a time, and read the release notes for Swarm entries.
  • Back up the swarm state (/var/lib/docker/swarm on a manager, with the swarm stopped or locked) and treat the backup as secret.
  • Keep the odd number of managers, and drain a node before you reboot it.
  • Every human gets their own identity through the proxy; a shared admin certificate is an audit log with one name in it.

Common concerns, answered

Is Docker Swarm secure by default? The parts Swarm owns are: node-to-node traffic is mutually authenticated TLS with certificates the managers rotate, the Raft log that holds secrets is encrypted at rest, and overlay networks can be encrypted with one flag. What is not secured by default is everything around it: the Docker daemon socket is root, published ports are open on every node, and images are whatever you pulled.

Which ports does Docker Swarm need open between nodes? TCP 2377 for cluster management to the managers, TCP and UDP 7946 for node gossip, and UDP 4789 for overlay network traffic. Open them between swarm nodes only, never to the internet, and add TCP 2376 for the mTLS endpoint if you run one.

Should I expose the Docker daemon on port 2375? No. Port 2375 is the unauthenticated TCP socket, and anyone who can reach it is root on that host. Use the local Unix socket, or TLS with client verification on 2376, or a proxy that adds identity and roles on top of mTLS.

The checklist

  1. Daemon on the Unix socket or mTLS on 2376; nothing on 2375.
  2. A proxy with identity, roles and audit in front of the daemon for every team.
  3. Secrets through _FILE, versioned rotation, autolock on.
  4. Swarm ports open between nodes only; encrypted overlays where the network is not yours; publish only the reverse proxy.
  5. Every image scanned, listed and signed in CI; production pinned by digest.
  6. Every service non-root, read-only, capabilities dropped, limits set, logs rotated.
  7. Managers private, patched, backed up, odd in number.
  8. Docker Bench run on every node after each change, and its warnings worked off.

The Definitive Docker Swarm Guide covers the networking mechanics, and the mTLS how-to the certificate procedure this guide assumes.

Cookies & Privacy

We use strictly necessary cookies to ensure our website functions properly. With your consent, we may also use optional cookies to improve your experience.