ProdCraft
Subscribe
Docker

Install Docker Engine and Compose on Ubuntu: A Safe Step-by-Step Guide

Install Docker Engine and Compose on Ubuntu using the official apt repository, verify containers and avoid common permission mistakes.

ProdCraft5 min read
ProdCraft featured image — Install Docker Engine and Compose on Ubuntu: A Safe Step-by-Step Guide

Install Docker Engine and Compose on Ubuntu: A Safe Step-by-Step Guide addresses a problem that appears when teams move from working containers to dependable services. This tutorial gives commands, a realistic example, failure diagnosis and safe operational practices. Perform privileged commands only on infrastructure you administer. Use documented, supported component versions and test against non-production data first.

Choose the installation method

There are multiple Docker packages with similar names. For a reproducible Ubuntu server installation, use Docker’s documented apt repository rather than downloading an unreviewed shell installer. Check cat /etc/os-release and CPU architecture first. Back up containers and volumes before changing an existing host, and remove conflicting distribution packages only after confirming the package list in the official instructions. A fresh VM is safer for following along. Keep one SSH session for administration and enough disk capacity for image layers.

Set up the signed apt source

Run sudo apt update and sudo apt install ca-certificates curl. Create /etc/apt/keyrings, download Docker’s GPG key from https://download.docker.com/linux/ubuntu/gpg into /etc/apt/keyrings/docker.asc, and make it readable. Create a deb822 source file at /etc/apt/sources.list.d/docker.sources referencing the official Ubuntu Docker package URI, your Ubuntu codename, stable component, machine architecture and Signed-By path. Use the full commands from Docker’s continuously maintained instructions when your version changes; do not substitute an arbitrary online mirror.

Install the Engine and Compose plugin

After sudo apt update, install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin using apt. These packages supply the daemon, client, container runtime integration, Buildx and Compose V2. Validate with sudo systemctl status docker, sudo docker version and sudo docker compose version. Compose V2 is invoked as docker compose with a space; many legacy articles use the old hyphenated command. If the installation fails, check your distribution codename and apt policy before randomly installing mismatched packages.

Run and inspect a first container

Try sudo docker run --rm hello-world to verify client-to-daemon operation and image downloading. Then start a disposable web container with sudo docker run --rm -d --name demo-web -p 127.0.0.1:8080:80 nginx:alpine; test curl http://127.0.0.1:8080 from the host. Inspect sudo docker ps, sudo docker logs demo-web, then stop it with sudo docker stop demo-web. Binding the published port to loopback intentionally avoids exposing a practice server to the internet.

Explain Docker socket permissions

Running Docker without sudo usually means adding your user to the docker group, but that group effectively grants root-level power through the daemon. Treat membership as privileged access, not as a harmless convenience. For a shared workstation, consider rootless Docker if its networking and storage limitations fit. A common “permission denied while trying to connect to the Docker daemon socket” error means the client cannot access the socket or the service isn’t running. Check permissions and daemon state rather than applying chmod 666 to the socket.

Prepare the host for real workloads

Docker does not replace host hardening, backups or resource planning. Set policies for registry credentials, disk usage, image pinning, log retention, restart behavior and persistent volume backups. Test firewall behavior carefully: published Docker ports interact with firewall rules and may bypass assumptions about UFW policy. Audit which bind addresses are public. Pin images or digests for controlled rollouts and review Docker Engine upgrades in staging. A quick hello-world test proves the installation, not that a host is secure to operate production applications.

How to validate the installation end to end

Do not stop at a successful command. Record the starting state and build an observable test that crosses the same interface your users rely on. First inspect process or container status; then verify connectivity; then exercise one representative application operation and examine its logs. Repeat after a restart or recreation. If data should persist, explicitly test persistence; if credentials should not be exposed, search logs and image settings for leaks. This separates “Docker accepted my configuration” from “the service behaves correctly.”

Check which addresses and ports are actually reachable from another machine, not just from inside the container. Verify whether the example uses a local-only bind, a development credential, or a disposable volume so nobody accidentally carries a lab shortcut into production. Record the exact command and version that produced each result for future troubleshooting.

Deployment review and rollback notes

Before promoting a container change, specify the image digest, configuration version, persistent storage ownership, expected resource use and recovery trigger. Make one meaningful change at a time. Build in CI, deploy to a test environment, and automate a health or smoke test that would have caught a previous incident. If a release degrades, return to the last known-good image and configuration; database migrations require their own backward-compatibility plan. Keep Docker daemon access restricted, log secret-free operational events and update the runbook as soon as a new failure mode appears.

Frequently asked questions

Should I use the same commands on a production host?

Use them as an understood starting point, not an unreviewed production script. Confirm image tags, daemon version, persistence rules, access boundaries and backup availability. Some examples deliberately bind ports to localhost to prevent accidental exposure.

What if the container starts but the application fails?

Inspect container logs, the real application listening address, environment variables and service dependencies. Check whether the failing request reaches the application before changing network rules or restarting unrelated services.

Official references and further reading

Use the upstream Docker documentation for this topic as the primary source for version-specific details. For a practical application example, compare our Python RAG chatbot implementation, which also benefits from repeatable images, secure configuration and observable deployment processes.

Reproducible installation check

After installing, create a text file named compose.yaml with a single service using image: nginx:alpine and ports: ["127.0.0.1:8080:80"], expressed as properly indented YAML. Run docker compose config to see the normalized service definition, then docker compose up -d and request http://127.0.0.1:8080. Finally run docker compose down. This verifies not only the daemon but also the Compose plugin and ordinary service DNS/network lifecycle.

Check that the demo did not leave a published public port or important data volume. Never delete unknown volumes as part of routine cleanup. Next, build a realistic app with the FastAPI container guide.

Share this article

Written by

ProdCraft

ProdCraft publishes practical engineering guides on DevOps, Kubernetes, Docker, Linux, databases, cloud and LLMs — written for engineers who ship.

227 articles View profile

The weekly ops & AI brief

One email a week with the best new guides, tools and incident write-ups. No spam, unsubscribe anytime.

Leave a Reply

Your email address will not be published. Required fields are marked *