Docker has revolutionized the way developers build, ship, and run applications. If you’re working with Ubuntu and want to get started with containerization, this comprehensive guide will walk you through every step of installing Docker, configuring it properly, and running your first containers.
What Is Docker and Why Should You Use It?
Docker is an open-source platform that enables developers to package applications and their dependencies into lightweight, portable containers. Unlike traditional virtual machines, containers share the host operating system’s kernel, making them significantly faster to start and more efficient with system resources.
Here’s why Docker has become an essential tool for developers and system administrators:
- Consistency: Docker ensures your application runs the same way in development, testing, and production environments. No more “it works on my machine” problems.
- Isolation: Each container runs in its own isolated environment, preventing conflicts between applications and their dependencies.
- Portability: Docker containers can run on any system that supports Docker, whether it’s your laptop, a cloud server, or a Raspberry Pi.
- Efficiency: Containers are lightweight and share the host OS kernel, using far fewer resources than traditional virtual machines.
- Scalability: Docker makes it easy to scale applications horizontally by spinning up additional containers as needed.
- Version Control: Docker images can be versioned and stored in registries, making it easy to roll back to previous versions if something goes wrong.
Prerequisites
Before you begin, make sure you have:
- Ubuntu 22.04 (Jammy Jellyfish) or Ubuntu 24.04 (Noble Numbat) — either desktop or server edition
- A user account with sudo privileges
- A stable internet connection for downloading packages
- At least 4 GB of RAM and 20 GB of free disk space (recommended)
You should also be comfortable using the terminal, as all installation steps are performed via the command line.
Step 1: Remove Old Docker Versions
Before installing Docker CE (Community Edition), it’s important to remove any older versions that might already be installed on your system. Older versions were sometimes called docker, docker.io, or docker-engine.
sudo apt-get remove docker docker-engine docker.io containerd runc
Don’t worry if this command reports that none of these packages are installed — that just means you’re starting fresh.
Step 2: Update Your System
Always start by updating your package index and upgrading existing packages to their latest versions:
sudo apt-get update
sudo apt-get upgrade -y
Step 3: Install Required Dependencies
Docker requires a few packages to allow apt to use repositories over HTTPS:
sudo apt-get install -y \
ca-certificates \
curl \
gnupg \
lsb-release
Step 4: Add Docker’s Official GPG Key
To ensure the integrity of the packages you download, add Docker’s official GPG key:
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
Step 5: Set Up the Docker Repository
Add the Docker repository to your system’s software 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
Update the package index again to include Docker’s repository:
sudo apt-get update
Step 6: Install Docker CE
Now install Docker Community Edition along with the Docker CLI, containerd, and Docker plugins:
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
This installs the latest stable version of Docker. The installation process will also start the Docker daemon automatically.
Step 7: Post-Installation Steps
Add Your User to the Docker Group
By default, Docker commands require sudo privileges. To run Docker commands without sudo, add your user to the docker group:
sudo usermod -aG docker $USER
Important: You need to log out and log back in for this change to take effect. Alternatively, you can use the following command to apply the changes immediately in your current session:
newgrp docker
Enable Docker to Start on Boot
Docker should start automatically on boot, but you can verify and enable this with:
sudo systemctl enable docker
sudo systemctl enable containerd
Check Docker Service Status
Verify that the Docker service is running:
sudo systemctl status docker
You should see output indicating that the Docker service is active (running).
Step 8: Verify Your Installation
The best way to verify Docker is working correctly is to run the hello-world container:
docker run hello-world
If everything is set up correctly, you’ll see a message confirming that Docker is installed and working. This command downloads a test image from Docker Hub, creates a container from it, runs it, and displays a confirmation message.
You can also check the installed Docker version:
docker version
And get detailed system information:
docker info
Installing Docker Compose
Docker Compose is a tool for defining and running multi-container Docker applications. With the docker-compose-plugin package installed in Step 6, you already have Docker Compose v2 available as a Docker plugin.
Verify the installation:
docker compose version
If you need the standalone docker-compose command (v1 compatibility), you can install it separately:
sudo apt-get install docker-compose-plugin
With Docker Compose v2, you use docker compose (with a space) instead of the older docker-compose (with a hyphen).
Example Docker Compose File
Here’s a simple docker-compose.yml file that runs a web server with a database:
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: admin
POSTGRES_PASSWORD: secretpassword
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Run it with:
docker compose up -d
Essential Docker Commands
Here are the most important Docker commands you’ll use regularly:
Container Management
# Run a container
docker run -d --name my-nginx -p 8080:80 nginx
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# Stop a container
docker stop my-nginx
# Start a stopped container
docker start my-nginx
# Restart a container
docker restart my-nginx
# Remove a stopped container
docker rm my-nginx
# Force remove a running container
docker rm -f my-nginx
# View container logs
docker logs my-nginx
# Execute a command inside a running container
docker exec -it my-nginx /bin/sh
Image Management
# Pull an image from Docker Hub
docker pull ubuntu:24.04
# List downloaded images
docker images
# Remove an image
docker rmi ubuntu:24.04
# Remove all unused images
docker image prune -a
# Search for images on Docker Hub
docker search nginx
System Cleanup
# Remove all stopped containers, unused networks, and dangling images
docker system prune
# Remove everything including unused volumes
docker system prune -a --volumes
# Check disk usage
docker system df
Creating Your First Dockerfile
A Dockerfile is a text file that contains instructions for building a Docker image. Let’s create a simple Node.js application container.
First, create a project directory:
mkdir my-docker-app && cd my-docker-app
Create a simple app.js file:
cat > app.js << 'EOF'
const http = require('http');
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello from Docker!\n');
});
server.listen(port, '0.0.0.0', () => {
console.log(`Server running at http://0.0.0.0:${port}/`);
});
EOF
Now create the Dockerfile:
# Use the official Node.js 22 Alpine image as the base
FROM node:22-alpine
# Set the working directory inside the container
WORKDIR /app
# Copy application files
COPY app.js .
# Expose port 3000
EXPOSE 3000
# Define the command to run the application
CMD ["node", "app.js"]
Build the image:
docker build -t my-node-app .
Run a container from your image:
docker run -d --name my-app -p 3000:3000 my-node-app
Visit http://localhost:3000 in your browser to see your application running inside a Docker container!
Useful Tips and Best Practices
1. Use Official Images
Always start with official Docker Hub images when possible. They’re maintained, regularly updated, and scanned for vulnerabilities.
2. Use Alpine-Based Images
Alpine Linux images are much smaller than their Ubuntu or Debian counterparts. For example, node:22-alpine is roughly 50 MB compared to node:22 at over 300 MB.
3. Use .dockerignore
Create a .dockerignore file in your project to exclude unnecessary files from the build context, similar to .gitignore:
node_modules
.git
.env
*.md
docker-compose*.yml
4. Don’t Run as Root
For security, create a non-root user in your Dockerfile:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
5. Use Multi-Stage Builds
Multi-stage builds help keep your final image small by separating build dependencies from runtime dependencies:
# Build stage
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# Production stage
FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
CMD ["node", "app.js"]
6. Keep Layers Minimal
Combine related commands into single RUN instructions to reduce the number of image layers:
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
7. Tag Your Images
Always tag your images with meaningful version numbers instead of relying on the latest tag:
docker build -t my-app:1.0.0 .
docker build -t my-app:1.0.1 .
8. Use Docker Volumes for Persistent Data
Never store important data inside a container. Use volumes to persist data:
docker volume create my-data
docker run -v my-data:/app/data my-app
Troubleshooting Common Issues
Permission denied errors: Make sure your user is in the docker group and you’ve logged out and back in.
Cannot connect to Docker daemon: Ensure the Docker service is running with sudo systemctl start docker.
Port already in use: Check if another service is using the port with sudo lsof -i :PORT and either stop it or use a different port mapping.
Disk space issues: Run docker system prune -a to clean up unused images, containers, and volumes.
Conclusion and Next Steps
You’ve successfully installed Docker on Ubuntu, learned the essential commands, and created your first Dockerfile. Docker is a powerful tool that opens up a world of possibilities for development and deployment workflows.
Here are some recommended next steps to continue your Docker journey:
- Explore Docker Hub — Browse the vast library of official and community images
- Learn Docker Compose — Master multi-container applications for more complex setups
- Study Docker networking — Understand bridge, host, and overlay networks
- Try Docker Swarm or Kubernetes — Scale your containers across multiple machines
- Set up a CI/CD pipeline — Automate building and deploying Docker images with GitHub Actions
- Learn about container security — Implement best practices for securing your containers
Docker is an essential skill for modern software development, and the fundamentals you’ve learned here will serve as a solid foundation for building and deploying containerized applications. Happy containerizing!