Blog

A Practical Docker Isolation Security Checklist for Multi-Tenant Hosting

Secure your multi-tenant Docker hosting with this practical checklist covering non-root users, capabilities, seccomp, user namespaces, resource limits, and read-only filesystems.

Summary

Multi-tenant Docker hosting requires strong isolation to prevent container escapes. This article provides a practical security checklist covering six key areas: run as non-root, drop capabilities, apply seccomp profiles, enable user namespace remapping, set resource limits, and use read-only root filesystems. Each step includes a concrete configuration example for Docker Compose. You'll also learn common pitfalls like kernel compatibility issues with user namespaces and performance trade-offs when applying seccomp. By following this checklist, you can significantly reduce the attack surface without adding unnecessary complexity. The article concludes with a recommended baseline configuration for production multi-tenant environments.

If you run a multi-tenant Docker environment, the specter of a container escape attack keeps you up at night. One kernel exploit can break out of a container and give an attacker unfettered access to the host and all other tenants' data. While Docker provides powerful isolation primitives—namespaces, cgroups, and capabilities—misconfiguration leaves gaps. This article presents a step-by-step security checklist that you can apply today. Each step includes a working Docker Compose snippet and key caveats. By the end, you'll have a hardened baseline that balances security and performance.

1. Run Containers as a Non-Root User

Containers default to running as root inside the container. If an attacker gains root in the container, they have a head start on escaping. Always define a non-root user in your Dockerfile.

FROM alpine:3.18
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

In Compose, you can also set the user directly:

services:
  app:
    image: myapp
    user: "1000:1000"

Caveat: Some applications require root for legit operations (e.g., binding to ports below 1024). Use CAP_NET_BIND_SERVICE instead of running the whole container as root. For a deeper look at isolation fundamentals, see our guide on achieving true multi-tenant isolation in Docker.

2. Drop All Capabilities and Add Only What’s Needed

Linux capabilities give containers fine-grained privileges. By default, Docker grants a set of capabilities. Drop everything and grant only those required.

services:
  app:
    image: myapp
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE  # if needed

Caveat: Capabilities like SYS_ADMIN or NET_RAW are rarely needed. Audit your application to determine the minimum set. Dropping all capabilities blocks many escape vectors.

3. Apply a Seccomp Profile

Seccomp (secure computing mode) filters system calls available to a container. Docker ships with a default seccomp profile that blocks dangerous syscalls like clone with certain flags. You can customize it further.

services:
  app:
    image: myapp
    security_opt:
      - seccomp=/path/to/custom-profile.json

A hardened profile might block unshare, ptrace, and mount. Start with Docker's default and restrict more. Caveat: Overly strict profiles can break applications. Test thoroughly in a staging environment. For more on container escape defenses, read defending against container escape.

4. Enable User Namespace Remapping

User namespaces map the container’s root user to an unprivileged host user. This means even if an attacker gains root inside the container, they have no special privileges on the host.

Enable it on the Docker daemon by editing /etc/docker/daemon.json:

{
  "userns-remap": "default"
}

Then restart Docker. Caveat: User namespace remapping has two drawbacks: it breaks volume mounts when not configured carefully (files are owned by the remapped user) and is incompatible with some storage drivers like overlay2 on older kernels. Test thoroughly.

5. Set Resource Limits with Cgroups

Resource limits prevent a compromised container from launching a denial-of-service attack against the host. Use cgroups to cap CPU, memory, and disk I/O.

services:
  app:
    image: myapp
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M

For Docker Compose v3, use the deploy section (works with swarm or compose v2). For plain Docker, use --memory and --cpus. Caveat: Setting limits too low can cause OOM kills. Monitor usage and adjust accordingly.

6. Use Read-Only Root Filesystem

A read-only root filesystem prevents attackers from writing malicious binaries or modifying configuration files inside the container.

services:
  app:
    image: myapp
    read_only: true
    tmpfs:
      - /tmp:noexec,nosuid,size=64m

Mount tmpfs on directories that need write access (like /tmp). This forces all writable data to be ephemeral. Caveat: Some applications require persistent storage; use named volumes for that.

Common Pitfalls

  • Kernel compatibility: User namespace remapping and some seccomp rules require a recent Linux kernel (4.14+). Check your kernel version.
  • Performance impact: Seccomp and user namespaces add a tiny overhead, but it's negligible for most workloads. Benchmark your specific app.
  • Complexity: Adding all six measures at once can break things. Apply them one by one, test each change.

For a broader view of orchestration patterns, see our guide on designing a multi-tenant Docker architecture.

Conclusion

A secure multi-tenant Docker host doesn't require exotic tools—just correct use of Docker's built-in features. Start with a non-root user, drop all capabilities, apply a seccomp profile, enable user namespace remapping, set resource limits, and use a read-only filesystem. This checklist forms a strong baseline that blocks the most common escape techniques. After implementing, run security tools like docker-bench-security to verify your configuration. Remember: security is a process, not a product. As new kernel vulnerabilities emerge, revisit your settings. For automated landing pages that showcase your hosting service, use Pagenza to get your site live in minutes.

Sources (5)