Skip to content

Docker Mounting

  • All data written inside a container’s writable layer is ephemeral - it disappears when the container is removed.
  • Every container gets its own MNT namespace - an isolated file tree rooted at /. Docker mounts the container image at this root, then attaches external storage (volumes, bind mounts, tmpfs) at specific paths within that tree.
  • To persist data or share it between containers and the host, you attach external storage via a mount.
  • Three main mount types: Volumes (Docker-managed), Bind Mounts (host path), tmpfs (memory-only).
  • Stateful applications (databases, file servers, message queues) must use volumes or mounts - data must outlive the container. Stateless applications can rely on the ephemeral writable layer for temporary data only.
TypeManaged byPersists after docker rmPerformanceBest For
VolumeDockerYesNativeProduction data, database storage
Bind MountHost OSYes (host file)NativeDevelopment, config injection
tmpfsKernel (memory)NoFastestSecrets at runtime, temp caches
Volumes
  • Volumes are the recommended way to persist data in production. Docker creates a directory on the host (typically /var/lib/docker/volumes/<name>/_data) and manages it independently of the container lifecycle.

  • Volumes can be shared across multiple containers and survive container removal.

  • Native I/O performance: Volumes bypass the container’s Copy-on-Write filesystem layer entirely, giving native disk throughput. Critical for I/O-intensive workloads like databases - CoW overhead degrades performance under sustained write load.

  • Host-independent portability: Unlike bind mounts, volumes don’t require containers to know the host directory structure. The same image runs identically on any machine.

  • Polymorphic container pattern: The image packages static software; the volume provides dynamic data. A database image always runs the same binary - the mounted volume defines what data it holds. Same image + different volume = different database (prod, staging, test) with zero code changes.

    Polymorphic Container
    Terminal window
    # Create a named volume
    docker volume create db-data
    # Create with a label (for filtering and organizing volumes)
    docker volume create --label env=prod db-data
    # List volumes
    docker volume ls
    # Inspect a volume - the "Mountpoint" field in output shows the exact host path where data lives
    docker volume inspect db-data
    # Mount a volume at container run time
    docker run -d \
    -v db-data:/var/lib/postgresql/data \ # named volume syntax
    postgres:16-alpine
    # Same with --mount (more explicit, preferred in scripts)
    docker run -d \
    --mount type=volume,src=db-data,dst=/var/lib/postgresql/data \
    postgres:16-alpine
    # Read-only volume
    docker run -d \
    --mount type=volume,src=config-vol,dst=/etc/app,ro \
    my-app
    # Remove a volume (only when no containers use it)
    docker volume rm db-data
    # Remove volumes not currently mounted to any container or service
    docker volume prune
    # Remove ALL volumes on the host - including unmounted ones (data loss risk)
    docker volume prune --all
    # Target specific volumes using a filter (e.g., by label)
    docker volume prune --filter label=env=staging
    # Suppress confirmation prompt (for automation/scripts)
    docker volume prune --force

Named vs anonymous volumes:

Named Volumes
  • Named volumes (docker volume create my-data or -v my-data:/path) persist until explicitly deleted - they survive all container lifecycles, including docker run --rm. Useful for periodic job containers that must preserve output even after auto-cleanup.
  • Anonymous volumes (created without a name, e.g., from a bare VOLUME instruction in a Dockerfile) are automatically deleted when the container is removed with docker run --rm or docker rm -v.

On Linux: /var/lib/docker/volumes/<volume-name>/_data

On Docker Desktop (macOS/Windows), Docker runs inside a Linux VM. Volume data is inside that VM’s filesystem - not directly accessible from your Mac/Windows filesystem. To inspect it:

Terminal window
# Access the Docker Desktop VM filesystem (use this exact pinned image - security)
docker run -it --rm --privileged --pid=host \
justincormack/nsenter1@sha256:5af0be5e42ebd55eea2c593e4622f810065c3f45bb805eaacf43f08f3d06ffd8
# Then navigate to the volume data inside the VM
ls /var/lib/docker/volumes/db-data/_data
Volumes
  • Maps a specific host path directly into the container. Changes are immediately visible on both sides.
  • Ideal for development (mount source code into a container so edits are reflected live), injecting configuration files, or exporting container output - e.g., bind-mounting a log directory so a log-forwarding agent on the host can read it directly.
  • Not portable - depends on a specific host path existing. Avoid in production images.
  • Mount shadowing: When you mount to a directory that already has content from the image (e.g., /app), the mount hides the image’s original files for the life of the container. The image files are not deleted - just obscured. Unmounting restores visibility.
Terminal window
# Bind mount the current directory to /app in the container
docker run -d \
-v $(pwd):/app \ # short syntax
-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \ # read-only
my-app
# --mount equivalent (clearer for CI/CD scripts)
docker run -d \
--mount type=bind,src=$(pwd),dst=/app \
my-app
# Read-only bind mount via --mount (use readonly=true, not :ro)
docker run -d \
--mount type=bind,src=$(pwd)/config,dst=/etc/app,readonly=true \
my-app
Volumes
  • Data stored in memory only - never written to disk. Lost when the container stops or restarts.
  • Use for sensitive data (tokens, API keys at runtime) that must not be persisted to disk, or for high-speed temporary caches.
  • Docker’s tmpfs defaults (applied automatically unless overridden): no size limit, world-writable (1777 permissions), noexec (files cannot be executed), nodev (no special device files), suid bits ignored. Sensible for scratch space, but always set tmpfs-size and tmpfs-mode for security-critical mounts.
Terminal window
# Mount a tmpfs at /tmp/cache in the container
docker run -d \
--mount type=tmpfs,dst=/tmp/cache \
my-app
# With size and mode options
# tmpfs-mode=1700 = octal 0o1700 = sticky bit + rwx for owner only (no group/other access)
docker run -d \
--mount type=tmpfs,dst=/tmp/secrets,tmpfs-size=64m,tmpfs-mode=1700 \
my-app
  • Secrets and Configs are Docker Swarm primitives for distributing sensitive data to services without embedding it in images or environment variables.
  • Only available in Swarm mode - not in standalone docker run or Compose without Swarm.
Terminal window
# Create a secret from a file
echo "supersecretpassword" | docker secret create db_password -
# Use in a Swarm service
docker service create \
--name my-app \
--secret db_password \
my-app-image
# Secret is mounted at: /run/secrets/db_password
  • In Compose with Swarm, use the secrets: key. For non-Swarm Compose, use environment variables from .env files or bind-mount config files instead.

By default, volumes are stored on the local host filesystem. Volume drivers let you back a named volume with remote or distributed storage:

Terminal window
# Example: NFS-backed volume (useful with AWS EFS on EC2)
docker volume create \
--driver local \
--opt type=nfs \
--opt o=addr=efs.example.com,rw \
--opt device=:/ \
efs-data

Cloud-native alternatives: AWS EFS CSI driver (EKS), Azure Files (AKS), GCS Fuse - these are typically configured at the orchestrator level, not directly with Docker.

Popular third-party volume plugins: REX-Ray, Portworx, Docker CloudStor, Flocker, Blockbridge. These integrate with AWS EBS, SAN, NAS, NFS, Ceph, and vSphere. Plugins are managed via docker plugin subcommands:

Terminal window
# List installed plugins
docker plugin ls
# Install a plugin
docker plugin install rexray/ebs
# Create a volume using the installed driver
docker volume create --driver rexray/ebs ebs-vol
  • Databases and stateful services in production → Named Volume
  • Source code in development → Bind Mount (live reload)
  • Config files → Bind Mount (read-only) or Docker Secret (Swarm)
  • Build artifacts or temp files → tmpfs or anonymous volume
  • Sharing data between containers → Named Volume
  • Cross-host persistence (multi-node) → Volume with NFS/cloud driver or orchestrator CSI