How to Secure Docker for Production
A practical Docker production hardening guide covering daemon access, rootless mode, container privileges, images, updates, and troubleshooting.

To secure Docker for production, restrict who can control the daemon, keep its API off untrusted networks, run workloads with the least privilege they need, retain kernel confinement, and manage images as versioned supply-chain inputs. No single Docker flag makes a host secure: review the kernel and host, daemon exposure, container settings, and the way images are built and updated.
This guide gives you a layered baseline for Docker Engine. Treat each example as a starting point to validate against your Engine version, Linux distribution, application, and deployment system. Containers share the host kernel, so they are not a complete security boundary.
1. Inventory the host and Docker Engine
Before changing production settings, record what you are protecting and how Docker is operated. These details determine which controls are available and where a change could break the workload.

- Record the operating system, kernel, Docker Engine version, and whether the host is dedicated to containers.
- Check whether the daemon uses the containerd image store or another configuration. Fresh Docker Engine 29 installations use the containerd image store by default; do not assume older examples match your host.
- List daemon access paths: local Unix socket, TCP listeners, CI runners, administrators, and automation identities.
- Record which host controls are active, including AppArmor or SELinux, and how Engine and OS updates are tested and deployed.
- Identify containers with host networking or PID namespaces, broad bind mounts, devices, elevated capabilities, or privileged mode.
Use docker version, docker info, and your service manager’s configuration to establish the actual state. Review the Docker daemon configuration documentation and the release notes for the Engine version you run. Configuration names and defaults can change over time.
2. Protect access to the Docker daemon
Daemon control is a high-trust permission. A user who can issue arbitrary commands to a rootful daemon can often mount host paths into a container and access or alter host files. Membership in the docker group should therefore be treated as effectively root-level authority, not as a harmless convenience.
Keep the API local unless remote access is necessary
Docker normally accepts local requests through a Unix socket. Limit access to trusted administrators and narrowly scoped automation. Do not make an unauthenticated TCP listener reachable from an untrusted network. If a monitoring or deployment tool needs daemon access, grant only the required host and account access, and audit it like privileged access.
Use SSH or mutually authenticated TLS for remote management
Docker supports SSH and TLS client authentication for remote daemon access. SSH can reuse a controlled host-access workflow; TLS is useful when clients need a certificate-authenticated connection. In either case, restrict network reachability and protect private keys and certificates: their holders can direct the daemon.
# Create a client context that reaches a remote daemon over SSH
docker context create production --docker host=ssh://docker-user@host1.example.com
# Select it and confirm the connection
docker context use production
docker version
For TLS, configure the daemon to require client certificates signed by a trusted CA, then distribute client credentials securely and rotate or revoke them when access changes. Follow Docker’s version-specific setup in Protect the Docker daemon socket and Configure remote access. Do not enable a plain TCP listener as a shortcut.
3. Consider rootless mode
Rootless mode runs both the Docker daemon and containers as a non-root user inside a user namespace. It reduces the daemon’s host authority if the daemon or a container is compromised. It is a risk-reduction measure, not a universal drop-in: ports, networking, volumes, cgroups, service lifecycle, and application assumptions need validation.

Docker documents prerequisites including newuidmap and newgidmap plus at least 65,536 subordinate UIDs and GIDs for the user in /etc/subuid and /etc/subgid. On supported package installations, install as the non-root account:
# Run as the account that will own the rootless daemon
dockerd-rootless-setuptool.sh install
docker context use rootless
docker info
Check the output for the rootless context and security options. Docker’s setup tool can configure the CLI context. Review the rootless mode guide for prerequisites and service setup. In particular, confirm which daemon is active: leaving the system-wide rootful service running means you may still be using rootful Docker.
Test resource limits and service startup on the target host. Docker documents that cgroup-related limits can be ignored when required host conditions are not met. If rootless mode does not fit, document why and assign ownership for the compensating controls on the rootful daemon.
4. Reduce container privileges
Start from the workload’s required permissions and remove what it does not need. Run the application as a non-root user where feasible, avoid --privileged, and do not share host namespaces or mount sensitive host paths without a specific operational need. A container that can write to host control files or access devices has a very different risk profile from an unprivileged web process.
Use a non-root application user and a read-only root filesystem
Set the user in the image or override it at runtime. The following illustrative command also drops capabilities, keeps Docker’s default seccomp profile, and makes the container’s root filesystem read-only. Replace the image, command, mounts, and writable paths with values appropriate to your service:
docker run -d --name web \
--user 10001:10001 \
--read-only \
--cap-drop ALL \
--security-opt no-new-privileges:true \
--pids-limit 256 \
--memory 512m \
--cpus 1.0 \
--tmpfs /tmp:rw,noexec,nosuid,size=64m \
-p 127.0.0.1:8080:8080 \
example/web@sha256:REPLACE_WITH_VERIFIED_DIGEST
This is not a copy-and-paste universal profile. A service may need a specific capability, writable directory, device, or port configuration. Add only the narrowly required permission, document the reason, and test upgrades. Binding to loopback limits exposure to the host; use your ingress or load balancer design for public access.
Keep seccomp and host security modules enabled
Docker’s default seccomp profile is an allowlist and blocks around 44 system calls out of 300-plus. Docker recommends retaining it rather than casually replacing or disabling it. AppArmor or SELinux can provide additional host-level confinement where supported; check that the expected policy is loaded and actually applied to the container.
If an application hits a permission error, identify the denied operation and decide whether it can be removed from the application path or granted narrowly. Do not switch to seccomp=unconfined just to make the error disappear. A custom profile should be justified by workload evidence, reviewed, and regression-tested. See Docker’s seccomp documentation.
5. Treat images as production inputs
Use maintained images from publishers you trust, keep the production image focused on runtime needs, and define how patches move into service. A tag such as stable is a moving name; the content it resolves to can change. Pinning by digest identifies exact content, but pinning without an update process can leave vulnerable software frozen in place.
- Choose a base image with a clear maintenance and update path.
- Build a production image that excludes compilers, test data, credentials, and tools the running service does not need.
- Keep secrets out of image layers and the build context. Supply them through an appropriate runtime secret mechanism.
- Scan and review images in CI, then test and promote updates through your normal release path.
- Record the digest of the image deployed to each environment and retain enough build and release metadata to trace it back to source.
Signatures and attestations can support a provenance policy, but they do not prove that software is vulnerability-free. Decide which identities and signers you trust, where verification happens, how signing keys are stored, and how they are recovered or rotated. Docker Content Trust documentation describes key roles and warns that losing the root key can prevent recovery; check current registry and tooling support before choosing a workflow. See Docker build best practices and Docker content trust.
6. Make updates and configuration review routine
Security settings can drift as images, workloads, hosts, and Docker Engine versions change. Put Engine and OS updates through a defined process: review release notes, test representative workloads, deploy in stages where possible, and verify the resulting configuration. Apply the same discipline to custom seccomp profiles, daemon settings, base images, and signing policy.
Version context matters. For example, daemon configuration and image-store defaults vary across releases, and recent release notes may document security fixes or compatibility issues. Check the documentation and release notes for the exact version before applying a snippet. Do not copy an old workaround, including a less restrictive security profile, without understanding its current security tradeoff.
7. Production hardening checklist
- Daemon: Only trusted administrators and automation identities can control it; the API is local by default.
- Remote access: SSH or client-authenticated TLS is used when remote control is required; credentials are protected and revocable.
- Runtime: Workloads run as non-root where possible; privileged mode, host namespaces, devices, mounts, and capabilities are minimized.
- Confinement: Default seccomp and available AppArmor or SELinux controls remain enabled.
- Filesystem and resources: Read-only filesystems, narrowly scoped writable paths, and resource limits are used where compatible.
- Images: Trusted maintained bases, traceable deployed digests, a tested patch cadence, and an intentional provenance policy are in place.
- Operations: Engine, OS, daemon configuration, and application updates are reviewed and validated after changes.
8. Troubleshooting common hardening problems
| Symptom | Likely cause | What to check or change |
|---|---|---|
permission denied on /var/run/docker.sock |
The account lacks access to the selected daemon socket, or the CLI is targeting the wrong context. | Check docker context show, socket ownership, and the approved access process. Do not solve this by making the socket world-writable or granting broad daemon access. |
| Remote client cannot connect | SSH account permissions, firewall routing, TLS trust, or daemon listener configuration is wrong. | Verify the host and port are reachable only from intended clients, confirm the client certificate and CA, and inspect daemon logs. Avoid falling back to unauthenticated TCP. |
| Rootless install reports missing mappings or helpers | Subordinate ID ranges or newuidmap/newgidmap are missing or incorrect. |
Check the user entries in /etc/subuid and /etc/subgid, install the distribution’s uidmap package, then rerun the documented setup. |
| Rootless resource flags appear ineffective | The host’s cgroup configuration does not support the requested limits. | Confirm cgroup version, delegation, and rootless prerequisites for the OS. Do not assume a supplied limit is enforced; verify behavior on the actual host. |
| Container fails after dropping capabilities or using read-only mode | The application writes to an unexpected path or needs a specific capability. | Inspect logs and denied operations. Provide a narrow writable mount or one justified capability, then add a regression check. Avoid restoring broad privilege as a permanent fix. |
| Application breaks under seccomp | A required syscall is restricted by the profile, or the host/kernel behavior differs. | Reproduce in a controlled environment, inspect syscall or audit evidence, and assess a narrowly scoped custom profile. Keep unconfined mode out of the default production configuration. |
| New image digest behaves differently | The content changed, or the deployment now references a different build than expected. | Compare digests, build metadata, base image updates, and runtime configuration. Use staged promotion and a rollback plan; digest pinning identifies content but does not certify it as safe. |
9. Performance, reliability, and cost
Hardening has operational costs that depend on the workload. Read-only filesystems, non-root users, capability removal, rootless networking, and resource limits can expose assumptions that were previously hidden. Validate startup, health checks, logs, temporary files, graceful shutdown, and recovery under the intended limits before rollout. A limit set too low can cause restarts or degraded service; a limit set too high may fail to constrain an incident.
Remote authentication and image verification add key, certificate, and policy lifecycle work. Rootless mode introduces prerequisites and operational differences. Image digest pinning adds a promotion step. These are maintainability costs to plan for; the dossier provides no measured performance or breach-reduction figures, so do not treat any control as a quantified guarantee. Use staged deployment, monitoring, and a tested rollback path to preserve reliability while tightening controls.
Or skip the browser setup
If your production workflow also needs website screenshots for documentation, QA, or AI-agent tasks, ScreenshotNeo is a website screenshot API and MCP server. One request returns an image or PDF, so your service does not need to set up and operate a browser for that capture. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free account and get 1,000 screenshots a month with no card.
FAQ
Does using Docker make an application secure by itself?
No. Docker adds isolation mechanisms, but security still depends on the host kernel, daemon access, container configuration, images, and operations around the workload.
Is rootless Docker always the best production choice?
It reduces daemon and container authority on the host, but its prerequisites and compatibility constraints make it a workload and operations decision. Validate it on the target environment.
Does signing an image mean it is safe?
No. A signature can establish that an artifact came from an expected signer or process. It does not show that the image contains no vulnerabilities or unsafe code.
Should every capability be removed?
Remove capabilities the process does not need. Test the workload and grant only explicitly justified permissions; some applications require capabilities for legitimate operations.


