How to Mount a Single File in a Volume
Mounting a single file into a running container is a common need. Maybe you want to inject one config file, a CA certificate, or a feature flag without replacing an entire directory. This guide shows you how to mount exactly one file using Docker and Kubernetes in a safe, repeatable way.
TLDR
- Use Docker CLI bind mounts to map one host file to one container path: set
:ro
when possible. - In Docker Compose, use long syntax
type: bind
withsource
andtarget
pointing to files. - In Kubernetes, mount a single file from a ConfigMap or Secret with
subPath
mapped to a file path. - Watch out for SELinux labels on Linux hosts
:z
or:Z
, and file permission differences across macOS, Linux, and WSL2.
Small mental model for what happens at runtime:
Host FS Container FS
--------- -------------
/srv/app/config.yaml -> /app/config/config.yaml (mounted file)
^ bind mount replaces only this file at target path
Prerequisites
- Docker Desktop 4.x or Docker Engine 24.x+
- kubectl 1.27+ and a cluster for the Kubernetes examples (kind or Minikube works)
Docker CLI: mount one file with a bind mount
You can bind mount a single file by mapping a host file to a container file path. Use read-only whenever the container does not need to write to it.
# Example: run NGINX with a custom top-level nginx.conf from the host
mkdir -p /tmp/nginx
cat > /tmp/nginx/nginx.conf <<'CONF'
events {}
http {
server {
listen 8080;
location / {
return 200 'ok from custom config';
}
}
}
CONF
# Map exactly one file into the container
docker run --rm -p 8080:8080 \
-v /tmp/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
nginx:1.27-alpine
# In another terminal, verify the config is active
curl -fsS http://localhost:8080
Why this works:
- When the source is a file and the target is a file path, Docker mounts only that file.
- If the target directory exists, Docker overlays the file at the target path without replacing the rest of the directory.
:ro
keeps the container from mutating the host file.
Common variations:
# Inject an application env file
docker run --rm \
-v $(pwd)/deploy/app.env:/app/config/app.env:ro \
ghcr.io/examplecorp/invoice-service:1.9.3
# Trust a custom root CA
docker run --rm \
-v /etc/ssl/mycompany.pem:/usr/local/share/ca-certificates/mycompany.pem:ro \
alpine:3.20 sh -c "update-ca-certificates && wget https://internal.api"
Notes for Linux hosts with SELinux
On SELinux-enabled hosts, containers may be blocked from reading host files. Add a label option to the bind mount.
# :z for shared content, :Z for private content
docker run --rm \
-v /secure/config.yaml:/app/config/config.yaml:ro,Z \
ghcr.io/examplecorp/invoice-service:1.9.3
Docker Compose: mount one file with long syntax
Compose supports single-file binds with the long volume syntax. This is easier to read and less error prone than short source:target
strings.
version: '3.9'
services:
nginx:
image: nginx:1.27-alpine
ports:
- '8080:8080'
volumes:
- type: bind
source: ./ops/nginx/nginx.conf # host file
target: /etc/nginx/nginx.conf # container file
read_only: true
Why this is useful:
- You only replace one file inside the container while keeping the image defaults for the rest.
read_only: true
matches production expectations for config.
Kubernetes: mount one file with subPath from a ConfigMap
In Kubernetes you typically do not mount host files directly. Instead you mount files from a volume source like a ConfigMap or Secret. To map exactly one entry to a specific path, use subPath
.
First, create a ConfigMap with multiple keys. Each key becomes a file in the volume.
kubectl create configmap web-config \
--from-literal=nginx.conf='events {}\nhttp { server { listen 8080; location / { return 200 "ok from cm"; } } }' \
--from-literal=extra.conf='# extra directives here'
Then mount only nginx.conf
to the desired path using subPath
.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 1
selector:
matchLabels: { app: web }
template:
metadata:
labels: { app: web }
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 8080
volumeMounts:
- name: web-config
mountPath: /etc/nginx/nginx.conf # mount a single file
subPath: nginx.conf # choose the key to project
readOnly: true
volumes:
- name: web-config
configMap:
name: web-config
This maps only one file, not the whole directory. The rest of /etc/nginx
stays from the image.
Mount a single Secret file
You can repeat the same pattern with a Secret.
kubectl create secret generic tls-root-ca \
--from-file=mycompany.pem=/etc/ssl/mycompany.pem
volumeMounts:
- name: ca
mountPath: /usr/local/share/ca-certificates/mycompany.pem
subPath: mycompany.pem
readOnly: true
volumes:
- name: ca
secret:
secretName: tls-root-ca
Read-only, ownership, and permissions
- Prefer read-only mounts for configuration. In Docker, use
:ro
. In Kubernetes, usereadOnly: true
. - Container users may differ from the file owner on the host. For Docker on Linux, you might need
chown
on the host or run the container with a matching UID. - In Kubernetes, use
securityContext
to run as a non-root user if the application can handle it.
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
Verifying your mount
These quick checks save time when debugging.
# Inside a Docker container
docker exec -it $(docker ps --filter name=nginx -q) sh -lc \
'ls -l /etc/nginx/nginx.conf && head -n5 /etc/nginx/nginx.conf'
# Inside a Kubernetes Pod
kubectl exec -it deploy/web -- sh -lc \
'ls -l /etc/nginx/nginx.conf && head -n5 /etc/nginx/nginx.conf'
Troubleshooting
- Target path is a directory: Docker may error or behave unexpectedly. Point the target to a file path, not a directory.
- Host path does not exist: Docker will create a directory if you accidentally give a directory-like path. Double check the source points to a file.
- SELinux denied access: use
:Z
or:z
on Linux hosts. - Docker Desktop file sharing: on macOS and Windows, the source path must be under a shared location.
- File not updating in Kubernetes: ConfigMap updates do not automatically refresh when using
subPath
. Roll the Pod or use a different pattern if you need live reloads.
With these patterns you can cleanly inject one file into a container for configuration, certificates, or feature flags. Start with Docker bind mounts for local development, and use Kubernetes subPath
with ConfigMaps or Secrets in clusters.
Found an issue?