Open-source project
docker/compose avatar
docker/compose

Docker Compose never reached Docker Swarm, and its Linux install is a hand-placed plugin file

GitHub describes it as Define and run multi-container applications with Docker. The repository metadata lists Go as its primary language. The metadata lists the Apache-2.0 license. This article stays within the project description and details documented in the GitHub repository README.

38,256 stars5,845 forksGoApache-2.0

At a glance

What is it?
Docker Compose is the Go tool that runs multi-container applications described by a compose file, distributed differently on every platform: bundled inside Docker Desktop on Windows and macOS, installed by hand on Linux. The two details that decide whether it fits your setup are the Swarm compatibility note, which records a permanent syntax gap, and the exact filename and directory the CLI plugin must land in.
Who is it for?
Docker Compose is the right tool when your containers run under a single engine and you want one file to describe them, and the install story is trivial if Docker Desktop is already on the machine. It is the wrong tool if you are standardizing on Swarm, because the compose specification was never adopted there and some syntax is simply unavailable, and it adds a manual step on Linux hosts where nothing is bundled.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The plugin file is named docker-compose but the subcommand has a space in it

On Windows and macOS there is nothing to install, because Docker Compose ships inside Docker Desktop. On Linux you download a binary from the release page, rename the one for your OS to `docker-compose`, and copy it to `$HOME/.docker/cli-plugins`. The system-wide alternatives are four paths, and the README gives all of them: `/usr/local/lib/docker/cli-plugins`, `/usr/local/libexec/docker/cli-plugins`, `/usr/lib/docker/cli-plugins`, or `/usr/libexec/docker/cli-plugins`. You may also need to make the file executable with `chmod +x`. The naming split is the part that catches people out. The file on disk carries a hyphen while the command you type is `docker compose`, with a space, because the space is the Docker CLI plugin mechanism rather than part of the binary name. So what this cannot do is self-correct a wrong placement: put the file somewhere the CLI does not scan and the subcommand simply is not there, with no fallback to a globally installed `docker-compose`.

Swarm never adopted the compose specification, and that gap is permanent

There is a note in the project about Docker Swarm that is worth reading as a constraint rather than a caveat. Swarm used to rely on the legacy compose file format but did not adopt the compose specification, which means it is missing some of the recent enhancements in the compose syntax. The note then closes the door: after acquisition by Mirantis, Swarm is not maintained by Docker Inc, and as such some Docker Compose features are not accessible to Swarm users. So what this cannot offer is a forward path. A newer compose syntax feature is not a matter of waiting for Swarm to catch up, since the party that would implement it does not own the project. The consequence is that if your deployment target is Swarm, the current specification is the wrong vocabulary, and the gap will not narrow on its own.

The only shape the quick start gives you binds your source tree and a port

The quick start is three steps: define the environment with a `Dockerfile`, define the services in `compose.yaml`, then run `docker compose up`. The whole example is ten lines:

yaml
services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
  redis:
    image: redis

Two choices in there matter more than they look. `build: .` means the web service is compiled from the directory you are standing in, and the relative bind mount of `.` into `/code` hands the container your working tree. What this example cannot show you is how the same file behaves on a shared host, where a relative bind mount and a published port on `5000` are different decisions. The consequence is that the file is a starting shape, not a production one, and the relative paths are the first thing to replace.

The Go module is versioned /v5 while the Python original sits on branch v1

The module declaration in go.mod is `github.com/docker/compose/v5`, and the release tags are v5.5.1, v5.5.0, and v5.4.0. The major version suffix in the import path is not cosmetic, since Go requires it once a project is past version 1, and it tells you the tool has been through four incompatible module generations. That history is visible elsewhere too: the README points out that the Python version of Compose is available under the `v1` branch. The `v1` name is therefore a branch label for the old implementation, not a version of the current tool, and the two are easy to confuse when you are searching for an answer. The consequence for anyone reading the source is that the current code, the Go code under `cmd/`, `internal/`, and `pkg/`, and the Python code reachable from that branch share a repository and nothing else.

The parser comes from a commit, not a tag, and one dependency is a release candidate

Look closely at the versions in the require block. The compose specification parser, `compose-spec/compose-go/v2`, is pinned to `v2.15.1-0.20260918184426-f18e211cbeaf`, which is a pseudo-version naming a specific commit rather than a published tag. `containerd/platforms` is at `v1.0.0-rc.5`, a release candidate, and `DefangLabs/secret-detector` is another pseudo-version at `v0.0.0-20260916192156-3e28d7ed64df`. The rest sit on ordinary tags, including `moby/buildkit` at v0.33.0 and `moby/moby/api` at v1.56.0. What this means for an operator is that the part of the stack reading your compose file is tracking a moving commit, so two builds of the same Compose release can disagree about what a file means. That is a deliberate choice for a spec that is still evolving, and it is also the reason a pinned Compose binary is the level at which behavior is actually reproducible.

The Makefile insists the binary lands at exactly $DESTDIR/docker-compose

The build file carries a long comment about DESTDIR, and it is not a general packaging note. DESTDIR overrides the output path for binaries and other artifacts, it is used by docker/docker-ce-packaging for the apt and rpm builds, and the comment says it is important that the resulting binary ends up EXACTLY at the path `$DESTDIR/docker-compose` when specified, with a link to the rpm spec that consumes it. Version stamping follows the same contract, with `GO_LDFLAGS` set to inject a `Version` value derived from `git describe --match 'v[0-9]*' --dirty='.m'`, and a dirty tree is marked with an `m` suffix in the version string. What this cannot accommodate is a distributor that wants a different layout or a renamed binary. The consequence is that anyone packaging Compose is not free to reorganize the output, and the default is to place everything under subdirectories of `./bin/`.

CGO is off by default and the build image carries a macOS cross toolchain

The build container sets `CGO_ENABLED=0`, which is why the base stage installs a plain Alpine Go image rather than a C toolchain runtime, and the same stage adds `clang`, `docker`, `file`, `findutils`, `git`, `make`, `protoc`, and `protobuf-dev` for the build itself. Cross-compilation runs through a dedicated helper image, and the macOS binaries are produced with a separate osxcross stage. Two other build arguments shape the output: `DOCS_FORMATS` defaults to `md,yaml`, and `LICENSE_FILES` defaults to a pattern that selects Dockerfiles, Makefiles, Go, hcl, and shell files for the license header check. The detail that affects every local build is the tag: `BUILD_TAGS` defaults to `e2e` in the Dockerfile and `GO_BUILDTAGS` defaults to `e2e` in the Makefile, so the end-to-end test code is compiled into a default build rather than only into a test build.

Editorial conclusion

Docker Compose is the right tool when your containers run under a single engine and you want one file to describe them, and the install story is trivial if Docker Desktop is already on the machine. It is the wrong tool if you are standardizing on Swarm, because the compose specification was never adopted there and some syntax is simply unavailable, and it adds a manual step on Linux hosts where nothing is bundled. Before committing, confirm your orchestration target, place the plugin binary in a cli-plugins directory the Docker CLI actually scans, and remember that the e2e build tag is on by default if you build from source.

Frequently asked questions

What is Compose used for?

It is a tool for running multi-container applications on Docker, defined using the Compose file format. A Compose file describes how the one or more containers that make up an application are configured, and once you have that file you can create and start the application with the single command `docker compose up`.

How do I install Docker Compose on Linux?

Download the binary from the repository release page, rename the one for your OS to `docker-compose`, and copy it to `$HOME/.docker/cli-plugins`. A system-wide install can go in `/usr/local/lib/docker/cli-plugins`, `/usr/local/libexec/docker/cli-plugins`, `/usr/lib/docker/cli-plugins`, or `/usr/libexec/docker/cli-plugins`, and you may need to run `chmod +x` on it.

Do I need to install Docker Compose on Windows or macOS?

No. Docker Compose is included in Docker Desktop for Windows and macOS. The manual binary placement described in the project is the route documented for Linux only.

Does Docker Compose work with Docker Swarm?

Partly, and the gap is not closing. Swarm used to rely on the legacy compose file format but did not adopt the compose specification, so it is missing recent syntax enhancements, and after acquisition by Mirantis it is not maintained by Docker Inc, which means some Compose features are not accessible to Swarm users.

What happened to the Python version of Docker Compose?

It is still reachable: the README points to the `v1` branch of this repository for the Python version of Compose. The current tool is written in Go, and its module path is github.com/docker/compose/v5 with release tags in the v5 line.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/docker-compose.svg)](https://hysenlabs.com/projects/docker-compose)