OneLinersCommand workbench
Guides
Containers & Kubernetes / Security

Install Docker Engine and Compose with safer defaults

Install from Docker's signed repository, limit daemon exposure, bound logs, and document privilege boundaries.

35 min9 stepsChanges system stateRevision 2
Save or explore
Save to collectionCreate a collection in the sidebar first.
0 of 9 steps completed
Goal

Provide a maintainable runtime without silently exposing a privileged daemon or unbounded logs.

Supported environments
  • Ubuntu Server 24.04 LTS
  • Docker Engine 29.x
  • Compose plugin 2.x
Prerequisites
  • Supported host Confirm Ubuntu release and architecture.
  • Package audit Identify conflicting unofficial packages and active containers.
  • Privilege decision docker group membership is root-equivalent; prefer sudo or rootless mode.
Operating boundary

OneLiners never runs these steps or stores secrets. Review placeholders, versions, current state, and change-control requirements before using a command.

Full guide

What you will build

System
  • A Docker Engine 29.x installation on Ubuntu 24.04 from Docker's signed APT repository, including the CLI, containerd, Buildx and Compose v2 plugin.
  • A validated daemon policy with bounded local logs, live restore and no-new-privileges as the default for newly created containers.
  • A least-privilege Compose baseline and an explicit decision that Docker socket access remains restricted to trusted administrators.
Observable outcome
  • docker version and docker compose version report a working client/server pair from the intended repository.
  • The daemon is active, no unauthenticated TCP API listens on 2375 or 2376, and AppArmor plus the built-in seccomp profile appear in security options.
  • A disposable hello-world image runs successfully and is removed when it exits.
  • New Compose workloads inherit bounded logging and no-new-privileges while individual services drop capabilities and use read-only filesystems where supported.

Architecture

How the parts fit together

The Docker CLI and Compose plugin send privileged API requests through a root-owned Unix socket to dockerd. Dockerd coordinates containerd, OCI runtimes, network rules, image storage, volumes and container logs on the host.

Docker CLI and Compose pluginTranslate operator commands and Compose models into daemon API requests.
dockerdRuns as a privileged host service and owns containers, images, networks, volumes and daemon defaults.
containerd and OCI runtimeManage container lifecycle and create isolated processes using kernel namespaces and cgroups.
Unix socketRestricts local daemon control through filesystem permissions; possession of access is effectively root access.
daemon.jsonDefines host-wide defaults such as logging, live restore and no-new-privileges.
  1. An administrator invokes docker or docker compose through sudo.
  2. The CLI sends a request over /var/run/docker.sock to the privileged daemon.
  3. dockerd pulls or builds content, asks containerd to create the container, and programs storage and networking.
  4. Kernel namespaces, cgroups, capabilities, AppArmor and seccomp constrain the process according to daemon and workload policy.

Assumptions

  • The host is a supported 64-bit Ubuntu 24.04 system and is not an unsupported derivative.
  • Conflicting docker.io, podman-docker, containerd or runc packages and any existing /var/lib/docker state have been inventoried before repository changes.
  • Docker's published-port firewall behavior is understood. The current Docker documentation warns that exposed container ports can bypass UFW or firewalld policy and that Docker manages iptables-compatible rules.
  • The host uses sudo for daemon control. Rootless mode is evaluated separately if unprivileged operation is a requirement.
  • A maintenance window exists for the deliberate daemon restart, and every existing workload has a recovery or restart plan.

Key concepts

Docker daemon
The privileged service that accepts API requests and controls host resources. Access to it can be used to gain full host control.
Image
An immutable content-addressed filesystem and metadata template used to create containers.
Container
A host process with isolation and resource controls, not a virtual-machine security boundary by itself.
Capability
A discrete Linux privilege. Dropping all and adding only required capabilities reduces the power of a compromised process.
Seccomp and AppArmor
Kernel controls that restrict system calls and filesystem or process actions in addition to namespaces and capabilities.
Live restore
A daemon feature that can keep compatible containers running while dockerd is unavailable, but does not guarantee application continuity for every upgrade or configuration change.

Before you copy

Values used in this guide

{{ubuntuCodename}}

Ubuntu release codename used in Docker's APT source.

Example: noble

Security and production boundaries

  • Members of the docker group can mount the host root filesystem or control privileged containers. Treat the group as an administrator boundary, not a convenience permission.
  • Never expose the daemon on plain TCP. Even a firewall-limited daemon endpoint can be reachable from containers and must use authenticated TLS or an SSH transport.
  • A container running as root still maps to powerful host identities unless rootless or user namespace mechanisms are deliberately configured.
  • Docker manipulates host packet-filtering rules for published ports. Design the host firewall and DOCKER-USER policy with Docker rather than assuming UFW alone controls exposure.

Stop before continuing if

  • Stop if an existing container runtime, production workload or /var/lib/docker data set has not been inventoried and backed up.
  • Stop if the Docker APT key or repository source cannot be verified or does not match the host architecture and Ubuntu codename.
  • Stop if daemon.json is invalid or Docker cannot restart; restore the previous file before further changes.
  • Stop if the daemon binds to TCP 2375/2376 unexpectedly or security options lose the expected AppArmor/seccomp controls.
  • Stop before adding a user to the docker group unless root-equivalent access is explicitly approved.
01

command

Install repository prerequisites

caution

Install CA certificates, curl, and the apt keyring directory.

Why this step matters

Install only the tools needed to verify HTTPS downloads and create a scoped APT keyring before trusting a new package source.

What to understand

ca-certificates supplies the TLS trust store and curl retrieves the official signing key. The dedicated keyring directory avoids the deprecated global apt-key trust model.

The directory is world-readable but root-writable; APT's unprivileged helpers must be able to read repository keys.

System changes

  • Refreshes APT metadata, installs ca-certificates and curl, and creates /etc/apt/keyrings with mode 0755.

Syntax explained

apt install --yes
Installs named packages and accepts the package confirmation.
install -m 0755 -d
Creates the directory with explicit permissions.
Command
sudo apt update && sudo apt install --yes ca-certificates curl && sudo install -m 0755 -d /etc/apt/keyrings
Example output / evidence
/etc/apt/keyrings created

Checkpoint: Confirm prerequisite paths

curl --version | head -n 1 && stat -c '%U:%G %a %n' /etc/apt/keyrings

Continue whencurl is available and the keyring directory is root-owned mode 755.

Stop whenAPT is incomplete or the directory is writable by non-root users.

Security notes

  • This step changes packages but does not yet trust Docker's repository.

Alternatives

  • Use an approved internal mirror that preserves signature verification.

Stop conditions

  • Resolve package-manager errors before adding another source.
02

config

Add Docker's signing key and repository

caution

Use Docker's official deb822 repository definition, scoped to its signing key and the verified Ubuntu codename.

Why this step matters

Scope Docker's signing key to one deb822 source so the key cannot authenticate unrelated repositories.

What to understand

curl fails on HTTP errors, follows the official redirect and writes the ASCII-armored key to the dedicated keyring.

The docker.sources file binds URI, suite, architecture and Signed-By path. Replace the codename only with the value reported by /etc/os-release.

System changes

  • Creates /etc/apt/keyrings/docker.asc and /etc/apt/sources.list.d/docker.sources.

Syntax explained

curl --fail --show-error --location
Fails on HTTP errors, retains diagnostics and follows redirects.
--output
Writes the key to the exact root-controlled path.
Signed-By
Limits repository authentication to Docker's dedicated key.
Architectures
Restricts packages to the host architecture reported by dpkg.
File /etc/apt/sources.list.d/docker.sources
Configuration
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: {{ubuntuCodename}}
Components: stable
Architectures: amd64
Signed-By: /etc/apt/keyrings/docker.asc
Command
sudo curl --fail --show-error --location https://download.docker.com/linux/ubuntu/gpg --output /etc/apt/keyrings/docker.asc && sudo chmod a+r /etc/apt/keyrings/docker.asc
Example output / evidence
/etc/apt/keyrings/docker.asc downloaded
Docker repository definition saved for noble

Checkpoint: Inspect source and key fingerprint

gpg --show-keys --with-fingerprint /etc/apt/keyrings/docker.asc && cat /etc/apt/sources.list.d/docker.sources

Continue whenThe official Docker key is readable and the source uses download.docker.com, the host architecture and intended Ubuntu codename.

Stop whenThe fingerprint, URI, architecture, suite or Signed-By path is unexpected.

If this step fails

APT reports NO_PUBKEY or conflicting Signed-By values.

Likely causeThe key is unreadable, the path is wrong, or an older Docker source exists with a different key configuration.

Safe checks
  • ls -l /etc/apt/keyrings/docker.asc
  • grep -Rns 'download.docker.com' /etc/apt/sources.list /etc/apt/sources.list.d

ResolutionRemove only the obsolete duplicate source, correct key readability and refresh APT metadata.

Security notes

  • Do not disable signature verification to work around a repository error.

Alternatives

  • Pin the official repository through an internal signed mirror under organizational control.

Stop conditions

  • Do not install until apt-cache policy shows the expected Docker origin.
03

command

Install Engine and plugins

caution

Install Engine, CLI, containerd, Buildx, and Compose.

Why this step matters

Install the daemon, matching CLI, bundled containerd and supported plugins as one coherent package set.

What to understand

docker-ce provides the daemon, docker-ce-cli the client, containerd.io the tested runtime bundle, and the two plugins provide build and Compose workflows.

For production, list available versions and pin an approved version instead of automatically taking a new major release.

System changes

  • Installs and starts Docker Engine, creates the docker group and socket, and initializes runtime directories under /var/lib/docker and /var/lib/containerd.

Syntax explained

docker-ce / docker-ce-cli
Install Docker's daemon and matching command-line client.
containerd.io
Install Docker's supported containerd and runtime dependency bundle.
docker-buildx-plugin
Adds extended BuildKit-based build commands.
docker-compose-plugin
Adds Compose v2 as docker compose.
Command
sudo apt update && sudo apt install --yes docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Example output / evidence
Setting up docker-ce ...
Setting up docker-compose-plugin ...

Checkpoint: Verify package origin and service

apt-cache policy docker-ce | sed -n '1,12p' && systemctl is-active docker

Continue whenThe candidate originates from download.docker.com and the daemon is active.

Stop whenPackages come from an unintended source or the service is failed.

If this step fails

Docker installation completes but the service is failed.

Likely causeA conflicting runtime, storage driver, firewall setup or old daemon configuration prevents startup.

Safe checks
  • systemctl status docker --no-pager
  • sudo journalctl -u docker -n 100 --no-pager
  • sudo dockerd --validate

ResolutionCorrect the reported conflict without deleting runtime data, then start the service and repeat package verification.

Security notes

  • The privileged daemon and its socket now exist; keep access root-controlled.

Alternatives

  • Install an explicitly pinned package version after testing upgrades.

Stop conditions

  • Do not configure workloads while daemon health or package origin is ambiguous.
04

config

Set bounded logging and live restore

caution

Prevent unbounded logs and never expose the Docker TCP API without a documented mTLS design.

Why this step matters

Host-wide defaults reduce log exhaustion and privilege gain for every new container, while live restore can reduce disruption during daemon maintenance.

What to understand

The local log driver rotates files using max-size and max-file. Existing containers retain the logging configuration they were created with.

no-new-privileges prevents processes from gaining privileges through setuid or file capabilities. Workloads that require those mechanisms must be reviewed rather than globally exempted.

System changes

  • Creates /etc/docker/daemon.json and changes defaults applied after daemon validation and restart.

Syntax explained

log-driver: local
Uses Docker's optimized local log format with rotation support.
max-size: 10m
Rotates each log segment around ten megabytes.
max-file: 3
Retains at most three local rotated segments per container.
live-restore: true
Allows compatible containers to remain running while the daemon is unavailable.
no-new-privileges: true
Sets the Linux no-new-privileges bit by default for new containers.
File /etc/docker/daemon.json
Configuration
{
  "log-driver": "local",
  "log-opts": { "max-size": "10m", "max-file": "3" },
  "live-restore": true,
  "no-new-privileges": true
}
Example output / evidence
daemon.json saved as valid JSON

Checkpoint: Validate the candidate without restarting

jq empty /etc/docker/daemon.json && sudo dockerd --validate --config-file=/etc/docker/daemon.json

Continue whenJSON parsing succeeds and dockerd reports configuration validation successful.

Stop whenJSON is invalid, an option is unknown or conflicts with a daemon flag.

Security notes

  • Do not add hosts entries that publish the daemon TCP API.

Alternatives

  • Override logging or privileges per service only when the exception is documented and tested.

Stop conditions

  • Back up an existing daemon.json and validate the complete merged replacement before restart.
05

verification

Validate JSON and restart deliberately

caution

A restart may affect workloads that cannot use live restore.

Why this step matters

A controlled restart is the point where daemon defaults take effect, so gate it on validation and immediately prove service recovery.

What to understand

jq catches JSON syntax and dockerd validation catches daemon-specific issues. The restart may disrupt unmanaged or incompatible workloads despite live restore.

Inspect existing containers before the window and confirm them afterward; active service state alone does not prove workload health.

System changes

  • Restarts the privileged Docker daemon and applies daemon.json defaults for subsequently created containers.

Syntax explained

jq empty
Parses JSON and emits nothing on success.
systemctl restart docker
Stops and starts the daemon under systemd.
systemctl is-active
Requires the resulting unit state to be active.
Command
jq empty /etc/docker/daemon.json && sudo systemctl restart docker && systemctl is-active docker
Example output / evidence
active

Checkpoint: Confirm daemon and existing workloads

systemctl is-active docker && sudo docker ps --format 'table {{.Names}}\t{{.Status}}'

Continue whenDocker is active and every expected workload is present with a healthy status.

Stop whenThe daemon is failed or a known workload is missing or unhealthy.

If this step fails

Restart leaves docker.service failed.

Likely causedaemon.json is invalid at runtime or conflicts with systemd flags.

Safe checks
  • sudo journalctl -u docker -n 100 --no-pager
  • systemctl cat docker
  • sudo dockerd --validate --config-file=/etc/docker/daemon.json

ResolutionRestore the previous daemon.json, restart Docker and verify workloads before reworking the candidate.

Security notes

  • Preserve /var/lib/docker; configuration rollback must not become destructive data cleanup.

Alternatives

  • Reload only options documented as reloadable, but still verify their effective behavior.

Stop conditions

  • Rollback immediately if critical containers do not recover.
06

verification

Verify versions and security options

read-only

Record Engine, Compose, AppArmor, and seccomp state.

Why this step matters

Record the actual client, server, Compose and kernel security controls so future incidents and upgrades have a known baseline.

What to understand

docker version distinguishes client from server. Compose is a plugin with its own release. docker info exposes enabled security options.

The Go template returns JSON text suitable for unambiguous recording. Missing AppArmor can be legitimate on another distribution but is unexpected in this Ubuntu baseline.

System changes

  • No persistent changes; queries daemon and plugin metadata.

Syntax explained

docker version
Shows client and daemon API versions.
docker compose version
Shows the installed Compose v2 plugin version.
docker info --format
Prints selected daemon information using a Go template.
{{json .SecurityOptions}}
Serializes the daemon security-options field as JSON.
Command
sudo docker version && sudo docker compose version && sudo docker info --format '{{json .SecurityOptions}}'
Example output / evidence
Docker Engine 28.x
Docker Compose v2.x
["name=apparmor","name=seccomp,profile=builtin"]

Checkpoint: Save the security baseline

sudo docker version && sudo docker compose version && sudo docker info --format '{{json .SecurityOptions}}'

Continue whenEngine 29.x, Compose v2, and expected AppArmor plus builtin seccomp options are visible.

Stop whenClient/server versions are incompatible or expected kernel controls are absent.

Security notes

  • Version metadata is safe to record, but do not publish full docker info output without reviewing host details.

Alternatives

  • Export the selected fields into inventory management.

Stop conditions

  • Investigate missing seccomp/AppArmor before production workloads.
07

command

Run a disposable smoke test

caution

Run and remove the official hello-world container.

Why this step matters

A disposable official sample proves registry pull, image extraction, runtime creation, network egress and log delivery end to end.

What to understand

Docker pulls hello-world only if absent, creates a short-lived container and prints a confirmation. --rm removes the stopped container, not the image.

This is a platform smoke test, not a security assessment of arbitrary images. Production images need provenance and vulnerability controls.

System changes

  • May download the hello-world image and creates then removes one disposable container.

Syntax explained

docker run
Creates and starts a container from an image.
--rm
Removes the container automatically after it exits.
hello-world
Selects Docker's small installation-test image.
Command
sudo docker run --rm hello-world
Example output / evidence
Hello from Docker!

Checkpoint: Require the expected greeting

sudo docker run --rm hello-world

Continue whenHello from Docker! and exit code 0.

Stop whenPull, DNS, storage, runtime or container execution fails.

Security notes

  • Do not generalize successful execution of one image into trust of all registry content.

Alternatives

  • Use an internally mirrored, signed smoke image in restricted environments.

Stop conditions

  • Resolve platform errors before deploying Compose workloads.
08

config

Create a least-privilege Compose baseline

caution

Pin images, drop capabilities, use read-only filesystems, and keep secrets outside the file.

Why this step matters

A compact baseline demonstrates the controls each application team should start from rather than relying only on daemon defaults.

What to understand

read_only protects the image filesystem, tmpfs supplies writable temporary space, and cap_drop removes kernel capabilities. Applications may need explicit writable volumes or a narrowly added capability.

Pin production images by digest after testing. A semantic tag alone can move to different content.

System changes

  • Creates compose.yaml for an example service; no container is started by this step.

Syntax explained

image
Selects the tested image reference; add an immutable digest in production.
read_only
Mounts the container root filesystem read-only.
cap_drop: [ALL]
Removes all Linux capabilities before any explicit additions.
no-new-privileges:true
Prevents process privilege gains inside the container.
tmpfs: [/tmp]
Provides ephemeral writable memory-backed storage for temporary files.
restart: unless-stopped
Restarts after failures or reboot unless an operator intentionally stopped it.
File compose.yaml
Configuration
services:
  app:
    image: example/app:1.2.3
    read_only: true
    cap_drop: [ALL]
    security_opt: [no-new-privileges:true]
    tmpfs: [/tmp]
    restart: unless-stopped
Example output / evidence
docker compose config renders one hardened service

Checkpoint: Render the effective Compose model

docker compose config --quiet && docker compose config

Continue whenValidation succeeds and the rendered service contains the reviewed restrictions without secret values.

Stop whenThe service needs privileged mode, broad host mounts or unresolved values.

Security notes

  • A read-only filesystem and dropped capabilities are defense in depth, not proof that an image is trustworthy.

Alternatives

  • Document the minimum capability or writable mount when an application cannot run under this baseline.

Stop conditions

  • Do not weaken all controls merely to make an unknown image start.
09

warning

Treat Docker access as root access

caution

Do not add general users to the docker group as a convenience.

Why this step matters

Make the privilege boundary explicit because adding a user to the docker group silently grants practical control over the host.

What to understand

A Docker API user can bind-mount /, access devices or start privileged containers. Socket permissions therefore protect a root-equivalent interface.

Use named administrators with sudo audit trails or rootless mode; never chmod the socket to make a permission error disappear.

System changes

  • No automatic change; records the access decision and prevents an unsafe convenience action.
Example output / evidence
Privilege decision recorded

Checkpoint: Audit socket access

getent group docker && stat -c '%U:%G %a %n' /var/run/docker.sock

Continue whenOnly approved administrators are members and the socket is root:docker mode 660 or stricter.

Stop whenA general user, service account or web process has daemon access without root-equivalent approval.

Security notes

  • Treat any exposed docker.sock mount as a critical host-control path.

Alternatives

  • Use sudo with command logging or deploy documented rootless mode.

Stop conditions

  • Remove unauthorized access and review daemon audit evidence before continuing.

Finish line

Verification checklist

Daemonsystemctl is-active dockerReturns active.
Composedocker compose versionReports Compose v2.
Socket exposuress -lntp | grep -E ':2375|:2376' || trueNo unexpected Docker TCP listener.

Recovery guidance

Common problems and safe checks

APT reports conflicting packages or cannot install containerd.io.

Likely causeDistribution containerd, runc, docker.io, podman-docker or an old Compose package remains installed.

Safe checks
  • dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc
  • apt-cache policy docker-ce containerd.io

ResolutionPlan removal of conflicting packages and preserve existing runtime data; do not purge /var/lib/docker or /var/lib/containerd.

Docker fails to start after daemon.json is installed.

Likely causeThe JSON is malformed, an option conflicts with a systemd command-line flag, or a configured feature is unsupported.

Safe checks
  • jq empty /etc/docker/daemon.json
  • sudo dockerd --validate --config-file=/etc/docker/daemon.json
  • sudo journalctl -u docker -n 100 --no-pager

ResolutionRestore the backed-up file, start Docker, then correct and validate the candidate before another controlled restart.

docker commands return permission denied on /var/run/docker.sock.

Likely causeThe operator is not root and is intentionally not a member of the root-equivalent docker group.

Safe checks
  • stat -c '%U:%G %a %n' /var/run/docker.sock
  • id

ResolutionUse sudo for the administrative command or deploy rootless mode; do not broaden socket permissions.

A container port is reachable despite an expected UFW deny.

Likely causeDocker's published-port rules bypass the UFW path, as documented for the supported installation.

Safe checks
  • docker ps --format '{{.Names}} {{.Ports}}'
  • sudo iptables -S DOCKER-USER
  • sudo ss -lntup

ResolutionRemove unintended published ports and implement reviewed filtering in the Docker-compatible path, including DOCKER-USER where appropriate.

After the procedure

Alternatives and next steps

Consider these alternatives

  • Use Docker rootless mode when its networking, storage and privileged-feature limitations fit the workload.
  • Use Podman or another daemonless runtime when the operational model and application tooling support it; do not install compatibility packages alongside Docker without reviewing conflicts.
  • Use SSH transport for remote Docker administration rather than publishing the daemon API.

Operate it safely

  • Define an upgrade policy that pins and tests a specific Engine version before production rollout.
  • Configure image provenance, vulnerability scanning and digest pinning in the build and deployment workflow.
  • Add per-service CPU, memory, PID and storage limits based on observed workload requirements.
  • Back up and restore named volumes and configuration independently of container images.
  • Monitor daemon health, disk usage, container restart counts and log retention.

Reference

Frequently asked questions

Is membership in the docker group safer than sudo?

No. Docker control can mount host filesystems and start highly privileged containers, so group membership is effectively root-equivalent.

Does live-restore mean restarting Docker is risk-free?

No. It can keep compatible containers running during daemon unavailability, but networking, plugins, upgrades and application behavior still require a maintenance plan.

Why use the local log driver?

It provides built-in rotation and a compact format for local operation. Centralized logging still requires an explicit collection and retention design.

Recovery

Rollback

Restore the previous daemon configuration or package set.

  1. Stop Compose applications cleanly.
  2. Restore and validate /etc/docker/daemon.json.
  3. Restart Docker and verify known workloads.
  4. Preserve /var/lib/docker until data retention is decided.

Evidence

Sources and review

Verified 2026-07-24Review due 2026-10-22
Install Docker Engine on UbuntuofficialDocker daemon referenceofficialDocker securityofficial