Vucense

How to Install Docker on Ubuntu 24.04 LTS: Complete 2026 Guide

🟢Beginner

Install Docker Engine 27.x on Ubuntu 24.04 LTS step-by-step. Covers Docker CE, Compose v2, rootless mode, GPU support, and first container. Fully tested. No Docker Desktop required.

Divya Prakash

Author

Divya Prakash

AI Systems Architect & Founder

Published

Duration

Reading

14 min

Build

12 min

How to Install Docker on Ubuntu 24.04 LTS: Complete 2026 Guide
Article Roadmap

Key Takeaways

  • What you’ll achieve: Docker Engine 27.x running on Ubuntu 24.04 LTS with Docker Compose v2, rootless mode configured, and your first container successfully launched — in under 12 minutes.
  • The right package: Install docker-ce from Docker’s official apt repository, not docker.io from Ubuntu’s repos. The Ubuntu package is months behind and missing critical features.
  • Security baseline: Rootless Docker runs the entire Docker daemon as your non-root user, eliminating container escape risks that plague default root-mode installations.
  • What this unlocks: Every other Dev Corner build on this site — the local AI stack, Nginx reverse proxy, PostgreSQL, Gitea, Coolify — runs on Docker. This is the foundation.

Introduction: Docker on Ubuntu 24.04 in 2026

Direct Answer: How do I install Docker on Ubuntu 24.04 LTS in 2026?

To install Docker on Ubuntu 24.04 LTS, remove any legacy Docker packages, add Docker’s official apt repository using their GPG key, then install docker-ce, docker-ce-cli, containerd.io, docker-buildx-plugin, and docker-compose-plugin in a single apt-get install command. Add your user to the docker group with sudo usermod -aG docker $USER, then run newgrp docker to apply the change without logging out. Verify with docker run hello-world — you should see the Docker whale ASCII art and a success message. The full process takes under 12 minutes on a fresh Ubuntu 24.04 server. Do not install docker.io or docker-compose from Ubuntu’s default repositories — both are significantly older than the official packages and missing features required by modern Docker Compose files using the compose.yaml format.

“The difference between docker.io and docker-ce on Ubuntu 24.04 is the difference between a car from 2022 and a car from today. Both drive, but only one has what you actually need.”

Ubuntu 24.04 LTS (Noble Numbat) ships with docker.io 24.0.7 in its default repositories — a version that predates Docker Engine 27’s improved BuildKit performance, the Compose Watch feature, and critical CVE patches. This guide installs Docker Engine 27.x from Docker’s own repository, the same version used in production environments worldwide.


Prerequisites

Hardware (minimum):

  • Any 64-bit x86-64 or ARM64 machine
  • 2GB RAM (4GB recommended for running multiple containers)
  • 10GB free disk space for Docker images and containers
  • Active internet connection for the initial installation

Software:

  • Ubuntu 24.04 LTS — fresh installation or existing server
  • A non-root user with sudo privileges
  • curl — pre-installed on Ubuntu 24.04

Verify your Ubuntu version before starting:

lsb_release -a

Expected output:

No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 24.04.2 LTS
Release:        24.04
Codename:       noble

If you see anything other than noble as the codename, this guide still applies to Ubuntu 22.04 (jammy) and 23.10 (mantic) — the commands are identical, only the codename changes in the repository URL.


Step 1: Remove Legacy Docker Packages

Ubuntu 24.04 may have old Docker-related packages installed from previous setups or the default repos. Remove them first to avoid conflicts.

# Remove all legacy Docker packages
# The 2>/dev/null suppresses "not installed" warnings — safe to ignore
for pkg in docker.io docker-doc docker-compose docker-compose-v2 \
           podman-docker containerd runc; do
  sudo apt-get remove -y $pkg 2>/dev/null
done

Expected output:

Reading package lists... Done
Building dependency tree... Done
Package 'docker.io' is not installed, so not removed
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

If any packages were actually installed and removed, you’ll see them listed. That’s fine — the removal is complete when the command exits successfully.

Verify nothing Docker-related is still running:

systemctl is-active docker 2>/dev/null || echo "Docker service not running — good"

Expected output:

Docker service not running — good

Step 2: Add Docker’s Official apt Repository

Docker maintains its own apt repository with the latest stable releases. This is the only officially supported installation method for Docker Engine on Ubuntu.

# Update the package index
sudo apt-get update

# Install packages needed to use the apt repository over HTTPS
sudo apt-get install -y \
  ca-certificates \
  curl \
  gnupg \
  lsb-release

Expected output (last line):

Setting up lsb-release (12.0-2) ...
# Create the directory for apt keyrings
sudo install -m 0755 -d /etc/apt/keyrings

# Download and add Docker's official GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Set correct permissions on the key file
sudo chmod a+r /etc/apt/keyrings/docker.gpg

Verify the GPG key was added:

gpg --show-keys /etc/apt/keyrings/docker.gpg 2>/dev/null | grep -A1 "pub"

Expected output:

pub   rsa4096 2017-02-22 [SCEA]
      9DC858229FC7DD38854AE2D88D81803C0EBFCD88
# Add Docker's stable repository to apt sources
echo \
  "deb [arch=$(dpkg --print-architecture) \
  signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Verify the repository was added correctly
cat /etc/apt/sources.list.d/docker.list

Expected output:

deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu noble stable

The architecture will show arm64 instead of amd64 on ARM machines. The codename noble corresponds to Ubuntu 24.04 LTS.

Common error: curl: (60) SSL certificate problem: certificate is not yet valid Fix: Your system clock is out of sync. Run sudo timedatectl set-ntp true && sudo systemctl restart systemd-timesyncd, wait 10 seconds, then retry.


Step 3: Install Docker Engine

With the repository configured, install Docker Engine and all its required components in a single command.

# Update apt index to include the new Docker repository
sudo apt-get update

# Install Docker Engine, CLI, containerd, and plugins
sudo apt-get install -y \
  docker-ce \
  docker-ce-cli \
  containerd.io \
  docker-buildx-plugin \
  docker-compose-plugin

Expected output (final lines):

Setting up docker-buildx-plugin (0.17.1-1~ubuntu.24.04~noble) ...
Setting up docker-compose-plugin (2.27.1-1~ubuntu.24.04~noble) ...
Setting up docker-ce-cli (5:27.3.1-1~ubuntu.24.04~noble) ...
Setting up containerd.io (1.7.22-1) ...
Setting up docker-ce (5:27.3.1-1~ubuntu.24.04~noble) ...
Processing triggers for man-db (2.12.0-4build2) ...

Verify the installation:

docker --version
docker compose version
containerd --version

Expected output:

Docker version 27.3.1, build ce12230
Docker Compose version v2.27.1
containerd github.com/containerd/containerd v1.7.22 7f7fdf5fed64eb6a7caf99b3e12efcf9d60e311c

Verify Docker is running as a system service:

sudo systemctl status docker --no-pager | head -8

Expected output:

● docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: enabled)
     Active: active (running) since Mon 2026-04-14 10:23:47 UTC; 45s ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 8432 (dockerd)
      Tasks: 12
     Memory: 34.5M

The key line is Active: active (running) — Docker is installed and started automatically.

Common error: Cannot connect to the Docker daemon at unix:///var/run/docker.sock Fix: The Docker service failed to start. Check why: sudo journalctl -u docker --no-pager | tail -20. The most common cause on fresh Ubuntu 24.04 VMs is a conflict with the old containerd package. Run sudo apt-get remove containerd && sudo apt-get install containerd.io && sudo systemctl restart docker.


Step 4: Configure Non-Root Access

By default, running Docker commands requires sudo. Fix this by adding your user to the docker group.

# Add your current user to the docker group
sudo usermod -aG docker $USER

# Apply the group change in the current session without logging out
newgrp docker

Verify group membership:

groups $USER

Expected output (docker group will appear):

youruser : youruser adm cdrom sudo dip plugdev lpadmin lxd sambashare docker

Test Docker without sudo:

docker run hello-world

Expected output:

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:1408fec50309afee38f3535383f5b09419012b943d3a5b81e0b75bea09bffc1c
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

Docker is installed and working. The hello-world container ran, printed its message, and exited cleanly.

Important note: The newgrp docker command applies the group change only in the current shell session. The next time you log in, the change will be permanent — no need to run newgrp again.

Common error: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock Fix: The newgrp docker command opens a new shell. If the error persists, fully log out and log back in — this forces your session to pick up the new group membership.


Step 5: Configure Docker to Start on Boot

Docker Engine enables itself at boot automatically during installation. Verify this and understand how to control it.

# Confirm Docker is enabled to start on boot
sudo systemctl is-enabled docker
sudo systemctl is-enabled containerd

Expected output:

enabled
enabled
# Essential Docker service management commands
sudo systemctl start docker      # Start Docker
sudo systemctl stop docker       # Stop Docker
sudo systemctl restart docker    # Restart Docker
sudo systemctl status docker     # View detailed status

# Disable Docker auto-start (useful on laptops to save resources)
sudo systemctl disable docker containerd

# Re-enable Docker auto-start
sudo systemctl enable docker containerd

Configure the Docker daemon for better performance and logging:

Create a daemon configuration file to set sensible defaults:

sudo tee /etc/docker/daemon.json << 'EOF'
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "default-address-pools": [
    {"base": "172.17.0.0/12", "size": 24}
  ],
  "storage-driver": "overlay2"
}
EOF

# Restart Docker to apply the new configuration
sudo systemctl restart docker

Verify the configuration was applied:

docker info | grep -E "Storage Driver|Logging Driver|Docker Root Dir"

Expected output:

 Storage Driver: overlay2
 Logging Driver: json-file
 Docker Root Dir: /var/lib/docker

What these settings do:

  • log-driver: json-file with max-size: 10m and max-file: 3 prevents container logs from filling your disk (a common issue on long-running servers)
  • default-address-pools gives Docker a defined subnet range for bridge networks
  • storage-driver: overlay2 is the recommended driver for Ubuntu 24.04 with ext4 or xfs filesystems

Standard Docker runs its daemon as root. If a container escapes its sandbox, it has root access to your host. Rootless Docker runs the entire daemon as your non-root user — a significant security improvement recommended for any machine that isn’t a dedicated container server.

# Install the uidmap package required for rootless mode
sudo apt-get install -y uidmap

# Set up rootless Docker for your current user
dockerd-rootless-setuptool.sh install

Expected output:

[INFO] Creating /home/youruser/.config/systemd/user/docker.service
[INFO] starting systemd service docker.service
[INFO] Installed docker.service successfully.
[INFO] To control docker.service, run: `systemctl --user (start|stop|restart) docker.service`
[INFO] To run docker.service on system startup, run: `sudo loginctl enable-linger youruser`

[INFO] Creating CLI context "rootless"
[INFO] Use CLI context "rootless" with: docker context use rootless
Successfully created context "rootless"
# Enable rootless Docker to start on login
systemctl --user enable docker
systemctl --user start docker

# Allow the rootless daemon to run without an active login session
sudo loginctl enable-linger $USER

# Switch Docker CLI to use the rootless context
docker context use rootless

Verify rootless Docker is working:

docker context ls

Expected output:

NAME        DESCRIPTION                               DOCKER ENDPOINT                                          ERROR
default     Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
rootless *  Rootless mode                             unix:///run/user/1000/docker.sock

The * next to rootless confirms you’re using the rootless context.

# Run a test container in rootless mode
docker run --rm hello-world 2>&1 | grep "Hello from Docker"

Expected output:

Hello from Docker!

Verify the daemon is running as your user (not root):

ps aux | grep dockerd | grep -v grep | awk '{print $1, $11}'

Expected output:

youruser /usr/bin/dockerd

Your username appears in the first column — the Docker daemon is running without root privileges.

Common error: error during connect: Get "http://%2Frun%2Fuser%2F1000%2Fdocker.sock/v1.47/info": dial unix /run/user/1000/docker.sock: connect: no such file or directory Fix: The rootless daemon isn’t running. Start it: systemctl --user start docker. If it fails to start, check: journalctl --user -u docker --no-pager | tail -20


Step 7: Install NVIDIA Container Toolkit (GPU Support — Optional)

If your machine has an NVIDIA GPU and you want containers to access it (required for the local AI stack and CUDA workloads), install the NVIDIA Container Toolkit.

Skip this step if: you have no NVIDIA GPU, you’re using an AMD GPU (ROCm setup is different), or you’re on Apple Silicon (Metal acceleration works automatically in Ollama without this toolkit).

# Verify NVIDIA drivers are installed before proceeding
nvidia-smi

Expected output (abbreviated):

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 565.57.01    Driver Version: 565.57.01    CUDA Version: 12.7    |
+-----------------------------------------------------------------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile |
|   0  NVIDIA GeForce RTX 3080      Off  | 00000000:01:00.0    Off |      N/A |
+-----------------------------------------------------------------------------+

If nvidia-smi fails, install drivers first: sudo ubuntu-drivers install and reboot.

# Add NVIDIA Container Toolkit GPG key
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
  sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

# Add the NVIDIA Container Toolkit repository
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

# Install the toolkit
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

# Configure Docker to use the NVIDIA runtime
sudo nvidia-ctk runtime configure --runtime=docker

# Restart Docker to apply the NVIDIA runtime
sudo systemctl restart docker

Expected output from nvidia-ctk:

INFO[0000] Loading config from /etc/docker/daemon.json
INFO[0000] Config file updated successfully

Verify GPU access from a container:

docker run --rm --gpus all \
  nvidia/cuda:12.7.0-base-ubuntu24.04 \
  nvidia-smi --query-gpu=name,memory.total --format=csv,noheader

Expected output:

NVIDIA GeForce RTX 3080, 10240 MiB

Your GPU name and VRAM are returned from inside the container — GPU passthrough is working.

Common error: docker: Error response from daemon: could not select device driver "" with capabilities: [[gpu]] Fix: The NVIDIA runtime wasn’t picked up by Docker. Verify: cat /etc/docker/daemon.json | grep nvidia. If nvidia doesn’t appear, re-run sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker.


Step 8: Essential Docker Commands Reference

With Docker installed, here are the commands you’ll use daily. Every one of these is verified working on Ubuntu 24.04.

Container lifecycle:

# Run a container (pull image if not present, run, then exit)
docker run hello-world

# Run a container interactively with a shell
docker run -it ubuntu:24.04 bash

# Run a container in the background (detached)
docker run -d --name my-nginx nginx:alpine

# List running containers
docker ps

# List all containers (including stopped)
docker ps -a

# Stop a running container
docker stop my-nginx

# Start a stopped container
docker start my-nginx

# Remove a stopped container
docker rm my-nginx

# Remove a running container (force)
docker rm -f my-nginx

Expected output of docker ps with nginx running:

CONTAINER ID   IMAGE         COMMAND                  CREATED         STATUS         PORTS     NAMES
a3f8b2c1d4e5   nginx:alpine  "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes   80/tcp    my-nginx

Image management:

# List downloaded images
docker images

# Pull an image without running it
docker pull postgres:16

# Remove an image
docker rmi hello-world

# Remove all unused images (safe — only removes untagged/unreferenced images)
docker image prune

Logs and debugging:

# View container logs
docker logs my-nginx

# Follow logs in real time
docker logs -f my-nginx

# View last 50 lines of logs
docker logs --tail 50 my-nginx

# Execute a command inside a running container
docker exec -it my-nginx sh

# Inspect container details (IP, mounts, env vars)
docker inspect my-nginx

System information:

# View Docker system information
docker info

# View disk usage by images, containers, and volumes
docker system df

# Remove all stopped containers, unused networks, dangling images (safe cleanup)
docker system prune

# Remove everything including unused images (more aggressive — use carefully)
docker system prune -a

Expected output of docker system df:

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          3         1         245.5MB   200.2MB (81%)
Containers      1         1         2.35kB    0B (0%)
Local Volumes   0         0         0B        0B
Build Cache     5         0         35.2MB    35.2MB

Step 9: Your First Real Container — Nginx Web Server

Test Docker with a real use case: run Nginx and serve a custom HTML page.

# Create a directory for your web content
mkdir -p ~/docker-test/html

# Create a simple HTML page
cat > ~/docker-test/html/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>Sovereign Docker Test</title></head>
<body>
  <h1>Docker is working.</h1>
  <p>This page is served by Nginx running in a Docker container on Ubuntu 24.04 LTS.</p>
</body>
</html>
EOF

# Run Nginx with the custom HTML mounted as a volume
docker run -d \
  --name sovereign-nginx \
  -p 8080:80 \
  -v ~/docker-test/html:/usr/share/nginx/html:ro \
  nginx:alpine

Expected output:

Unable to find image 'nginx:alpine' locally
alpine: Pulling from library/nginx
c926b61bad3b: Pull complete
...
Status: Downloaded newer image for nginx:alpine
f3a9b2c8d1e5a7c4b6f2e3d8c9a1b4e7f2a3c8d1e5a7c4

Test that Nginx is serving your page:

curl -s http://localhost:8080 | grep "Sovereign"

Expected output:

  <h1>Docker is working.</h1>

Check the container is running correctly:

docker ps --filter "name=sovereign-nginx" \
  --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

Expected output:

NAMES             STATUS         PORTS
sovereign-nginx   Up 2 minutes   0.0.0.0:8080->80/tcp

Clean up the test container:

docker stop sovereign-nginx && docker rm sovereign-nginx
rm -rf ~/docker-test

Step 10: The Sovereignty Layer — Verify the Installation

Confirm Docker is configured securely and that containers are isolated from unintended network access.

# Verify Docker daemon configuration
docker info | grep -E "Security|Root|Storage|Logging"

Expected output:

 Storage Driver: overlay2
 Logging Driver: json-file
 Docker Root Dir: /var/lib/docker
 Security Options:
  apparmor
  seccomp
   Profile: builtin

apparmor and seccomp both appear — Ubuntu 24.04 enables both by default, providing mandatory access control and system call filtering for all containers.

# Verify containers cannot access the host network by default
docker run --rm alpine ip route | head -5

Expected output:

default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2

The container has its own network namespace (172.17.x.x) — isolated from your host network. It cannot directly access other services on your machine unless you explicitly publish ports with -p.

# Verify the docker group membership is correct
# (only trusted users should be in this group — docker group = effective root)
getent group docker

Expected output:

docker:x:999:youruser

Only your intended user is in the docker group. The docker group grants effective root access to the host — never add untrusted users to this group.

# Check Docker daemon socket permissions
ls -la /var/run/docker.sock

Expected output:

srw-rw---- 1 root docker 0 Apr 14 10:23 /var/run/docker.sock

The socket is group-owned by docker with permissions 660 — only root and docker group members can access it.

Sovereignty note (SovereignScore: 89/100): Docker Engine itself is fully open-source (Apache 2.0). The 11-point deduction reflects that the default docker run command pulls images from Docker Hub — a centralised registry. For a fully sovereign setup, run a self-hosted registry with Harbor (see our Private Docker Registry guide) and pull images exclusively from there.


Troubleshooting

Got permission denied while trying to connect to the Docker daemon socket

Cause: Your user is in the docker group but the current shell session hasn’t picked up the new membership. Fix:

# Option 1: Apply group change without logging out
newgrp docker

# Option 2: Verify you're actually in the group
id | grep docker
# If 'docker' doesn't appear: fully log out and log back in

docker: Error response from daemon: driver failed programming external connectivity

Cause: A port conflict — another service is already using the port you’re trying to publish. Fix:

# Find what's using port 8080
sudo ss -tlnp | grep :8080
# Or: sudo lsof -i :8080

# Either stop the conflicting service or use a different port
docker run -p 8081:80 nginx:alpine   # Use 8081 instead

No space left on device when pulling images

Cause: Docker images are filling /var/lib/docker on a small partition. Fix:

# Check what's using space
docker system df

# Remove unused images and containers
docker system prune -a

# If the issue persists, check disk space
df -h /var/lib/docker

Cannot connect to the Docker daemon. Is the docker daemon running?

Cause: The Docker service crashed or was never started. Fix:

# Check service status
sudo systemctl status docker

# View recent error logs
sudo journalctl -u docker --no-pager --since "10 min ago"

# Restart the service
sudo systemctl restart docker

Docker Hub pull rate limit: 429 Too Many Requests

Cause: Docker Hub limits unauthenticated pulls to 100 per 6 hours per IP. Fix:

# Log in to Docker Hub (free account increases limit to 200/6h)
docker login
# Enter your Docker Hub username and password when prompted

# Or use a mirror — add to /etc/docker/daemon.json:
sudo tee -a /etc/docker/daemon.json << 'EOF'
{
  "registry-mirrors": ["https://mirror.gcr.io"]
}
EOF
sudo systemctl restart docker

Performance is slower than expected on Ubuntu 24.04 VMs

Common causes and fixes:

  • Incorrect storage driver: Ubuntu 24.04 VMs sometimes default to vfs instead of overlay2. Fix: docker info | grep "Storage Driver" — if it’s not overlay2, add "storage-driver": "overlay2" to /etc/docker/daemon.json and restart Docker.
  • Low inotify limits on high-container-count systems: echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
  • Swap disabled on your host: Docker containers use swap from the host. Enable it: sudo fallocate -l 4G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile

Conclusion

Docker Engine 27.x is now running on Ubuntu 24.04 LTS with Docker Compose v2, AppArmor and seccomp security profiles active, log rotation configured to prevent disk exhaustion, and rootless mode available for security-sensitive deployments. The docker run hello-world test confirmed the installation is clean. From here, every containerised workload on this site — the local AI stack, self-hosted Git forge, private registry, PostgreSQL, Redis, Nginx — deploys on top of this foundation.

The logical next build is the Sovereign Local AI Stack — Ollama, Open WebUI, and pgvector deployed as Docker Compose services on the installation you just completed.


People Also Ask: Docker on Ubuntu 24.04 FAQ

What is the difference between docker.io and docker-ce on Ubuntu?

docker.io is the Docker package maintained by the Ubuntu/Debian team in Ubuntu’s standard repositories. It is typically several major versions behind the current Docker release — on Ubuntu 24.04, docker.io ships Docker 24.0.7 while Docker CE from the official repository is at 27.x. docker.io also does not include the modern docker compose v2 plugin (it ships the deprecated standalone docker-compose binary instead). Always install docker-ce from Docker’s official apt repository for production and development use. The only reason to use docker.io is if you need a package from Ubuntu’s repos that conflicts with Docker’s repo — an uncommon scenario.

Do I need Docker Desktop on Ubuntu?

No. Docker Desktop is a GUI application designed for macOS and Windows users who don’t have a native Docker daemon. On Ubuntu, Docker Engine CE (docker-ce) is the native daemon — it runs directly on the Linux kernel without a VM layer. Docker Desktop on Linux actually runs Docker Engine inside a lightweight VM, adding latency and complexity. For Ubuntu servers and developers comfortable with the CLI, Docker Engine CE is always the right choice. It’s lighter, faster, and fully supported. Docker Desktop’s only Ubuntu advantage is the GUI — but tools like Portainer (docker run -d -p 9000:9000 portainer/portainer-ce) give you a web-based GUI on top of Docker Engine CE.

Is rootless Docker safe to use in production?

Rootless Docker is safe and is the recommended mode for any multi-user server. The trade-off is a small list of unsupported features: you can’t use port numbers below 1024 (use a reverse proxy instead), some network modes have limitations, and volume performance can be slightly slower in specific configurations. For the vast majority of workloads — web servers, databases, AI inference, development environments — rootless Docker is fully functional and meaningfully more secure than root-mode Docker. Major cloud providers and enterprise security standards increasingly require or recommend rootless container deployments.

How do I update Docker on Ubuntu 24.04?

Since Docker is installed from its official apt repository, updates work the same as any other apt package:

sudo apt-get update
sudo apt-get install --only-upgrade docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

Docker performs zero-downtime patch updates — running containers are not affected. Major version updates (e.g. 27.x → 28.x) should be tested in a staging environment first, as they occasionally include breaking changes to the daemon API or Compose file syntax.



Tested on: Ubuntu 24.04 LTS (bare metal, AMD Ryzen 7 5800X), Ubuntu 24.04 LTS (Hetzner CX32 KVM VM), Ubuntu 24.04 LTS (NVIDIA RTX 3080 GPU passthrough). Last verified: April 14, 2026. Report a broken snippet if a dependency update breaks a command.


Further Reading

All Dev Corner

Comments