How to Install Docker on Ubuntu (the Correct, Production-Ready Way)

Most “install Docker on Ubuntu” instructions you will find online are subtly wrong, or right for the wrong reasons. They tell you to run `apt install docker.io` because it is one line, or to pipe a script from the internet into your root shell because it is fast. Both work, in the narrow sense that you end up with *a* Docker installation. Neither gives you the version control, predictable upgrade path, or component set that a production host actually needs.

This guide installs Docker the way Docker itself documents: from Docker’s official apt repository. You get the latest `docker-ce`, the matching CLI, the `containerd.io` runtime, and the modern Compose and Buildx plugins, all managed through `apt` and all upgradable with `apt upgrade`. We will also cover the post-install steps that are routinely skipped, and one security decision (the docker group) that people make without understanding what they are granting.

Key Takeaways
Use Docker’s official apt repository. It is the only method that gives you current versions plus clean, repeatable upgrades through `apt`.
Avoid the `docker.io` package from Ubuntu’s default repos (frequently outdated) and the convenience `get.docker.com` script for production (less control over what lands on the host).
Remove conflicting old packages first (`docker.io`, `docker-engine`, `containerd`) or the install will fight itself.
Adding your user to the `docker` group is effectively granting root. It is standard and convenient, but treat it as a root grant and never add untrusted users.
Compose is now a plugin: the command is `docker compose` (v2), not the legacy `docker-compose` binary.

Why does the installation method matter so much?

The method you choose determines three things: which version of Docker you get, how you upgrade it later, and how much you can see and control about what is being installed. On a laptop these barely matter. On a server that will run real workloads, they matter a great deal.

Ubuntu ships a package called `docker.io` in its universe repository. It is maintained by Ubuntu, not by Docker, and it tends to lag behind upstream by months. You may install it today and find that a feature documented in the current Docker manual simply does not exist in your version. The package also bundles components in ways that do not match Docker’s own component split, which makes following official documentation frustrating.

The convenience script at `get.docker.com` is the opposite problem. It is current, but it is a shell script that detects your distribution and runs privileged commands on your behalf. Docker explicitly recommends it only for quick, non-production setups. For a host you care about, piping a remote script into `sh` as root removes the deliberate, auditable step where *you* decide what enters the system.

The official repository method asks a little more of you up front, and returns control in exchange.

Install methods compared

Method Command (summary) Version freshness Upgrade path Control / auditability Recommended for
Official apt repo Add GPG key + repo, `apt install docker-ce …` Current (upstream) `apt upgrade`, clean High Production and most use cases
apt default (`docker.io`) `apt install docker.io` Often outdated `apt upgrade` Medium Quick throwaway tests only
Convenience script `curl -fsSL https://get.docker.com \ sh` Current Reinstalls / script-managed Low Fast dev sandboxes, not production
Snap `snap install docker` Varies Snap auto-refresh Low (confinement quirks) Rarely; confinement causes surprises

How do you install Docker from the official repository?

The procedure below targets current Ubuntu releases (22.04 LTS, 24.04 LTS, and newer). Run each block and read what it does before pressing Enter.

Step 1: Remove conflicting packages

If any old or distro-packaged Docker components are present, remove them first. Leaving them in place is the single most common cause of a broken install.

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

This will not error if a package is not installed. It simply clears the field. Note that your images and volumes in `/var/lib/docker/` are left untouched by `apt remove`; only the packages go.

Step 2: Install prerequisites and add Docker’s GPG key

Update the index and install the tools needed to fetch over HTTPS and manage the signing key:

“`bash sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg

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 “`

The GPG key is what lets `apt` verify that packages genuinely come from Docker and were not tampered with in transit. Storing it under `/etc/apt/keyrings/` (rather than the deprecated `apt-key` mechanism) is the current, correct convention.

Step 3: Add the Docker repository

This line tells `apt` where to find Docker’s packages and pins them to the signing key you just installed:

“`bash echo \ “deb [arch=$(dpkg –print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo “$VERSION_CODENAME”) stable” | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null “`

The `$(dpkg –print-architecture)` and `$(. /etc/os-release && echo “$VERSION_CODENAME”)` substitutions make this portable: it resolves your actual CPU architecture (e.g. `amd64`, `arm64`) and your release codename (e.g. `noble`, `jammy`) so you are not hardcoding values that break on the next machine.

Step 4: Install Docker Engine and its components

Refresh the index so it picks up the new repository, then install the full set:

“`bash sudo apt-get update sudo apt-get install -y \ docker-ce \ docker-ce-cli \ containerd.io \ docker-buildx-plugin \ docker-compose-plugin “`

Install all five of these explicitly. Here is what each one is:

  • `docker-ce` — the Docker Engine daemon (the thing that actually runs containers).
  • `docker-ce-cli` — the `docker` command-line client you type against.
  • `containerd.io` — the lower-level container runtime the engine builds on.
  • `docker-buildx-plugin` — the modern `docker build` backend (BuildKit / `docker buildx`).
  • `docker-compose-plugin` — Compose v2, invoked as `docker compose`.

A deliberate point on Compose: the package is `docker-compose-plugin`, and the command is two words, `docker compose`. The old Python tool was a separate hyphenated binary, `docker-compose`. They are not the same program. Tutorials still floating around tell you to `apt install docker-compose`, which pulls the legacy v1 tool. Install the plugin, use `docker compose`, and ignore anything that hyphenates it.

What must you do after installation?

Installing the packages is not the finish line. Three post-install steps separate a working setup from a confusing one.

Start and enable the service

On most Ubuntu installs the Docker service starts automatically, but make it explicit and ensure it survives reboots:

“`bash sudo systemctl enable –now docker “`

`enable –now` does two jobs in one command: enable registers Docker to start at boot, and `–now` starts it immediately. Confirm it is healthy:

“`bash sudo systemctl status docker “`

Verify with the hello-world container

This is the canonical smoke test. It pulls a tiny image, runs it, and prints a confirmation message:

“`bash sudo docker run hello-world “`

If you see “Hello from Docker!”, the daemon, the runtime, the CLI, and your network path to the registry all work. This single command exercises the entire stack end to end.

Run docker without sudo (read the caveat first)

By default the Docker socket is owned by `root`, so non-root users must prefix every command with `sudo`. The standard fix is to add your user to the docker group:

“`bash sudo usermod -aG docker $USER “`

Group membership is only re-evaluated at login, so you must log out and back in (or run `newgrp docker` in the current shell) for it to take effect. After that, `docker run hello-world` works without `sudo`.

Before you run that command, understand exactly what it grants — covered next.

Why is adding your user to the docker group the same as giving root?

This is the part most guides mention as a throwaway convenience and never explain. It deserves a clear statement:

Membership in the `docker` group is functionally equivalent to root access on the machine.

The reasoning is concrete, not theoretical. The Docker daemon runs as root. Anyone who can talk to the Docker socket can ask that root daemon to do root things. Specifically, a user in the docker group can start a container that mounts the host’s root filesystem into the container — for example:

“`bash docker run -v /:/host -it ubuntu chroot /host “`

From inside that container they have read/write access to the entire host filesystem as root: they can edit `/etc/shadow`, drop a SUID binary, or add themselves to `sudoers`. No password prompt, no audit trail beyond the Docker logs.

The practical conclusion:

  • For your own administrative account on a host you control, docker-group membership is standard and acceptable. You already had root.
  • Never add untrusted or low-privilege users to the docker group expecting it to be a limited grant. It is not. Treat it as handing that user root on the box.
  • On shared or multi-tenant systems, consider rootless Docker or restricting socket access instead.

Convenient, ubiquitous, and absolutely root-equivalent. Decide with that in mind.


Running Docker properly needs real root and real resources

Installing Docker the official way assumes you actually have full root access to the host — which is exactly what shared hosting does not give you. To install Docker from its official repository, manage `containerd`, control the docker group, and run Compose stacks, you need a server you fully own.

DarazHost provides VPS and dedicated servers with complete root access, so you can install Docker the correct way, run containers and multi-service Compose stacks, and allocate the CPU, RAM, and SSD storage that containerized applications demand. With reliable infrastructure, fast SSD storage, and 24/7 support, it is a sound base for production container workloads rather than fragile experiments.


How do you diagnose the common failures?

Three failure modes account for the overwhelming majority of “Docker isn’t working” reports on Ubuntu. Each has a precise cause and fix.

“permission denied while trying to connect to the Docker daemon socket”

The full message references `/var/run/docker.sock` and a permissions error. It means your user is not a member of the docker group, or you added yourself but have not logged out and back in since.

“`bash

groups

“`

Fix: run `sudo usermod -aG docker $USER`, then fully log out and back in (a new SSH session counts; reusing an old one does not). As a one-shot in the current shell, `newgrp docker` activates the group without re-login.

“Cannot connect to the Docker daemon … Is the docker daemon running?”

This is not a permissions issue — the service is not running. Start and enable it:

“`bash sudo systemctl enable –now docker sudo systemctl status docker “`

If `status` shows a failure, inspect the journal for the real cause:

“`bash sudo journalctl -u docker –no-pager -n 50 “`

Conflicting old packages cause install or runtime errors

If you skipped Step 1, a previously installed `docker.io`, `docker-engine`, or distro `containerd` can collide with the official packages and produce confusing dependency or socket errors. Remove the old ones, then reinstall the official set:

“`bash sudo apt-get remove -y docker.io docker-engine containerd runc sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io \ docker-buildx-plugin docker-compose-plugin “`

Remember that `apt remove` preserves `/var/lib/docker/`, so your images and volumes survive the swap.

Frequently asked questions

Should I ever use `apt install docker.io` instead? Only for a disposable test environment where you do not care about the version. For anything you will maintain, the official repository gives you current releases and clean upgrades. The two are not interchangeable in practice.

What is the difference between `docker compose` and `docker-compose`? `docker compose` (space) is Compose v2, a Go plugin installed via `docker-compose-plugin` and maintained as the current version. `docker-compose` (hyphen) is the legacy Python v1 tool. Use the plugin and the spaced command; the hyphenated binary is end-of-life.

Do I have to add my user to the docker group? No. It is a convenience so you can drop `sudo`. If you skip it, simply prefix commands with `sudo`. Because the group is root-equivalent, skipping it is the more conservative choice on shared machines.

How do I upgrade Docker later? Because you installed from the apt repository, upgrades are ordinary package upgrades: `sudo apt-get update && sudo apt-get upgrade`. There is no script to re-run and no manual version juggling.

Does this work on Ubuntu 22.04 and 24.04? Yes. The repository line resolves your release codename automatically via `$(. /etc/os-release && echo “$VERSION_CODENAME”)`, so the same commands work across supported LTS releases without edits.

Conclusion

Installing Docker on Ubuntu correctly is not harder than the shortcuts — it is just more deliberate. Use Docker’s official apt repository for current versions and clean upgrades. Remove conflicting packages first. Install the full component set, including the Buildx and Compose plugins. Then enable the service, verify with `hello-world`, and decide about the docker group with full knowledge that it grants root. Do those things and you have an installation you can trust under real workloads, not one you will be quietly fighting later.

About the Author

Leave a Reply