How to Replace Your Current Docker Binary with a New One: Upgrade and Installation Guide

How to Replace Your Current Docker Binary with a New One

Sometimes the package manager isn’t fast enough — a critical CVE just dropped, you need a specific patch version for compatibility testing, or you’re working on an air-gapped machine without repository access. In all of these cases, you need to know how to replace the Docker binaries directly, safely, without breaking the containers already running on the host. This guide covers exactly that.

Understanding What “The Docker Binary” Actually Is

“Docker” isn’t a single binary — it’s a small collection of them, each with a distinct role:

  • dockerd — the daemon itself, the long-running background service
  • docker (or docker-cli) — the CLI client you interact with
  • containerd — the actual container runtime the daemon delegates to
  • runc — the low-level OCI runtime that creates and runs the actual container processes
  • docker-proxy, docker-init — smaller support binaries for port forwarding and PID 1 duties inside containers

Replacing “the Docker binary” usually means replacing dockerd and/or the CLI, and possibly containerd/runc if you need a specific version of those too.

Before You Start: Check Your Current Version

docker version
Client: Docker Engine - Community
 Version:           26.1.4
 API version:       1.45

Server: Docker Engine - Community
 Engine:
  Version:          26.1.4
  API version:      1.45 (minimum version 1.24)

Note both client and server versions — you’ll want to confirm the upgrade actually took effect afterward.

Step 1: Back Up What Matters

Before touching binaries, protect yourself from a bad upgrade:

# Note running containers so you can verify state afterward
docker ps -a > ~/docker-container-state-backup.txt

# Back up daemon configuration
sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak 2>/dev/null || echo "no daemon.json present"

# Optional: snapshot the data root if you can afford the downtime and disk space
sudo tar -czf ~/docker-data-backup.tar.gz -C /var/lib docker

Step 2: Download the Static Binaries

Docker publishes versioned static binary tarballs specifically for this kind of manual install/upgrade, separate from the OS package repos:

cd /tmp
curl -fsSL -o docker-27.3.1.tgz https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgz

Verify the download (always check the checksum against Docker’s published values on the download page before trusting a binary you’re about to run as root):

sha256sum docker-27.3.1.tgz

Extract it:

tar xzvf docker-27.3.1.tgz
docker/
docker/containerd-shim-runc-v2
docker/containerd
docker/runc
docker/ctr
docker/dockerd
docker/docker-init
docker/docker-proxy
docker/docker

Step 3: Stop the Running Daemon

Containers managed by containerd can, in many configurations, keep running even if dockerd stops (this is intentional — it’s what allows Docker upgrades without killing your workloads). Still, plan for a maintenance window:

sudo systemctl stop docker
sudo systemctl stop docker.socket

Confirm nothing is still holding the socket:

sudo lsof /var/run/docker.sock 2>/dev/null

Step 4: Replace the Binaries

Copy the new binaries over the old ones. The standard install location is /usr/bin/:

sudo cp docker/* /usr/bin/

Confirm the files landed and check their versions directly:

/usr/bin/dockerd --version
/usr/bin/docker --version
Docker version 27.3.1, build 41ca978
Docker version 27.3.1, build 41ca978

Step 5: Restart the Daemon

sudo systemctl daemon-reexec
sudo systemctl start docker
sudo systemctl status docker

Expected healthy status output:

● docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled)
     Active: active (running) since Wed 2026-07-29 10:02:11 UTC; 3s ago

Step 6: Verify the Upgrade and Container State

docker version

Confirm both client and server now report the new version. Then confirm your previously running containers survived:

docker ps -a
diff <(docker ps -a --format '{{.Names}}' | sort) <(awk '{print $NF}' ~/docker-container-state-backup.txt | sort)

No diff output means everything you had running before is still present (note: whether they auto-restarted depends on their restart policy — this diff only confirms the containers still exist, not that they’re in the running state you expect, so also check docker ps for actual running status).

Doing This on Docker Desktop (macOS/Windows) Instead

The above is for Linux servers running Docker Engine directly. On Docker Desktop, you don’t manually swap binaries — you install a specific release package instead:

# macOS example — download the specific version's .dmg from Docker's release archive,
# then replace the running app
brew install --cask docker

Or download a pinned version directly from Docker’s release notes page and reinstall over the existing app bundle.

Rolling Back If Something Breaks

Keep the old binaries around before you overwrite them — this is the actual safety net, more important than any backup step above:

sudo mkdir -p /opt/docker-backup-26.1.4
sudo cp /usr/bin/dockerd /usr/bin/docker /usr/bin/containerd /usr/bin/runc /opt/docker-backup-26.1.4/

To roll back:

sudo systemctl stop docker
sudo cp /opt/docker-backup-26.1.4/* /usr/bin/
sudo systemctl daemon-reexec
sudo systemctl start docker
docker version

Internal Working: Why Docker Upgrades Don’t Have to Kill Containers

This is the detail that makes manual binary replacement viable at all in production: dockerd doesn’t directly hold onto the container processes — containerd does, via containerd-shim processes, one per running container. When dockerd restarts (or is upgraded), it reconnects to the still-running containerd daemon and re-associates with the existing shims and their containers, rather than restarting them. This is why stopping docker.service alone, upgrading, and restarting it is generally non-disruptive to already-running containers — the actual container processes were never owned by the piece you replaced.

If you also replace containerd itself, this safety property goes away for that upgrade, since you’re now touching the component that directly supervises the container processes — plan for a full stop/start cycle in that case.

Automating This with a Script

For repeatable upgrades across a fleet, wrap the above in a script:

#!/usr/bin/env bash
set -euo pipefail

VERSION="${1:?Usage: $0 <docker-version>}"
BACKUP_DIR="/opt/docker-backup-$(docker version --format '{{.Server.Version}}' 2>/dev/null || echo unknown)"

echo "Backing up current binaries to $BACKUP_DIR"
sudo mkdir -p "$BACKUP_DIR"
sudo cp /usr/bin/dockerd /usr/bin/docker /usr/bin/containerd /usr/bin/runc "$BACKUP_DIR/" 2>/dev/null || true

echo "Downloading Docker $VERSION"
curl -fsSL -o "/tmp/docker-$VERSION.tgz" "https://download.docker.com/linux/static/stable/x86_64/docker-$VERSION.tgz"

echo "Stopping Docker"
sudo systemctl stop docker docker.socket

echo "Installing new binaries"
tar xzf "/tmp/docker-$VERSION.tgz" -C /tmp
sudo cp /tmp/docker/* /usr/bin/

echo "Starting Docker"
sudo systemctl daemon-reexec
sudo systemctl start docker

docker version
echo "Upgrade to $VERSION complete."

Best Practices

  • Always keep the previous version’s binaries in a labeled backup directory before overwriting — this is your fastest rollback path.
  • Verify checksums of downloaded static binaries; never pipe a curl download straight into execution as root.
  • Schedule a maintenance window even though most upgrades are non-disruptive — some do require a full container restart, particularly major version jumps or containerd/runc changes.
  • Read the release notes for the target version, especially around deprecated flags, daemon.json schema changes, and default behavior changes.
  • Prefer your distro’s package manager (apt, yum, dnf) for routine upgrades where available; reserve manual static-binary replacement for cases where you need precise version control or lack repository access.

Troubleshooting

SymptomCauseFix
docker: command not found after replacing binariesCopied to wrong path, not on $PATHConfirm binaries are in /usr/bin and it’s on PATH
Daemon fails to start after upgradeConfig schema in daemon.json incompatible with new versionCheck journalctl -u docker for the exact error, consult release notes
Containers show as running but unreachabledocker-proxy binary version mismatch left stale port forwardingRestart the affected containers explicitly
Version mismatch between client and daemonOnly the CLI binary was replaced, not dockerdEnsure you copy the entire extracted binary set, not just docker

Summary

Replacing Docker’s binaries manually gives you precise control over exactly which version runs on a host, independent of your distro’s package repository timing. The key safety nets are keeping the old binaries backed up for instant rollback, verifying checksums before running anything as root, and understanding that containerd — not dockerd — is what actually keeps your containers alive across most Docker Engine upgrades.

References

  • Docker Engine static binary downloads: https://download.docker.com/linux/static/stable/
  • Docker Engine release notes: https://docs.docker.com/engine/release-notes/
  • Docker Engine installation guide: https://docs.docker.com/engine/install/
  • containerd documentation: https://containerd.io/docs/

Total
0
Shares

Leave a Reply

Previous Post
How to Manage and Configure the Docker Daemon

How to Manage and Configure the Docker Daemon: Settings, Logging, and Runtime Options

Next Post
How to Access the Docker Daemon Remotely

How to Access the Docker Daemon Remotely: Secure Remote Connection Setup and Configuration

Related Posts