I used to install Docker by simply running apt install docker.io and grabbing docker-compose from Ubuntu’s APT repository.

However, I discovered that those versions were often outdated and missing some new features – for example, the new Docker Compose v2 plugin wasn’t available with the old setup. The standalone docker-compose binary from APT is the older v1 release, which is now deprecated​.

In this tutorial, I’ll share what I learned about properly installing Docker Engine and the Docker Compose plugin using Docker’s official repository.

This way, you’ll get the latest versions and features straight from Docker, which is now considered best practice.

📬 Newsletter

Subscribe for occasional updates – installation guides, practical tips, and lessons learned.

I’m In!

Why Not Use docker.io and docker-compose from APT?

Ubuntu’s default packages for Docker (docker.io) and Compose (docker-compose) are convenient, but there are a few important reasons to avoid them:

  • Outdated Versions: The Docker packages provided via Ubuntu’s repositories often lag behind Docker’s official releases. Using older versions means you might miss critical updates and features.
  • Deprecated Compose V1: The APT package docker-compose installs the old Python-based Docker Compose (Compose V1). Docker has deprecated this in favor of Compose V2, which is rewritten in Go and is now part of Docker CLI. The v1 tool will no longer receive updates, so sticking with it isn’t ideal.
  • Missing New Features: Newer Docker features (like the official Compose V2 plugin that integrates with the docker command) aren’t available if you install via the default docker.io and docker-compose. The old approach doesn’t include the docker compose command at all. By contrast, Docker’s repository provides a docker-compose-plugin package (Compose v2) that lets you run docker compose ... commands natively.
  • Best Practice: Docker officially recommends installing Docker Engine and the Compose plugin from their own APT repository for up-to-date, consistent results. This ensures you get Docker as it’s meant to be used.

In short, using the Ubuntu-packaged docker.io and docker-compose might work, but you’ll be a step behind.

The rest of this tutorial will show how to switch to Docker’s official packages so you have the latest and greatest.

Uninstall Old Packages

Before installing Docker from the official repository, remove any old Docker- or container-related packages to prevent conflicts.

You can uninstall them all in one go with this loop:

for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
  sudo apt remove -y $pkg
done

This command iterates through common legacy Docker and container packages – such as docker.iodocker-composecontainerd, and more – and uninstalls each one.

It won’t delete your images or containers. It simply removes the outdated binaries, so you’re starting with a clean slate for the new installation.

Once that’s done, clean up any leftover dependencies:

sudo apt autoremove -y

This ensures any libraries or packages that were only needed by the old Docker binaries are removed, leaving you with a clean slate for the new installation.

Install Docker from Official Docker Repository

Now we’ll install Docker Engine and the Docker Compose Plugin using Docker’s official Apt repository.

This will ensure we get the latest version of Docker CE (Community Edition) and the integrated Compose v2.

First, set up Docker’s apt repository on your Ubuntu server (this only needs to be done once). Run the following commands to add the Docker GPG key and the “stable” repository source:

sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  | sudo tee /etc/apt/keyrings/docker.asc > /dev/null
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
  "deb [arch=$(dpkg --print-architecture) \
  signed-by=/etc/apt/keyrings/docker.asc] \
  https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update

Let’s break down what this does:

  • Installs required packages like curl and gnupg (if not already present) for handling HTTPS and GPG keys.
  • Downloads Docker’s official GPG key and saves it in /etc/apt/keyrings/docker.asc (a secure location for APT keys).
  • Adds Docker’s APT repository to your server’s software sources (pointing to your Ubuntu release codename).
  • Updates the package list to include Docker’s repository.

With the repository in place, you can install Docker. We’ll install Docker CE, the Docker CLI, the container runtime, and plugins for Buildx and Compose – all in one go. Run:

sudo apt install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

This installs the Docker daemon (docker-ce) along with:

  • docker-ce-cli: The command-line interface for Docker.
  • containerd.io: The container runtime Docker uses under the hood.
  • docker-buildx-plugin: Docker’s Buildx plugin (for extended build capabilities).
  • docker-compose-plugin: The Docker Compose V2 plugin, which adds the docker compose subcommand.

These are the same packages recommended by Docker’s official installation guide.

With this, Docker Engine is installed as a service on your server.


Sometimes Docker doesn’t start automatically after installation. To ensure it’s running, start the service and check its status:

sudo systemctl start docker.service
sudo systemctl status docker.service

If the service is listed as active (running), Docker is ready to use – otherwise, you can inspect its logs with sudo journalctl -u docker.service --no-pager to diagnose any startup issues.

Run Docker Hello-World (verification)

To verify that Docker Engine is installed and working properly, run the classic hello-world container:

sudo docker run hello-world

This command downloads a test image and runs it in a container.

When the container runs, it prints a Hello from Docker! confirmation message and then exits.

If you see this message, congrats – your Docker installation is successful.

(Optional) Manage Docker as a non-root user

By default, you need to use sudo for all Docker commands. If you’d prefer to run Docker as your normal user (without sudo):

sudo usermod -aG docker $USER

After running this, log out and ssh back in (or reboot) for the group change to take effect.

Once you’ve sshed in again, test it by running docker info or docker run hello-world again – this time without the sudo prefix.

Verify Docker Compose Plugin

With Docker and the Compose plugin installed, you should verify that Docker Compose v2 is working properly.

Run the following command to see if Docker Compose is recognized:

sudo docker compose version

If everything was set up correctly, this will display the Compose plugin’s version, for example:

Docker Compose version v2.x.x

The important part is that you see a version starting with v2, confirming that you have Docker Compose V2 installed as a plugin.

If this command errors, ensure you didn’t skip installing the docker-compose-plugin package, and that you’re either root or in the docker group as mentioned.

Let’s do a quick test by running a container via Docker Compose.

Create a new directory for a test project and inside it, create a file named docker-compose.yml with the following content:

services:
  hello:
    image: hello-world

This YAML defines a Compose application with a single service called hello that uses the official hello-world image. To run this with Docker Compose, execute:

sudo docker compose up

Docker (via the Compose plugin) will pull the hello-world image (if not already pulled) and start the container.

You should see output indicating that Docker is creating and running the container, and then the Hello from Docker! message from the hello-world container.

Compose will manage the container’s lifecycle. Since hello-world exits after printing its message, you’ll likely see Compose stop the container once it’s done.

For example, the output may look like:

[+] Running 2/2
 ✔ Network test_default    Created  0.1s
 ✔ Container test-hello-1  Created  0.1s
Attaching to hello-1
hello-1  |
hello-1  | Hello from Docker!
hello-1  | This message shows that your installation appears to be working correctly.
...
hello-1 exited with code 0

Great! You’ve now confirmed that the docker compose command (Compose V2) works on your server.

The exact naming of the container may vary, but the key is that the Compose command successfully pulled the image and ran the container.

In a real scenario, you might run services that stay running (for example, using an image like nginx and mapping a port), but the hello-world example keeps things simple.

Conclusion and Final Thoughts

By installing Docker via Docker’s official repository, we ensured that we have the latest Docker Engine along with the official Docker Compose plugin. This is the current best practice for Ubuntu installations.

We removed the outdated packages, added Docker’s APT repo, and installed the up-to-date docker-ce packages (which include Compose V2). This approach not only gives you the newest features (like the integrated docker compose command), but also aligns with Docker’s official recommendations.

Going forward, you can easily update Docker using apt update && apt upgrade since it’s coming from the official source.

If you run into any issues or need further help, feel free to revisit this tutorial or reach out for assistance.

If you found value in this tutorial or have any questions or feedback, please don't hesitate to share your thoughts in the discussion section.