When debugging Docker containers, we often need to examine their filesystem contents from the host machine. While docker exec
allows command execution inside containers, sometimes direct filesystem access provides better visibility. The naive approach of browsing /var/lib/docker
exposes implementation details that may change between Docker versions.
Docker provides several built-in methods for container filesystem access:
# Export container filesystem as archive
docker export container_id > container_fs.tar
# Create image from container
docker commit container_id inspection_image
# Copy files from container to host
docker cp container_id:/path/in/container /host/path
For persistent read-only access, we can use these techniques:
Method 1: Using docker run with volumes
# First get the container's rootfs path
CONTAINER_ID=$(docker inspect --format '{{.Id}}' your_container)
HOST_PATH="/var/lib/docker/overlay2/$CONTAINER_ID/merged"
# Mount as read-only
sudo mount --bind -o ro $HOST_PATH /mnt/container_fs
Method 2: Leveraging nsenter
# Get container's PID
CONTAINER_PID=$(docker inspect --format '{{.State.Pid}}' your_container)
# Mount the namespace
sudo nsenter --mount --target $CONTAINER_PID -- sh -c 'mount --bind --ro / /mnt && sleep infinity' &
# Access from host
sudo ls /proc/$CONTAINER_PID/root/
For production systems, consider these robust approaches:
- Create a custom Docker volume plugin
- Use
runc
directly to access container rootfs - Implement FUSE-based solutions for safer access
Always mount container filesystems as read-only when possible. Remember that:
- Direct filesystem access bypasses container isolation
- Some paths (/proc, /sys) appear different from host perspective
- Permissions might require root access on host
When debugging containers or analyzing their contents, we often need read-only access to a container's filesystem from the host machine. While docker exec
allows interactive inspection, mounting the entire filesystem provides better visibility for batch operations, file comparisons, or forensic analysis.
The Docker Engine doesn't provide a direct API for this operation, but we have several reliable methods:
Method 1: Using docker export
This creates a portable filesystem snapshot:
# Create temporary container if needed
docker create --name temp_container my_image
# Export and extract
docker export temp_container | tar -xC /host/mount/path
# Clean up
docker rm temp_container
Method 2: Bind Mounting Storage Driver Paths
For advanced users comfortable with Docker internals (works with btrfs, overlay2):
# Find the container's writable layer
CONTAINER_ID=$(docker inspect --format '{{.Id}}' my_container)
STORAGE_PATH="/var/lib/docker/overlay2/$CONTAINER_ID/merged"
# Mount read-only
sudo mount --bind -o ro $STORAGE_PATH /mnt/container_view
For frequent usage, consider developing a volume plugin:
package main
import (
"github.com/docker/go-plugins-helpers/volume"
"path/filepath"
)
type fsInspectorDriver struct {
mountPath string
}
func (d *fsInspectorDriver) Mount(req *volume.MountRequest) (*volume.MountResponse, error) {
containerPath := filepath.Join("/var/lib/docker", req.Name)
return &volume.MountResponse{Mountpoint: containerPath}, nil
}
// Implement other required volume plugin methods...
- Always mount read-only (
-o ro
) when inspecting containers - Prefer
docker export
for production systems to avoid storage driver dependencies - Consider filesystem permissions - you may need root access for some methods
For complex inspection needs:
dive
- Analyzes Docker image layerscontainer-diff
- Compares container filesystemssysdig
- Real-time container monitoring