docker
containerization
privileged mode
software development
DevOps

Build with docker and --privileged

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The --privileged flag in Docker gives a container almost full access to the host system — all Linux capabilities, all devices, and the ability to modify kernel parameters. While this is sometimes necessary for builds that need low-level system access (like building kernel modules, running Docker-in-Docker, or mounting filesystems), it completely breaks container isolation. The better approach is to grant only the specific capabilities needed using --cap-add rather than opening everything with --privileged.

What --privileged Does

A normal Docker container runs with a restricted set of Linux capabilities. The --privileged flag removes those restrictions:

bash
1# Normal container — limited capabilities
2docker run --rm alpine cat /proc/self/status | grep Cap
3# CapBnd: 00000000a80425fb
4
5# Privileged container — all capabilities
6docker run --rm --privileged alpine cat /proc/self/status | grep Cap
7# CapBnd: 000001ffffffffff

Specifically, --privileged grants:

  • All Linux capabilities (CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_SYS_PTRACE, etc.)
  • Access to all host devices (/dev/sda, /dev/mem, etc.)
  • Ability to mount filesystems, load kernel modules, and modify kernel parameters
  • Disabled seccomp, AppArmor, and SELinux profiles

Building with --privileged

Some build operations require privileges not available in a standard container:

bash
1# Docker-in-Docker: building images inside a container
2docker run --privileged -v /var/run/docker.sock:/var/run/docker.sock \
3  docker:latest docker build -t my-app .
4
5# Building a filesystem image that requires mount
6docker run --privileged ubuntu bash -c "
7  dd if=/dev/zero of=/tmp/disk.img bs=1M count=100
8  mkfs.ext4 /tmp/disk.img
9  mount -o loop /tmp/disk.img /mnt
10  cp -r /app/* /mnt/
11  umount /mnt
12"

In Docker BuildKit, use --allow security.insecure instead of --privileged for build-time privileges:

bash
# BuildKit with insecure mode
docker buildx build --allow security.insecure -t my-app .
dockerfile
1# syntax=docker/dockerfile:1
2FROM ubuntu AS builder
3RUN --security=insecure mount -t tmpfs tmpfs /mnt && \
4    echo "privileged build step"

Safer Alternatives

Instead of --privileged, use the minimum capabilities needed:

bash
1# Need to change network settings? Add only NET_ADMIN
2docker run --cap-add NET_ADMIN alpine ip link set eth0 down
3
4# Need to use ptrace for debugging?
5docker run --cap-add SYS_PTRACE alpine strace ls
6
7# Need to load a kernel module?
8docker run --cap-add SYS_MODULE -v /lib/modules:/lib/modules alpine \
9  modprobe ip_tables
10
11# Need access to a specific device?
12docker run --device /dev/fuse alpine ls /dev/fuse

Common Capability Mappings

Use CaseCapabilityInstead of --privileged
Network configuration--cap-add NET_ADMINYes
Mount filesystems--cap-add SYS_ADMINYes
Debug with ptrace--cap-add SYS_PTRACEYes
Modify system time--cap-add SYS_TIMEYes
Access raw sockets--cap-add NET_RAWYes
Load kernel modules--cap-add SYS_MODULEYes

Docker-in-Docker Without --privileged

For CI/CD pipelines that need to build Docker images, avoid --privileged by using rootless alternatives:

bash
1# Option 1: Mount the Docker socket (no --privileged needed)
2docker run -v /var/run/docker.sock:/var/run/docker.sock \
3  docker:latest docker build -t my-app .
4
5# Option 2: Use Kaniko (no Docker daemon, no privileges)
6docker run \
7  -v $(pwd):/workspace \
8  gcr.io/kaniko-project/executor:latest \
9  --dockerfile /workspace/Dockerfile \
10  --destination my-registry/my-app:latest \
11  --context /workspace
12
13# Option 3: Use BuildKit with rootless mode
14docker run --security-opt seccomp=unconfined --security-opt apparmor=unconfined \
15  moby/buildkit:rootless --oci-worker-no-process-sandbox \
16  buildctl build --frontend dockerfile.v0 --local context=. --local dockerfile=.

Docker Compose with Privileged

yaml
1version: "3.8"
2services:
3  builder:
4    image: docker:latest
5    privileged: true  # Use sparingly
6    volumes:
7      - /var/run/docker.sock:/var/run/docker.sock
8      - .:/workspace
9    command: docker build -t my-app /workspace
10
11  # Better: use cap_add instead
12  network-tool:
13    image: alpine
14    cap_add:
15      - NET_ADMIN
16    cap_drop:
17      - ALL  # Drop everything first, then add only what you need

Common Pitfalls

  • Using --privileged for Docker-in-Docker in CI: Mounting the Docker socket (-v /var/run/docker.sock) or using Kaniko is safer. --privileged in CI gives every build full host access, which is a major security risk.
  • Leaving --privileged in production: The flag is sometimes added during debugging and forgotten. A privileged container can escape to the host — never use it in production deployments.
  • Not dropping capabilities first: When using --cap-add, also use --cap-drop ALL to start with zero capabilities, then add only what you need. Without --cap-drop ALL, the container still has the default set.
  • Assuming --privileged is needed for /dev access: Use --device /dev/fuse or --device /dev/kvm to expose specific devices. --privileged exposes every device on the host.
  • BuildKit security flag confusion: docker buildx build --allow security.insecure is build-time only and scoped to individual RUN steps. It is safer than running the entire container as privileged.

Summary

  • --privileged gives a container full host access — all capabilities, all devices, disabled security profiles
  • Use --cap-add with specific capabilities instead of --privileged whenever possible
  • For Docker-in-Docker builds, prefer socket mounting, Kaniko, or rootless BuildKit
  • In Docker Compose, use cap_add and cap_drop: [ALL] for fine-grained control
  • Never leave --privileged in production — it is a debugging and build-time convenience, not a runtime configuration

Course illustration
Course illustration

All Rights Reserved.