Short answer: docker-compose with a hyphen is Compose v1, a Python program whose last release came out in May 2021 and which Docker stopped supporting in July 2023. Current Docker ships Compose as a plugin of the docker command, so you type it with a space: docker compose up -d. Check which of the two you have:
docker compose version
docker-compose version- The first prints a version (
Docker Compose version v5.6.0or a 2.x): Compose is installed. Use the space form, or linkdocker-composeto it for scripts you cannot change. - Both fail: there is no Compose at all. Install
docker-compose-plugin. - Only the second works: you are running v1. Replace it; it breaks against current Docker Engines (below).
Which message do you see?
| Message | Where | Cause and fix |
|---|---|---|
bash: docker-compose: command not found, zsh: command not found: docker-compose | Linux and macOS shells | No v1 and no link. Normal on a current install: type docker compose, or add the link |
Command 'docker-compose' not found, but can be installed with: | Ubuntu | Its apt install docker-compose hint is v1 on 24.04 and 22.04: install the plugin instead |
docker: unknown command: docker compose (Docker 28 and later), docker: 'compose' is not a docker command. (27 and older) | Any | Plugin missing (install) or broken (check) |
unknown shorthand flag: 'd' in -d or 'f' in -f | docker compose up -d, docker compose -f FILE | The same: without the plugin, docker reads -d as its own option |
Either error only with sudo, or sudo: docker-compose: command not found | sudo | Installed for your user only: sudo |
/usr/local/bin/docker-compose: line 1: Not: command not found (dash: 1: Not: not found) | After a manual download | curl saved GitHub’s 404 page: manual install |
bash: /usr/local/bin/docker-compose: Permission denied | After a manual download | The file is not executable: sudo chmod +x it |
/bin/sh: 1: docker-compose: not found | CI jobs, Dockerfile RUN, cron | CI and images |
'docker-compose' is not recognized as an internal or external command,, The command 'docker-compose' could not be found in this WSL 2 distro. | Windows cmd or PowerShell, WSL | Windows and WSL |
KeyError: 'ContainerConfig' | docker-compose up recreating a container | v1 against a current Docker Engine: switch to the plugin |
The docker, bash, dash and sudo lines are real output from Docker CLI 29.9 and 27.5 without the plugin, on Debian 12.
docker-compose vs docker compose
| Aspect | Compose v1 | Compose v2 and v5 |
|---|---|---|
| Command | docker-compose | docker compose |
| Written in | Python | Go |
| Status | Last release 1.29.2 (May 2021), unsupported since July 2023 | Maintained; v5.6.0 is current |
| Installed as | Its own program in PATH | A plugin in a cli-plugins directory |
| Container names | myapp_web_1 | myapp-web-1 |
Compose v5 is v2 renumbered: Docker’s documentation calls it functionally identical, and skipped 3 and 4 to avoid confusion with the old file format versions 2.x and 3.x. Your docker-compose.yml keeps working, unless it is in the 2014 format without a top-level services: key. A top-level version: line is ignored and triggers the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion; delete the line.
Install the Compose plugin
Debian, Ubuntu, Rocky, Alma: Docker’s repository
If Docker came from Docker’s own repository (docker-ce, also what get.docker.com sets up), the plugin is one package:
sudo apt-get update
sudo apt-get install -y docker-compose-plugin docker-buildx-pluginsudo dnf install -y docker-compose-plugin docker-buildx-pluginBuildx is not strictly required, but the package recommends it: without it, docker compose build falls back to the classic builder with a warning. If apt answers E: Unable to locate package docker-compose-plugin, Docker’s repository is not set up, and a Docker you already have is the distribution’s docker.io. Either set up the repository as in Install Docker on Ubuntu and Debian, or use the distribution’s own package:
Distribution packages
| System | Package | What you get |
|---|---|---|
| Debian 12 | docker-compose | Compose v1 1.29.2. Skip it; Debian 12 has no v2 package, so use Docker's repository |
| Debian 13 | docker-compose | Compose 2.26.1: the plugin, plus /usr/bin/docker-compose linked to it |
| Ubuntu 22.04, 24.04 | docker-compose-v2 | Compose 2.40.3, plugin only. Their docker-compose package is v1 1.29.2 |
| Ubuntu 25.10, 26.04 | docker-compose-v2 | Compose 2.40.3, plugin only; the v1 package is gone |
| Alpine | docker-cli-compose | Plugin plus /usr/bin/docker-compose |
| Arch | docker-compose | Plugin plus /usr/bin/docker-compose |
Keep to one source. Docker’s docker-compose-plugin and Ubuntu’s docker-compose-v2 install the same file, /usr/libexec/docker/cli-plugins/docker-compose, so pair the distribution’s package with docker.io and Docker’s with docker-ce.
Manual install, any Linux
Without a package, download the binary from GitHub and check it against its published checksum. Use curl -f: without it, a wrong version (such as 5.6.0 without its v) or architecture saves the nine bytes Not Found as your Compose, which is where line 1: Not: command not found comes from.
VERSION=v5.6.0
ARCH=$(uname -m)
cd "$(mktemp -d)"
curl -fSLO https://github.com/docker/compose/releases/download/$VERSION/docker-compose-linux-$ARCH
curl -fSLO https://github.com/docker/compose/releases/download/$VERSION/docker-compose-linux-$ARCH.sha256
sha256sum -c docker-compose-linux-$ARCH.sha256
sudo install -D -m 0755 docker-compose-linux-$ARCH /usr/local/lib/docker/cli-plugins/docker-composesha256sum must print docker-compose-linux-x86_64: OK (or aarch64) before you install. /usr/local/lib/docker/cli-plugins works for every user, sudo included. Without root, install to ~/.docker/cli-plugins/docker-compose instead and drop the sudo. A manual install never updates itself; repeat it with a newer VERSION now and then.
Keep scripts that call docker-compose working
The plugin binary also runs on its own: called as docker-compose, it behaves like the old command. Docker’s official docker image is built exactly this way. One symlink serves every script, cron job and CI step:
sudo ln -s /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin/docker-compose
docker-compose versionThat is the path of Docker’s and Ubuntu’s packages; for a manual install, link /usr/local/lib/docker/cli-plugins/docker-compose. Debian 13, Alpine and Arch create the link themselves. An alias docker-compose='docker compose' in ~/.bashrc only helps your own typing: bash does not expand aliases in scripts, so every script, cron job and CI step still reports command not found.
One difference bites old scripts: container names use hyphens (myapp-web-1), so anything that greps for myapp_web_1 finds nothing. Run with --compatibility or set COMPOSE_COMPATIBILITY=true to get underscores back while you update them.
Installed, but docker still says unknown command
docker looks for docker-compose in this order and uses the first file it finds:
- directories listed under
cliPluginsExtraDirsin~/.docker/config.json ~/.docker/cli-plugins(or$DOCKER_CONFIG/cli-pluginswhenDOCKER_CONFIGis set)/usr/local/lib/docker/cli-plugins,/usr/local/libexec/docker/cli-plugins,/usr/lib/docker/cli-plugins,/usr/libexec/docker/cli-plugins
The first file wins even when it is broken. A leftover 404 download or a binary for the wrong CPU in ~/.docker/cli-plugins hides a good plugin in /usr/libexec, and you get “unknown command” with Compose installed. docker info names the culprit in its Client section. A working plugin looks like the first block, a broken one like the second:
Client:
Version: 29.9.0
Context: default
Debug Mode: false
Plugins:
compose: Docker Compose (Docker Inc.)
Version: v5.6.0
Path: /usr/libexec/docker/cli-plugins/docker-compose
Plugins:
WARNING: Plugin "/home/USER/.docker/cli-plugins/docker-compose" is not valid: failed to fetch metadata: fork/exec /home/USER/.docker/cli-plugins/docker-compose: exec format errorexec format error means a 404 page or the wrong architecture, permission denied a missing chmod +x. Delete or fix the file it names.
Works without sudo, fails with it
sudo sets HOME to root’s home, so sudo docker compose searches /root/.docker/cli-plugins, not yours, and its secure_path skips ~/.local/bin and similar directories. Install the plugin system-wide (a package, or /usr/local/lib/docker/cli-plugins as above), or stop needing sudo: see permission denied on the Docker socket.
macOS: Docker Desktop and Homebrew
Docker Desktop includes Compose and links both docker and docker-compose into ~/.docker/bin (the default from version 4.89, added to your PATH automatically) or /usr/local/bin, chosen under Settings, Advanced. zsh: command not found: docker-compose after an update usually means a stale link: switch that setting to the other location and back, which rewrites the links, then open a new terminal. Or point the link at the plugin yourself, the fix in Docker’s release notes:
sudo ln -sf /Applications/Docker.app/Contents/Resources/cli-plugins/docker-compose /usr/local/bin/docker-composeWithout Desktop (Colima, for example), brew install docker-compose gives you docker-compose. For docker compose, add Homebrew’s plugin directory to ~/.docker/config.json, merged into any keys already there. JSON does not expand variables, so write the path out: /opt/homebrew on Apple Silicon, /usr/local on Intel.
{
"cliPluginsExtraDirs": ["/opt/homebrew/lib/docker/cli-plugins"]
}Windows and WSL
Windows has no Compose of its own. cmd answers 'docker-compose' is not recognized as an internal or external command,, PowerShell 7 The term 'docker-compose' is not recognized as a name of a cmdlet, function, script file, or executable program. Install Docker Desktop, which brings both docker compose and docker-compose, then close and reopen every terminal and editor window so they pick up the new PATH.
Inside WSL, The command 'docker-compose' could not be found in this WSL 2 distro. is Docker Desktop telling you its integration is off for that distribution. Turn it on under Settings, Resources, WSL integration, then open a new WSL shell.
CI runners, Dockerfiles and cron
GitHub removed Compose v1 from its hosted runner images at the end of July 2024, so workflows that call docker-compose fail there with docker-compose: command not found. Change the step to docker compose. In GitLab CI and other Docker-in-Docker setups, a current official docker:29 image has both forms; images based on plain alpine or debian have neither and print /bin/sh: docker-compose: not found or /bin/sh: 1: docker-compose: not found. Install the package from the table in the image.
cron runs with a short PATH (/usr/bin:/bin), which misses /usr/local/bin. Call /usr/bin/docker compose by its full path in the crontab, or set PATH= at its top.
Check that it works
docker compose version
docker info --format '{{range .ClientInfo.Plugins}}{{.Name}} {{.Path}}{{"\n"}}{{end}}'
docker compose config --quiet && docker compose up -d && docker compose psThe second line lists each plugin with the file it loads from. docker compose config --quiet parses your file in the current directory and prints nothing when it is valid. Once ps shows the services running, test a published port from outside the server with our port checker: a port that answers locally but not from outside is a firewall rule, and Docker publishes ports past UFW, as the install guide explains.
Compose needs the full Docker Engine underneath, which wants a real kernel: a KVM VPS such as our Standard KVM runs it as on bare metal, and the Debian and Ubuntu templates take the repository commands above unchanged. If docker itself is missing, start with docker: command not found.
A script that stops with status 127 after this error? See exit code 127.
Frequently asked
What is the difference between docker-compose and docker compose?
docker-compose is Compose v1, a standalone Python program last released in May 2021 and unsupported since July 2023. docker compose is the current Compose (v2, now numbered v5), a Go plugin of the docker command. Both read the same YAML files; the plugin names containers with hyphens instead of underscores and ignores the top-level version key.
How do I fix "docker: 'compose' is not a docker command"?
Install the Compose plugin: sudo apt-get install docker-compose-plugin with Docker's repository (dnf on Rocky and Alma), or docker-compose-v2 with Ubuntu's own docker.io. Docker 28 and later print the same error as 'docker: unknown command: docker compose', and "unknown shorthand flag: 'd' in -d" after docker compose up -d has the same cause. If the plugin is installed, run docker info: a broken copy in ~/.docker/cli-plugins hides a good one.
How do I make old scripts that call docker-compose work with the new Compose?
Link the plugin binary into your PATH with sudo ln -s /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin/docker-compose. Called by that name, it behaves like the old command. An alias in ~/.bashrc does not help, because bash does not expand aliases in scripts. If a script looks for container names with underscores, run Compose with --compatibility until you update it.