--- title: "Install Docker on Debian and Ubuntu" tags: ['Docker', 'Debian', 'Ubuntu'] published: 2025-04-30 15:00:00 updated: 2025-05-05 08:41:59 excerpt: "Install Docker and Docker Compose on Debian Stable or Ubuntu LTS, add the official repository, enable IPv6, and try a small Compose stack." --- > This file is the raw Markdown source of an m.ac post; the canonical version lives at https://m.ac/install-docker-debian-ubuntu/ > The full index of posts and pages is at https://m.ac/llms.txt — read the index first before crawling further. ## Introduction ### What is Docker? [Docker](https://www.docker.com/) packages an application and its dependencies into containers so it runs the same way on your laptop, a test box, and a production server. Fewer "it works on my machine" mysteries, which is usually worth the trouble. ### What is Docker Compose? [Docker Compose](https://docs.docker.com/compose/) manages multi-container Docker applications from one YAML file. You define the services once, then start them with a single command instead of juggling a small circus of `docker run` flags. --- ## 1. Prerequisites Make sure you have installed [**Debian Stable**](https://www.debian.org/releases/) (either **oldstable** or **stable** releases, **sid** is not supported) or [**Ubuntu LTS**](https://releases.ubuntu.com/) (Interim releases not supported). The commands below use `sudo`. If you prefer a root shell, run `sudo -i` first and drop `sudo` from the commands. The `docker` commands are not prefixed with `sudo`: add your user to the `docker` group (`sudo usermod -aG docker $USER`, then log out and back in), or add `sudo` yourself. Update your system and install the required tools. ```bash sudo apt update -y sudo apt upgrade -y sudo apt full-upgrade -y sudo apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates -y ``` --- ## 2. Install Docker and Docker Compose ### 2.1 Add Docker's GPG key ```bash curl -sSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-ce.gpg ``` ### 2.2 Add the Docker repository Debian: ```bash echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-ce.gpg] https://download.docker.com/linux/debian $(lsb_release -sc) stable" | sudo tee /etc/apt/sources.list.d/docker.list ``` On Debian, you can also enable it with [extrepo](https://m.ac/use-extrepo-add-third-party-repositories-debian/): ```bash sudo apt install extrepo -y sudo extrepo enable docker-ce ``` Ubuntu: ```bash echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-ce.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -sc) stable" | sudo tee /etc/apt/sources.list.d/docker.list ``` ### 2.3 Install ```bash sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin ``` Check the installed Docker version: ```bash $ docker version Client: Docker Engine - Community Version: 29.6.1 API version: 1.55 Go version: go1.26.4 Git commit: 8900f1d Built: Fri Jun 26 11:40:34 2026 OS/Arch: linux/amd64 Context: default Server: Docker Engine - Community Engine: Version: 29.6.1 API version: 1.55 (minimum version 1.40) Go version: go1.26.4 Git commit: 8ec5ab3 Built: Fri Jun 26 11:40:34 2026 OS/Arch: linux/amd64 Experimental: true containerd: Version: v2.2.5 GitCommit: e53c7c1516c3b2bff98eb76f1f4117477e6f4e66 runc: Version: 1.3.6 GitCommit: v1.3.6-0-g491b69ba docker-init: Version: 0.19.0 GitCommit: de40ad ``` Check Docker Compose too: ```bash $ docker compose version Docker Compose version v5.3. ``` --- ## 3. Add IPv6 support By default, Docker does not enable IPv6 for bridge networks. To turn it on, add the IPv6 settings to the Docker daemon configuration. > Warning: Some containers do not behave well with IPv6. Test the applications you plan to run before relying on it in production. Create or edit the Docker daemon configuration file: ```bash sudo bash -c '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": "fdba::/64", "experimental":true, "ip6tables":true } EOF' ``` Restart Docker so it reads the new configuration: ```bash sudo systemctl restart docker ``` Test IPv6 support by creating a Docker network named `ipv6_network`: ```bash docker network create --ipv6 --subnet=fd00::/64 ipv6_network ``` Then use the `curlimages/curl` image to make an IPv6 request: ```bash docker run --rm --network ipv6_network curlimages/curl:latest curl -6 ip.sb -s ``` If the command prints your public IPv6 address, Docker's IPv6 bridge networking is working. --- ## 4. Use rootless mode (optional) [Rootless mode](https://docs.docker.com/engine/security/rootless/) lets the Docker daemon and containers run as a non-root user. That reduces the damage if something goes wrong, although it does not make containers magically harmless. > Warning: Some containers do not work in rootless mode. If a container behaves strangely, test it with regular Docker before blaming the application. Install the required system packages: ```bash sudo apt install uidmap dbus-user-session docker-ce-rootless-extras ``` > Note: The `docker-ce-rootless-extras` package is already included in the Docker installation. Create a new user named `showfom`, or use another name if you prefer: ```bash sudo adduser --disabled-password --gecos "" --home /home/showfom showfom ``` Disable the currently running Docker services: ```bash sudo systemctl disable --now docker.service docker.socket sudo rm /var/run/docker.sock ``` Enable lingering for the `showfom` user, so its systemd user manager (and the rootless Docker daemon under it) starts at boot and survives logout: ```bash sudo loginctl enable-linger showfom ``` Switch to the `showfom` user and run the `dockerd-rootless-setuptool.sh` script. Use `machinectl shell` rather than `su`: the setup script talks to `systemctl --user`, which needs a real systemd login session that `sudo su -` does not provide: ```bash sudo machinectl shell showfom@ /usr/bin/dockerd-rootless-setuptool.sh install ``` If `machinectl` is missing, install it with `sudo apt install systemd-container -y`. You should see output similar to the following: ```bash $ 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/user@1000.service/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 ``` Add the following environment variables: ```bash echo 'export PATH=/usr/bin:$PATH' >> ~/.bashrc echo 'export DOCKER_HOST=unix:///run/user/1000/docker.sock' >> ~/.bashrc source ~/.bashrc ``` Enable the Docker service for the user: ```bash systemctl --user enable --now docker systemctl --user status docker ``` You will see output like this: ```bash $ 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/user@1000.service/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). ``` Docker is installed. Time to make sure Compose works too. > Note: Docker rootless mode has limits, and some containers are not compatible with it. --- ## 5. Try Docker Compose A tiny Nginx site is enough to check that Docker Compose is behaving. Create a new directory, generate an `index.html` file, and add a simple Nginx configuration file named `default.conf`: ```bash 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 a file named `compose.yaml`: ```bash 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 ``` Pull the required container images: ```bash docker compose pull ``` After the Nginx image has been pulled, start the container: ```bash docker compose up -d ``` Use `docker ps` to check that the container is running: ```bash $ 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 ``` Test Nginx over both IPv4 and IPv6. IPv4: ```bash curl localhost:8080 -4 ``` IPv6: ```bash curl localhost:8080 -6 ``` If both commands print `Hello World`, Compose is doing its job. Stop the stack with `docker compose down`.