DevOps.Academy
CoursesCurriculumPricing

    Loading…

    ↑ ↓ move · ↵ open · Ctrl K anywhere

    ◔My learningSign in
    DevOps.Academy

    Free, hands-on DevOps lessons. Made with care in India.

    Learn

    All coursesLinux roadmapCurriculumMy learning

    Academy

    PricingCertificates

    Legal

    PrivacyTermsContact

    devops.rajeev.pro

    DockerLesson 3 of 14
    Your progress · 0%
    On this page
    1. What you're installing
    2. Docker Desktop: macOS and Windows
    3. Docker Engine on a Linux server
    4. Rootless mode
    ↖ Course roadmap
    Lesson 3 · Installation

    Installing Docker: Desktop, Engine on Linux, and rootless

    Install Docker the right way for your machine: Docker Desktop on macOS and Windows, Docker Engine from Docker's repository on a Linux server, and rootless mode when the docker group is too much power.

    25 min read · beginner · hands-on

    By the end of this lesson you'll be able to

    0 of 5 complete

    What you're installing

    "Docker" is several programs. The docker command you type is only a client; the work happens in a daemon it talks to:

    The CLI is a remote control. dockerd, containerd and runc do the work, on the host's Linux kernel or in Desktop's VM.© Diagram: DevOps Academy

    The CLI is a remote control. dockerd, containerd and runc do the work, on the host's Linux kernel or in Desktop's VM.© Diagram: DevOps Academy

    docker version shows both halves. If the Server part is missing, the CLI can't reach the daemon:

    $ docker version
    Client:
     Version:           29.1.3
     API version:       1.52
     Context:           default
    
    Server:
     Engine:
      Version:          29.1.3
      API version:      1.52 (minimum version 1.44)
     containerd:
      Version:          2.2.2
     runc:
      Version:          1.4.0-0ubuntu1
    
    docker version
    • dockerbuild and run containers
    • versionclient and server versions

    Docker Desktop: macOS and Windows

    On a laptop, install Docker Desktop from docker.com. It brings the CLI, Compose, Buildx, a small Linux VM to run containers in (WSL 2 on Windows, which you enable first), and a GUI to see containers, images and logs. Files, ports and DNS are forwarded between your laptop and the VM, so localhost:8080 works as you'd expect.

    Two things to know:

    • Licensing: Docker Desktop is free for personal use, education, non-commercial open source and small businesses (fewer than 250 employees and under $10 million in yearly revenue). Larger companies need a paid subscription. Docker Engine on Linux has no such licence.
    • Architecture: on Apple silicon Macs, images are arm64 by default. An image built only for amd64 runs slowly under emulation, and a laptop build may not match an amd64 server. Build with --platform linux/amd64 when the target is a typical cloud server.

    Docker Engine on a Linux server

    On servers, install Docker Engine from Docker's own repository (the Linux course's Packages lesson explains repositories and signing keys):

    $ sudo install -m 0755 -d /etc/apt/keyrings
    $ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    $ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \
        | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    $ sudo apt-get update
    $ sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    $ sudo systemctl enable --now docker
    
    sudo install -m 0755 -d /etc/apt/keyrings
    • sudorun the next command as root
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    • curlmake an HTTP request
    • -owrite the body to this file
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \
    • echoprint text
    sudo systemctl enable --now docker
    • systemctlcontrol systemd services

    Ubuntu's own docker.io package is an alternative (sudo apt-get install docker.io docker-buildx docker-compose-v2): simpler, sometimes a version or two behind. Note that Compose is a separate package either way; without it, docker compose answers unknown command.

    Check the install:

    $ docker run --rm hello-world
    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.
        (amd64)
     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 info | grep -E "Storage Driver|Cgroup Version|Docker Root Dir"
     Storage Driver: overlayfs
     Cgroup Version: 2
     Docker Root Dir: /var/lib/docker
    
    docker run --rm hello-world
    • dockerbuild and run containers
    • runstart a new container
    • --rmdelete the container when it exits
    • hello-worldthe image to start
    docker info | grep -E "Storage Driver|Cgroup Version|Docker Root Dir"
    • infohow the daemon is set up
    • |pipe: send output to the next command
    • grepfind lines that match a pattern

    Those four steps are the diagram above, in words.

    Running docker without sudo

    $ ls -l /var/run/docker.sock
    srw-rw---- 1 root docker 0 Oct 14 09:03 /var/run/docker.sock
    $ sudo usermod -aG docker "$USER"      # then log out and in again
    $ docker ps                            # works without sudo in a new login
    
    ls -l /var/run/docker.sock
    • lslist files
    • -llong format: permissions, owner, size, date
    sudo usermod -aG docker "$USER"
    • sudorun the next command as root
    docker ps
    • dockerbuild and run containers
    • pslist running containers

    Until you log in again, your shell keeps its old groups and gets permission denied while trying to connect to the docker API at unix:///var/run/docker.sock.

    The docker group is root

    Anyone who can talk to the Docker socket can run docker run -v /:/host alpine and change any file on the host. Add only people you'd give sudo, never service accounts that don't need it, and never expose the socket over the network without TLS.

    Rootless mode

    Rootless mode runs the daemon and every container as your normal user. Root inside a container is mapped to your unprivileged user outside, so a container escape gets your rights, not the host's:

    $ sudo apt-get install -y uidmap docker-ce-rootless-extras
    $ dockerd-rootless-setuptool.sh install           # as your normal user, not with sudo
    $ docker context use rootless
    $ docker info | grep -i rootless
    
    sudo apt-get install -y uidmap docker-ce-rootless-extras
    • sudorun the next command as root
    docker context use rootless
    • dockerbuild and run containers
    • contextchoose which daemon to talk to
    docker info | grep -i rootless
    • infohow the daemon is set up
    • |pipe: send output to the next command
    • grepfind lines that match a pattern
    • -iignore upper/lower case

    Trade-offs: ports below 1024 need an extra setting, some storage and network features are slower, and each user has their own images and containers. Podman runs rootless by default and accepts the same commands, which makes it a common choice on shared hosts.

    In production

    Pin the Docker version you run in production, update it on purpose like any package, and enable the service so containers with a restart policy come back after a reboot. On shared or sensitive hosts, prefer rootless mode or Podman over adding users to the docker group.

    ⌘
    Mini mission

    Install Docker and prove every part works

    On an Ubuntu VM, install Docker Engine from Docker's repository with the Compose and Buildx plugins, let your user run docker without sudo, and prove the client, daemon, containerd, storage driver, cgroups and image pulls all work.

    Input: An Ubuntu 24.04 or newer VM with sudoOutput: hello-world output, docker version with both halves, and docker compose version
    Reveal one safe solution
    $ sudo install -m 0755 -d /etc/apt/keyrings
    $ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    $ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \
        | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    $ sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    $ sudo usermod -aG docker "$USER" && exit                 # log in again
    $ docker run --rm hello-world | head -2
    Hello from Docker!
    This message shows that your installation appears to be working correctly.
    $ docker version --format 'client {{.Client.Version}} · server {{.Server.Version}}'
    $ docker info --format 'storage={{.Driver}} cgroup={{.CgroupVersion}}'
    storage=overlayfs cgroup=2
    $ docker compose version && docker buildx version
    
    sudo install -m 0755 -d /etc/apt/keyrings
    • sudorun the next command as root
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    • curlmake an HTTP request
    • -owrite the body to this file
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \
    • echoprint text
    sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    • &&run the next command only if this one worked
    docker run --rm hello-world | head -2
    • dockerbuild and run containers
    • runstart a new container
    • --rmdelete the container when it exits
    • hello-worldthe image to start
    • |pipe: send output to the next command
    • headshow the first lines
    docker version --format 'client {{.Client.Version}} · server {{.Server.Version}}'
    • versionclient and server versions
    • --format 'client {{.Client.Version}} · server {{.Server.Version}}'print only these fields (Go template)
    docker info --format 'storage={{.Driver}} cgroup={{.CgroupVersion}}'
    • infohow the daemon is set up
    • --format 'storage={{.Driver}} cgroup={{.CgroupVersion}}'print only these fields (Go template)
    docker compose version && docker buildx version
    • composerun a multi-container app from compose.yaml
    • buildxthe BuildKit builder

    If docker version prints only the client, run systemctl status docker; if it says permission denied, your new group hasn't applied yet: log out and in again.

    Try it yourself

    0 of 6 steps done

    Run each task in your own account or terminal, then tick it off.

    Knowledge check

    Quick check

    Question 1 of 5

    Why does Docker Desktop on macOS run a Linux virtual machine?

    Go deeper

    Official reading for this lesson

    • DocsDocker Docs: install Docker Engine on UbuntuDocker Docs ↗
    • DocsDocker Docs: Linux post-installation stepsDocker Docs ↗
    • DocsDocker Docs: rootless modeDocker Docs ↗
    • DocsDocker Docs: Docker DesktopDocker Docs ↗
    • DocsDocker Docs: protect the Docker daemon socketDocker Docs ↗

    Or get every quiz answer right and it completes itself.

    ← PreviousUnder the hood: namespaces, cgroups and layered filesystemsNext lesson →Docker basics: images, containers, ports, logs and exec