Docker Engine and Compose
Component map
Installing Docker does not deploy an application. Application workloads exist only once containers have been created and started.
Open full-size imageStart with the commands on the left: the client sends requests to the daemon. build creates an image, pull retrieves one from a registry, and run creates and starts a container from an image, pulling it first if needed. The image and its running container are separate objects. Compose uses the same Engine to manage several services; it is not shown in this diagram.
Inspect the current installation
command -v docker || true
docker --version || true
docker compose version || true
dpkg -l | grep -E 'docker|containerd' || true
If a supported Docker installation already works, do not reinstall it or mix package sources. For a new installation, configure apt using Docker’s official Ubuntu repository instructions rather than mixing these packages with Ubuntu’s docker.io packages.
Configure the official apt repository
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
Create the repository file below, replacing both placeholders with this VPS’s actual values. The commands that follow show its Ubuntu codename and architecture. If the file already exists, inspect it before changing it rather than overwriting it blindly.
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: <UBUNTU_CODENAME>
Components: stable
Architectures: <DPKG_ARCHITECTURE>
Signed-By: /etc/apt/keyrings/docker.asc
Use the system’s actual codename and architecture, rather than copying values from another Ubuntu release:
. /etc/os-release
printf '%s\n' "${UBUNTU_CODENAME:-$VERSION_CODENAME}"
dpkg --print-architecture
sudo apt update
apt-cache policy docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
If the official repository has no candidate packages for this release, stop. Do not substitute another Ubuntu codename in the source configuration.
Simulate the installation, then verify it
sudo apt-get -s install \
docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
Review the simulation for removals or conflicts and confirm that no critical packages will be removed before installing:
sudo apt install -y \
docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
systemctl is-enabled docker
systemctl is-active docker
docker --version
docker compose version
Non-root use and its security implications
sudo usermod -aG docker "$VPS_USER"
Supplementary group membership takes effect in a new login session. Log out and reconnect over Tailscale using SSH, then run id. Never use this command:
sudo chmod 666 /var/run/docker.sock
This Docker Engine runs as root. Membership in the docker group grants root-level privileges, because members can tell the daemon to mount host paths or start privileged containers. Add only trusted administrators to this group.
Functional test
id
docker info --format \
'ServerVersion={{.ServerVersion}} Driver={{.Driver}} CgroupDriver={{.CgroupDriver}}'
docker run --rm hello-world
docker ps -a
With hello-world, the CLI contacts the daemon, which pulls the image, creates a container, runs it, and prints the result. --rm deletes the container when it exits, not its image; the image normally remains in the local cache.
Keep images, containers, and volumes distinct
- Deleting a container does not automatically delete its image. Images are templates; containers are instances. For the processes inside a container and their memory boundaries, see processes and memory.
- Recreating a container does not preserve important data in its writable layer.
- Data that must persist belongs in named volumes or explicitly chosen host directories.
/var/lib/dockerholds Docker’s internal state. Do not edit it manually.- A Compose file describes the desired state, but no application Compose files are created during this foundation stage.
Firewall reminder
Docker-published container ports can bypass ordinary UFW rules. For every future deployment, review Compose ports, actual listening addresses, and Docker/iptables rules together. Binding a service only to 127.0.0.1, to a Tailscale address, or to a reverse-proxy network has different consequences; choose deliberately.