Linux Mint 22.3 calls itself zena, and Docker’s repository has never heard of it. That one mismatch is why Ubuntu instructions copied onto Mint stop at apt update with a 404: they build the repo line from lsb_release -cs, which returns Mint’s codename instead of the Ubuntu release Mint is built on. Point the repo at the Ubuntu base instead and you can install Docker on Linux Mint with the same packages Ubuntu gets, Compose plugin included.
This guide reproduces that error, then sets the repo up the right way, installs Docker Engine and Docker Compose, gives your user access without sudo and runs a first Compose project. It finishes with the fix for docker-compose: command not found, which old scripts hit on every new install. We ran every step on Linux Mint 22.3 Cinnamon (Ubuntu 24.04 noble base) in October 2026 with Docker Engine 29.9.1 and Compose 5.6.0.
Why the Ubuntu commands fail on Linux Mint
Mint keeps two codenames in /etc/os-release, its own release name and the Ubuntu release underneath it. Docker publishes packages only for Ubuntu codenames, so the repo line has to carry the Ubuntu one. Docker’s own docs say Ubuntu derivatives such as Mint aren’t officially supported, though they may work, and on our Mint VM every step below did. On Mint 22.3 the names line up like this:
- Mint release name:
zena, fromlsb_releaseandVERSION_CODENAME - Ubuntu base:
noble, fromUBUNTU_CODENAME - Suite Docker’s repo serves:
noble
You can check both values on any Mint system before touching apt, which takes two seconds and tells you exactly which suite to expect later.
lsb_release -cs
grep -E "VERSION_CODENAME|UBUNTU_CODENAME" /etc/os-release
lsb_release reports Mint’s name, while UBUNTU_CODENAME holds the base Docker actually builds for. Any guide that feeds the first value into a repo line will fail on Mint, so this is the check to run whenever an Ubuntu tutorial misbehaves here.
zena
VERSION_CODENAME=zena
UBUNTU_CODENAME=noble
We added the repo the way most Ubuntu guides still do, with $(lsb_release -cs) in the line, to see exactly what a Mint user gets. The repo line came out as https://download.docker.com/linux/ubuntu zena stable, and apt update failed on it. These are the error lines from its output, trimmed of the mirrors that loaded fine:
Err:9 https://download.docker.com/linux/ubuntu zena Release
404 Not Found [IP: 3.164.195.100 443]
Reading package lists...
E: The repository 'https://download.docker.com/linux/ubuntu zena Release' does not have a Release file.
If you already have that broken line, delete it before going further, because apt update keeps ending in that error for as long as the line exists. It usually lives in docker.list:
sudo rm -f /etc/apt/sources.list.d/docker.list
Mint 21 has the same split, with jammy as its Ubuntu codename, and Docker still publishes for jammy, so the method below should carry over. Our lab only ran Mint 22.3, so treat Mint 21 as untested and check that the Suites line reads jammy before you install. LMDE, the Debian edition of Mint, is built on Debian rather than Ubuntu, so this method doesn’t apply to it; use the Debian repo instead, as in our Docker CE on Debian guide.
Remove the old Docker packages
Mint’s Software Manager offers docker.io from the Ubuntu archive, and it’s tempting because it is one click. It’s also behind: on our Mint VM the archive candidate was eight minor releases older than Docker’s own engine, and its docker-compose-v2 package trailed the current Compose by a full major version. The two sets conflict over containerd and runc, so clear out any archive Docker first:
sudo apt remove -y $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc 2>/dev/null | cut -f1)
The dpkg filter passes only installed packages to apt, so on a fresh Mint install this removes nothing and reports zero changes. Removing the packages doesn’t delete Docker’s data. Volumes live under /var/lib/docker, and since Docker 29 uses the containerd image store on fresh installs, image data sits in containerd’s own data directory, separate from /var/lib/docker. Neither is removed with the packages, so delete them yourself only when you want a truly clean start.
Add the Docker repository for the Ubuntu base
Install the two tools the setup needs and store Docker’s signing key in its own keyring, so apt trusts it only for Docker’s packages. The Signed-By line in the next step points at this exact file, so keep the path as shown.
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
Now write the source file. The suite comes from UBUNTU_CODENAME, which is the whole fix, and it falls back to VERSION_CODENAME so the same line also works on plain Ubuntu:
printf 'Types: deb\nURIs: https://download.docker.com/linux/%s\nSuites: %s\nComponents: stable\nArchitectures: %s\nSigned-By: /etc/apt/keyrings/docker.asc\n' "ubuntu" "$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")" "$(dpkg --print-architecture)" | sudo tee /etc/apt/sources.list.d/docker.sources
Check the Suites line in what tee echoes back. It must say noble on Mint 22.x. If it says zena or another Mint name, the variable didn’t expand, so apt would fail the same way as before and you should rerun the command.
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: noble
Components: stable
Architectures: amd64
Signed-By: /etc/apt/keyrings/docker.asc
With the source in place, apt can see Docker’s packages for noble. Mint’s own repositories and the Ubuntu archive still serve everything else, so this file adds one source rather than turning the system into Ubuntu, and your Mint desktop updates carry on as before. When you later move to a Mint release on a newer Ubuntu base, rerun the printf line so the suite follows the new codename instead of pinning the old one.
Install Docker and the Compose plugin
Refresh apt and install the engine, CLI, containerd, Buildx and Compose in one command. Compose now ships as a plugin to the Docker CLI, so this is the only install step it needs, and it updates with the rest of Docker through Mint’s Update Manager.
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
apt pulled seven packages, 102 MB to download and 388 MB on disk, the same packages Ubuntu 24.04 gets, because it is the same repo. It took 31 seconds on our VM. Once it finishes, the packages have set up these pieces for you:
dockerandcontainerdservices, running and enabled at boot- a
dockergroup with no members yet /var/lib/dockerfor images, containers and volumes- the
docker composeanddocker buildxplugins
Because the service is already enabled, Docker comes back after every reboot without extra steps. Prove the engine works by running a test image, which exercises the client, the daemon and the pull from Docker Hub in one go.
sudo docker run --rm hello-world
The image is pulled from Docker Hub and prints its greeting. A “Cannot connect to the Docker daemon” message instead means the service is not running, so check systemctl status docker. A pull timeout points at DNS or a proxy rather than Docker itself.
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
4f55086f7dd0: Pull complete
Digest: sha256:11a3eb1597e864c51b3f56733cb2e8a90bf0c284bfc05f9444d81c558bc848aa
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
The same packages and repo setup work on Ubuntu itself; our Docker and Compose guide for Ubuntu 24.04 and Debian 13 covers those servers, including the UFW gap that published ports open.
Use Docker without sudo
On a desktop you will run Docker all day, so typing sudo each time gets in the way. Add your account to the docker group. Treat that like handing out sudo, because anyone in the group can start a container that mounts your whole filesystem as root:
sudo usermod -aG docker $USER
newgrp docker
newgrp only applies to the terminal you ran it in. Log out of the Cinnamon session and back in, and every new terminal picks up the group, because group membership is read when you log in. Then check both versions without sudo:
docker version --format "Client {{.Client.Version}}, Server {{.Server.Version}}"
docker compose version
Client and server should match, and Compose answers as a subcommand.

If docker version prints the client but a permission error for the server part, your session hasn’t picked up the group yet. Log out once more rather than adding sudo back, because group membership is only read at login.
Run a first Compose project
A small project shows the Compose workflow without pulling gigabytes: one nginx container serving a page from a folder in your home directory. Create the folder and the page:
mkdir -p ~/hello-compose/html
cd ~/hello-compose
echo "<h1>Compose on Linux Mint</h1>" > html/index.html
Compose reads compose.yaml from the current directory and names the project after that directory, which is why the container below is called hello-compose-web-1. Create the file.
vim ~/hello-compose/compose.yaml
Add the service. The port is bound to 127.0.0.1 on purpose, because a laptop moves between networks and a plain 8080:80 would expose the container to everyone on the cafe Wi-Fi. Docker’s port rules sit ahead of UFW, so the firewall wouldn’t stop it.
services:
web:
image: nginx:alpine
ports:
- "127.0.0.1:8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped
Start it in the background and wait until the container is up, then fetch the page. --wait returns only once the container is running, so the curl right after it does not race the startup.
docker compose up -d --wait
docker compose ps
curl -s localhost:8080
The container is up, the port is bound to loopback only, and nginx returns your page.
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
hello-compose-web-1 nginx:alpine "/docker-entrypoint.…" web 1 second ago Up Less than a second 127.0.0.1:8080->80/tcp
<h1>Compose on Linux Mint</h1>
Edit html/index.html and reload the browser to see the bind mount at work, since the container reads the file straight from your home folder. Stop and remove the project with docker compose down when you’re done. From here, the guide to managing containers with Docker Compose covers multi-service files, and Portainer gives you a web UI over the same engine. If containers are new to you, one of the Docker books we recommend explains images and volumes properly before you build anything bigger.
Fix “docker-compose: command not found”
The Compose plugin installs docker compose with a space, and nothing provides the old hyphenated command. Scripts, Makefiles and README files written for Compose v1 call docker-compose, and on our fresh Mint install that failed straight away:
bash: line 1: docker-compose: command not found
Do not install the old standalone v1 binary to fix it, because Compose v1 is end of life. A two-line wrapper that hands every argument to the plugin is enough, because the plugin accepts the same subcommands:
printf '#!/bin/sh\nexec docker compose "$@"\n' | sudo tee /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
Both spellings now reach the same plugin. Test it from inside the project directory, since that is where scripts call it.
docker-compose version
docker-compose config --services
It printed the plugin version and the web service from the project above, so the wrapper passes arguments through untouched.
Docker Compose version v5.6.0
web
Because the wrapper lives in /usr/local/bin, apt upgrades never touch it, and it always runs whatever plugin version is installed. Update your own scripts to the space form when you next edit them, and keep the wrapper for third-party tools you can’t change. For the same install on Fedora, Rocky Linux or Debian, our Docker install guide for Linux covers each package manager.
I didn’t manage to install current docker-compose version this way. Version 1.17 was installed 🙁
Thanks. The instructions on the actual docker website didn’t work, but yours did
Great thanks