How to Install Docker on Ubuntu and Debian (Complete Guide)

✅ Tested on Ubuntu 24.04 LTS, Ubuntu 22.04, Debian 12 — Last updated: June 2026
Docker on Linux is the standard way to run containerized applications, and installing it correctly is essential for anyone working with modern software development or running local AI tools. This guide covers installing Docker Engine on Ubuntu and Debian, the Docker Compose plugin, common post-installation steps, and everything you need to start running containers immediately.
Table of Contents
- What Is Docker and Why Use It
- Install Docker on Ubuntu 24.04 / 22.04
- Install Docker on Debian 12
- Post-Installation Steps
- Docker Compose
- Essential Docker Commands
- Volumes and Persistent Data
- Docker Networking
- Writing a Dockerfile
- Troubleshooting
- FAQ
What Is Docker and Why Use It
Docker packages applications and their dependencies into containers — lightweight, isolated environments that run consistently everywhere. Unlike virtual machines, containers share the host OS kernel, making them fast to start and efficient with memory.
Why Docker matters for Linux users:
- Run any software without conflicts: Different apps with different Python/Node/Java versions, all isolated from each other
- Works the same everywhere: A container that works on your laptop works on a server or VPS
- Essential for AI tools: Open WebUI, LocalAI, Stable Diffusion WebUI — all have official Docker images that are the easiest installation method
- Easy cleanup: Remove a container and all its files vanish. No leftover config files or conflicting libraries
- Reproducible environments: Docker Compose files define your entire stack as code
Install Docker on Ubuntu 24.04 / 22.04
Do not install Docker from the Ubuntu snap store or the default Ubuntu repositories — those versions are outdated. Use Docker's official repository for the latest version.
Step 1: Remove Old Versions
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
sudo apt remove $pkg -y 2>/dev/null
doneStep 2: Set Up the Repository
# Install prerequisites
sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release
# Add Docker's GPG key
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
# Add the Docker repository
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
sudo apt updateStep 3: Install Docker Engine
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginStep 4: Verify Installation
sudo docker run hello-world
# Expected output:
# Hello from Docker!
# This message shows that your installation appears to be working correctly.Install Docker on Debian 12
# Install prerequisites
sudo apt update
sudo apt install -y ca-certificates curl gnupg
# Add Docker's GPG key
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository (Debian uses different URL)
echo
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc]
https://download.docker.com/linux/debian
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" |
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginPost-Installation Steps
Run Docker Without sudo
By default, Docker requires root. Add your user to the docker group to run containers without sudo:
sudo usermod -aG docker $USER
# Apply the group change (log out and back in, or run):
newgrp docker
# Test:
docker run hello-world # no sudo needed now⚠️ Security note: The docker group gives root-equivalent access. Only add trusted users.
Start Docker on Boot
sudo systemctl enable docker
sudo systemctl enable containerd
sudo systemctl status dockerConfigure Log Rotation
Docker container logs can grow very large. Configure log rotation to prevent disk exhaustion:
sudo nano /etc/docker/daemon.json{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
sudo systemctl restart dockerDocker Compose
Docker Compose lets you define and run multi-container applications. If you installed Docker as shown above, the Compose plugin is already included as docker compose (with a space, not a hyphen).
docker compose version
# Docker Compose version v2.27.1Example: Run Open WebUI with Docker Compose
Create a docker-compose.yml file:
version: '3.8'
services:
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
ports:
- "3000:8080"
volumes:
- open-webui:/app/backend/data
environment:
- OLLAMA_BASE_URL=http://host.docker.internal:11434
extra_hosts:
- host.docker.internal:host-gateway
restart: unless-stopped
volumes:
open-webui:docker compose up -d # start in detached mode
docker compose logs -f # follow logs
docker compose down # stop and remove containers
docker compose pull # update imagesEssential Docker Commands
Images
docker pull ubuntu:24.04 # download an image
docker images # list local images
docker image ls # same as above
docker rmi ubuntu:24.04 # remove image
docker image prune # remove unused images
docker image prune -a # remove all unused images (including tagged)Containers
# Run a container:
docker run ubuntu:24.04 echo "Hello World"
# Run interactively:
docker run -it ubuntu:24.04 bash
# Run in background (detached):
docker run -d --name myapp nginx
# Run with port mapping:
docker run -d -p 8080:80 --name webserver nginx
# Run with volume mount:
docker run -d -v /host/data:/container/data --name myapp myimage
# Run and auto-remove on exit:
docker run --rm ubuntu:24.04 ls -la
# List running containers:
docker ps
# List all containers (including stopped):
docker ps -a
# Stop a container:
docker stop myapp
# Start a stopped container:
docker start myapp
# Remove a container:
docker rm myapp
# Remove running container (force):
docker rm -f myapp
# Execute command in running container:
docker exec -it myapp bash
# View container logs:
docker logs myapp
docker logs -f myapp # follow
docker logs --tail 50 myapp # last 50 lines
# Container stats (CPU, memory, network):
docker statsVolumes and Persistent Data
Containers are ephemeral — when you remove them, their filesystem changes are lost. Use volumes or bind mounts for persistent data.
Named Volumes (Recommended for Databases)
# Create a named volume:
docker volume create mydata
# Run container with the volume:
docker run -d -v mydata:/var/lib/mysql --name mysql mysql:8
# List volumes:
docker volume ls
# Inspect volume (see where it's stored):
docker volume inspect mydata
# Remove volume:
docker volume rm mydata
# Remove all unused volumes:
docker volume pruneBind Mounts (Recommended for Development)
# Mount a specific directory from host:
docker run -d -v /home/jm/myapp:/app -p 3000:3000 node:20 node /app/server.js
# Or with the --mount syntax (more explicit):
docker run -d
--mount type=bind,source=/home/jm/myapp,target=/app
-p 3000:3000 node:20Docker Networking
# List networks:
docker network ls
# Create a custom network:
docker network create mynetwork
# Connect containers on the same network (they can reach each other by name):
docker run -d --network mynetwork --name db postgres:16
docker run -d --network mynetwork --name app myapp
# Inside 'app', connect to 'db' using the hostname 'db'
# Inspect a network:
docker network inspect mynetwork
# Disconnect container from network:
docker network disconnect mynetwork mycontainerWriting a Dockerfile
A Dockerfile is the recipe for building a custom Docker image. Here's a practical example for a Python Flask app:
# Dockerfile
FROM python:3.12-slim
# Set working directory
WORKDIR /app
# Copy and install dependencies first (layer caching optimization)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Create non-root user (security best practice)
RUN useradd --create-home appuser && chown -R appuser /app
USER appuser
# Expose port
EXPOSE 5000
# Run the app
CMD ["python", "app.py"]# Build the image:
docker build -t myflaskapp:1.0 .
# Run it:
docker run -d -p 5000:5000 myflaskapp:1.0
# Build with no cache (force rebuild all layers):
docker build --no-cache -t myflaskapp:1.0 .Dockerfile Best Practices
- Use specific base image tags (
python:3.12-slim) notlatest—latestchanges and breaks builds - Copy
requirements.txtbefore the source code — unchanged requirements reuse the cached layer - Use
--no-cache-dirwith pip and similar flags to keep images smaller - Run containers as non-root users
- Use
.dockerignoreto excludenode_modules,.git,*.envfrom the build context - Prefer slim or alpine base images for smaller image size
Troubleshooting
Docker daemon not running
sudo systemctl start docker
sudo systemctl status docker
# Check logs:
sudo journalctl -u docker -n 50Permission denied — cannot connect to Docker daemon
# If you just added yourself to the docker group, log out and back in.
# Or apply immediately with:
newgrp docker
# Verify group membership:
groups $USER | grep dockerPort already in use
# Find what's using the port:
ss -tulnp | grep :8080
# Stop the conflicting process or use a different port:
docker run -d -p 8081:80 nginx # use 8081 insteadOut of disk space
# Check Docker's disk usage:
docker system df
# Clean up everything unused:
docker system prune -a --volumes
# This removes: stopped containers, unused images, unused volumes, build cacheContainer exits immediately
# Check the exit code and logs:
docker ps -a | grep myapp
docker logs myapp
# Run interactively to debug:
docker run -it --entrypoint bash myappFrequently Asked Questions
What's the difference between Docker and a virtual machine?
Virtual machines (VMs) emulate complete hardware and run a full OS with their own kernel. Containers share the host kernel and only contain the application and its dependencies. This makes containers start in milliseconds (vs. minutes for VMs) and use significantly less RAM and storage.
Docker vs. Podman — which should I use?
Both run OCI containers. Podman is daemonless (doesn't need a background service), runs rootless by default, and is included in Fedora/RHEL. Docker has better tooling, wider ecosystem support, and Docker Desktop. For most Linux users, Docker is the safe choice. On Fedora/RHEL, Podman is the native option and is compatible with most Docker commands.
How do I update a container to a new image version?
docker pull image:latest # pull new version
docker stop container_name
docker rm container_name
docker run -d ... image:latest # recreate with new image
# With Docker Compose:
docker compose pull
docker compose up -dHow do I backup a Docker volume?
# Backup named volume to tar file:
docker run --rm -v myvolume:/data -v $(pwd):/backup ubuntu
tar czf /backup/myvolume_backup.tar.gz -C /data .
# Restore:
docker run --rm -v myvolume:/data -v $(pwd):/backup ubuntu
tar xzf /backup/myvolume_backup.tar.gz -C /dataNow that Docker is running, you're ready to install containerized applications. Check out our guide on how to install Open WebUI with Docker to get a ChatGPT-style interface for your local LLMs in minutes.
