How to Install Docker on Debian and Ubuntu (Step-by-Step Guide)
Learn how to install Docker on Debian and Ubuntu step by step. This guide covers installation commands, repository setup, and post-installation tips for beginners.

Introduction
What is Docker?
Docker is a containerization platform that packages applications with all necessary dependencies into standardized units called containers. These lightweight containers ensure consistent application behavior across diverse environments, from development through production.

What is Docker Compose?
Docker Compose is a tool designed to simplify the management of multi-container Docker applications. By using a single YAML file, developers can define, configure, and launch multiple interconnected containers with a single command, streamlining complex application deployments.β

1. Prerequisites
Make sure you have installed Debian Stable (either oldstable or stable releases, sid is not supported) or Ubuntu LTS (Interim releases not supported).
For the installation process, I will switch to the root
user by sudo -i
or su root
.
First, update your system to the latest packages and install some required software.
apt update -y
apt upgrade -y
apt full-upgrade -y
apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates -y
Update system packages
2. Install Docker and Docker Compose
2.1 Add Docker GPG Key
curl -sSL https://download.docker.com/linux/debian/gpg | gpg --dearmor > /usr/share/keyrings/docker-ce.gpg
Add Docker GPG Key
2.2 Add Docker Repository
Debian:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-ce.gpg] https://download.docker.com/linux/debian $(lsb_release -sc) stable" > /etc/apt/sources.list.d/docker.list
Add Debian Repository
Ubuntu:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-ce.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -sc) stable" > /etc/apt/sources.list.d/docker.list
Add Ubuntu Repository
2.3 Install
apt update
apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
Install Docker and Docker Compose
We can now use docker version
and docker compose version
to check whether the latest version of Docker is installed:
# docker version
Client: Docker Engine - Community
Version: 28.1.1
API version: 1.49
Go version: go1.23.8
Git commit: 4eba377
Built: Fri Apr 18 09:52:57 2025
OS/Arch: linux/amd64
Context: default
Server: Docker Engine - Community
Engine:
Version: 28.1.1
API version: 1.49 (minimum version 1.24)
Go version: go1.23.8
Git commit: 01f442b
Built: Fri Apr 18 09:52:57 2025
OS/Arch: linux/amd64
Experimental: true
containerd:
Version: 1.7.27
GitCommit: 05044ec0a9a75232cad458027ca83437aae3f4da
runc:
Version: 1.2.5
GitCommit: v1.2.5-0-g59923ef
docker-init:
Version: 0.19.0
GitCommit: de40ad0
Docker Version
And
# docker compose version
Docker Compose version v2.35.1
Docker Compose Version
3. Add IPv6 Support
By default, Docker does not support IPv6 when using the bridge network mode. However, you can enable native IPv6 support by modifying the Docker configuration file:
cat > /etc/docker/daemon.json << EOF
{
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "3"
},
"userland-proxy": false,
"ipv6": true,
"fixed-cidr-v6": "fdb::/64",
"experimental":true,
"ip6tables":true
}
EOF
Modify Docker configuration
Then, restart the Docker service for the changes to take effect:
systemctl restart docker
Restart Docker
Now we can test IPv6 support. First, create a Docker network named ipv6_network
:
docker network create --ipv6 --subnet=fd00::/64 ipv6_network
Create a Network
Then, use the curlimages/curl
image to perform a test:
docker run --rm --network ipv6_network curlimages/curl:latest curl -6 ip.sb -s
Docker run curl to test IPv6
If your public IPv6 address is displayed in the terminal output, everything is working correctly.
4. Use Rootless Mode
Rootless mode allows the Docker daemon and containers to run as a non-root user, helping to mitigate potential security vulnerabilities in both the daemon and the containers.
First, install the required system packages:
apt install uidmap dbus-user-session docker-ce-rootless-extras
Install required packages
docker-ce-rootless-extras
package is already included in the Docker installation.Next, create a new user named showfom
or whatever you like:
sudo adduser --disabled-password --gecos "" --home /home/showfom showfom
Create User
Then, disable the currently running Docker services:
systemctl disable --now docker.service docker.socket
rm /var/run/docker.sock
Disable Docker Services
Now, switch to the showfom
user and run the dockerd-rootless-setuptool.sh
script:
su - showfom
cd ~
/usr/bin/dockerd-rootless-setuptool.sh install
Install Docker Rootless
You should see output similar to the following:
$ dockerd-rootless-setuptool.sh install
[INFO] Creating /home/showfom/.config/systemd/user/docker.service
[INFO] starting systemd service docker.service
+ systemctl --user start docker.service
+ sleep 3
+ systemctl --user --no-pager --full status docker.service
β docker.service - Docker Application Container Engine (Rootless)
Loaded: loaded (/home/showfom/.config/systemd/user/docker.service; disabled; preset: enabled)
Active: active (running) since Wed 2025-04-30 14:53:19 UTC; 3s ago
Docs: https://docs.docker.com/go/rootless/
Main PID: 227068 (rootlesskit)
Tasks: 39
Memory: 58.2M
CPU: 398ms
CGroup: /user.slice/user-1000.slice/[email protected]/app.slice/docker.service
ββ227068 rootlesskit --state-dir=/run/user/1000/dockerd-rootless --net=slirp4netns --mtu=65520 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run --propagation=rslave /usr/bin/dockerd-rootless.sh
ββ227080 /proc/self/exe --state-dir=/run/user/1000/dockerd-rootless --net=slirp4netns --mtu=65520 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run --propagation=rslave /usr/bin/dockerd-rootless.sh
ββ227104 slirp4netns --mtu 65520 -r 3 --disable-host-loopback --enable-sandbox --enable-seccomp 227080 tap0
ββ227112 dockerd
ββ227132 containerd --config /run/user/1000/docker/containerd/containerd.toml
Apr 30 14:53:19 m dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.393381898Z" level=warning msg="WARNING: No io.max (rbps) support"
Apr 30 14:53:19 m dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.393387228Z" level=warning msg="WARNING: No io.max (wbps) support"
Apr 30 14:53:19 m dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.393393279Z" level=warning msg="WARNING: No io.max (riops) support"
Apr 30 14:53:19 m dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.393398720Z" level=warning msg="WARNING: No io.max (wiops) support"
Apr 30 14:53:19 m dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.393415330Z" level=info msg="Docker daemon" commit=01f442b containerd-snapshotter=false storage-driver=overlay2 version=28.1.1
Apr 30 14:53:19 m dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.393481325Z" level=info msg="Initializing buildkit"
Apr 30 14:53:19 m dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.420441982Z" level=info msg="Completed buildkit initialization"
Apr 30 14:53:19 m dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.425685368Z" level=info msg="Daemon has completed initialization"
Apr 30 14:53:19 m dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.425749069Z" level=info msg="API listen on /run/user/1000/docker.sock"
Apr 30 14:53:19 m systemd[226972]: Started docker.service - Docker Application Container Engine (Rootless).
+ DOCKER_HOST=unix:///run/user/1000/docker.sock /usr/bin/docker version
Client: Docker Engine - Community
Version: 28.1.1
API version: 1.49
Go version: go1.23.8
Git commit: 4eba377
Built: Fri Apr 18 09:52:57 2025
OS/Arch: linux/amd64
Context: default
Server: Docker Engine - Community
Engine:
Version: 28.1.1
API version: 1.49 (minimum version 1.24)
Go version: go1.23.8
Git commit: 01f442b
Built: Fri Apr 18 09:52:57 2025
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.7.27
GitCommit: 05044ec0a9a75232cad458027ca83437aae3f4da
runc:
Version: 1.2.5
GitCommit: v1.2.5-0-g59923ef
docker-init:
Version: 0.19.0
GitCommit: de40ad0
rootlesskit:
Version: 2.3.4
ApiVersion: 1.1.1
NetworkDriver: slirp4netns
PortDriver: builtin
StateDir: /run/user/1000/dockerd-rootless
slirp4netns:
Version: 1.2.0
GitCommit: 656041d45cfca7a4176f6b7eed9e4fe6c11e8383
+ systemctl --user enable docker.service
Created symlink /home/showfom/.config/systemd/user/default.target.wants/docker.service β /home/showfom/.config/systemd/user/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 showfom`
[INFO] Creating CLI context "rootless"
Successfully created context "rootless"
[INFO] Using CLI context "rootless"
Current context is now "rootless"
[INFO] Make sure the following environment variable(s) are set (or add them to ~/.bashrc):
export PATH=/usr/bin:$PATH
[INFO] Some applications may require the following environment variable too:
export DOCKER_HOST=unix:///run/user/1000/docker.sock
Sample Output
Add the following environment variables:
echo 'export PATH=/usr/bin:$PATH' >> ~/.bashrc
echo 'export DOCKER_HOST=unix:///run/user/1000/docker.sock' >> ~/.bashrc
source ~/.bashrc
Add Docker environment variables
After that, enable the Docker service for the user:
systemctl --user enable --now docker
systemctl --user status docker
Enable Docker service and check status
You will see output like this:
$ systemctl --user status docker
β docker.service - Docker Application Container Engine (Rootless)
Loaded: loaded (/home/showfom/.config/systemd/user/docker.service; enabled; preset: enabled)
Active: active (running) since Wed 2025-04-30 14:53:19 UTC; 1min 13s ago
Docs: https://docs.docker.com/go/rootless/
Main PID: 227068 (rootlesskit)
Tasks: 39
Memory: 58.3M
CPU: 463ms
CGroup: /user.slice/user-1000.slice/[email protected]/app.slice/docker.service
ββ227068 rootlesskit --state-dir=/run/user/1000/dockerd-rootless --net=slirp4netns --mtu=65520 --slirp4netns-sandbox=auto ->
ββ227080 /proc/self/exe --state-dir=/run/user/1000/dockerd-rootless --net=slirp4netns --mtu=65520 --slirp4netns-sandbox=aut>
ββ227104 slirp4netns --mtu 65520 -r 3 --disable-host-loopback --enable-sandbox --enable-seccomp 227080 tap0
ββ227112 dockerd
ββ227132 containerd --config /run/user/1000/docker/containerd/containerd.toml
Apr 30 14:53:19 debian dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.393381898Z" level=warning msg="WARNING: No io.max (rbps) suppor>
Apr 30 14:53:19 debian dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.393387228Z" level=warning msg="WARNING: No io.max (wbps) suppor>
Apr 30 14:53:19 debian dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.393393279Z" level=warning msg="WARNING: No io.max (riops) suppo>
Apr 30 14:53:19 debian dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.393398720Z" level=warning msg="WARNING: No io.max (wiops) suppo>
Apr 30 14:53:19 debian dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.393415330Z" level=info msg="Docker daemon" commit=01f442b conta>
Apr 30 14:53:19 debian dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.393481325Z" level=info msg="Initializing buildkit"
Apr 30 14:53:19 debian dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.420441982Z" level=info msg="Completed buildkit initialization"
Apr 30 14:53:19 debian dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.425685368Z" level=info msg="Daemon has completed initialization"
Apr 30 14:53:19 debian dockerd-rootless.sh[227112]: time="2025-04-30T14:53:19.425749069Z" level=info msg="API listen on /run/user/1000/docker>
Apr 30 14:53:19 debian systemd[226972]: Started docker.service - Docker Application Container Engine (Rootless).
Sample output
Congratulations! Youβve successfully installed Docker. Now, letβs move on to using Docker Compose.
5. Use Docker Compose
First, create a new directory, generate an index.html
file, and add a simple Nginx configuration file named default.conf
:
mkdir html
echo 'Hello World' > ./html/index.html
echo 'server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}' > ./default.conf
Create Nginx example file and configuration
Next, create a file named compose.yaml
:
echo 'name: nginx
services:
nginx:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html
- ./default.conf:/etc/nginx/conf.d/default.conf
restart: always
networks:
- nginx-network
networks:
nginx-network:
name: nginx-network
enable_ipv6: true
driver: bridge
ipam:
driver: default
config:
- subnet: 192.168.0.0/24
gateway: 192.168.0.1
- subnet: "fdba::/64"
gateway: "fdba::1"' > ./compose.yaml
Create Docker Compose file
Then, run the following command to pull the required container images:
docker compose pull
Pull container images
Once the Nginx image has been pulled, start the container using:
docker compose up -d
Start the container
You can use the docker ps
command to check if all containers are running:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2e11699fbc47 nginx:latest "/docker-entrypoint.β¦" 5 seconds ago Up 5 seconds 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp nginx-nginx-1
Sample output
Now, test whether Nginx is working by accessing it over both IPv4 and IPv6.
IPv4:
curl localhost:8080 -4
Test IPv4
IPv6:
curl localhost:8080 -6
Test IPv6
If you see Hello World, everything is working correctly! You can shut down the container using the docker compose down
command.