Kubernetes storage can look simple from the application side. A container writes a file to a mounted directory, reads it later, and the application continues working.
The security implications are less obvious.
When Kubernetes uses a host path or bind mount, the container can gain access to a location backed by the node's filesystem. If that location is configured too broadly, a workload may gain access to files or directories that it was never supposed to see.
This becomes particularly important when working with hostPath volumes, local storage, CSI drivers, and other storage mechanisms that ultimately depend on directories on the node.
Kubernetes 1.37 continues the broader effort to make storage behavior more explicit and secure. For developers and cluster administrators, the important lesson is simple: a mounted directory should be treated as a security boundary, not just a storage location.
This article explains bind mounts in Kubernetes, where the security risks come from, how permissions affect access, and what to check before using node-backed storage in production.
What Is a Bind Mount?
A bind mount maps an existing directory from one filesystem location into another location.
In a containerized environment, the general idea looks like this:
Kubernetes Node
|
+-- /var/lib/application-data
|
+------> Container
|
+-- /data
The application sees:
/data
but the underlying storage may exist on the Kubernetes node.
This is different from a typical persistent volume abstraction where Kubernetes and a storage plugin manage the lifecycle of the storage independently from the container.
Why Bind Mounts Need Extra Care
A container normally has an isolated filesystem.
When you mount a host directory into that container, you deliberately create a connection between the container and the node.
For example:
Container
|
| mounted directory
v
Node filesystem
If the mounted directory is carefully selected, this can be useful.
If the mount points to a sensitive location, the container may gain access to information or resources outside the application's intended scope.
This is why arbitrary host filesystem access is generally considered a security-sensitive operation.
hostPath and Node Storage
A common Kubernetes mechanism for exposing a node filesystem path is hostPath.
For example:
apiVersion: v1
kind: Pod
metadata:
name: storage-demo
spec:
containers:
- name: app
image: nginx:latest
volumeMounts:
- name: app-data
mountPath: /data
volumes:
- name: app-data
hostPath:
path: /var/lib/app-data
type: DirectoryOrCreate
The container receives /data, while the node provides /var/lib/app-data.
This can be useful for workloads that genuinely need node-local data.
However, it also means that the workload depends on a specific node's filesystem.
Bind Mount vs Persistent Volume
These approaches should not be treated as interchangeable.
Area | Host-backed bind mount / | Persistent Volume |
|---|---|---|
Storage location | Node filesystem | Storage abstraction |
Portability | Usually lower | Usually higher |
Node dependency | Often strong | Depends on storage class |
Security boundary | Host filesystem is involved | Storage layer provides abstraction |
Common use | Node-specific data or system integration | Application persistent storage |
Operational complexity | Can be simple initially | Depends on storage backend |
For application data, a PersistentVolume-based design is often easier to manage consistently across a cluster.
A host path should have a specific reason to exist.
The Security Boundary
Consider this directory:
/var/lib/app-data
If the application has access only to that directory, the intended boundary is:
Node
|
+-- /var/lib/app-data
| |
| +-- Application files
|
+-- /etc
+-- /var/log
+-- /home
+-- other directories
The application should not be able to traverse into unrelated directories.
This is one reason the exact mount path matters.
A dangerous configuration is not necessarily dangerous because hostPath exists. The risk comes from what the workload can access through the mount and what permissions it receives.
Avoid Mounting the Node Root
A configuration such as:
volumes:
- name: host-root
hostPath:
path: /
creates a fundamentally different security situation.
The container now has access to the node's root filesystem.
This can expose:
System configuration
Logs
Credentials
Runtime information
Kubernetes-related files
Other application data
Mounting / should therefore be treated as an extremely high-risk configuration.
For normal application workloads, there is rarely a good reason to expose the entire node filesystem.
Use the Narrowest Host Path Possible
If a workload requires host-backed storage, prefer a dedicated directory.
For example:
hostPath:
path: /var/lib/my-application
type: Directory
rather than:
hostPath:
path: /var/lib
type: Directory
The smaller directory creates a narrower security boundary.
The principle is:
Broader mount
|
v
More host data exposed
Narrower mount
|
v
Less host data exposed
Understand Directory vs DirectoryOrCreate
The hostPath.type field controls how Kubernetes handles the target path.
For example:
type: Directory
expects the directory to already exist.
Whereas:
type: DirectoryOrCreate
allows Kubernetes to create the directory if it does not exist.
That distinction matters operationally.
With Directory, a missing path can prevent the workload from starting.
With DirectoryOrCreate, the path can be created automatically, but administrators still need to understand who owns the directory and what permissions it receives.
Do not use DirectoryOrCreate simply because it makes deployment easier.
File Permissions Still Matter
Suppose the host directory is:
/var/lib/my-application
The directory might be owned by:
root:root
with restrictive permissions.
The container might run as a non-root user:
securityContext:
runAsUser: 10001
runAsGroup: 10001
The application may then receive:
Permission denied
This is not necessarily a Kubernetes storage failure.
It can simply be a Linux filesystem permission issue.
The complete access path is:
Container User
|
v
Container Mount
|
v
Host Directory
|
v
Linux UID/GID + Permissions
All four need to line up.
Use a Non-Root Container
Applications generally should not run as root unless there is a specific operational requirement.
For example:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
allowPrivilegeEscalation: false
This reduces the privileges available to the application process.
However, runAsNonRoot does not magically fix host filesystem permissions.
The underlying directory must still be accessible to the configured user.
fsGroup Can Help With Volume Permissions
For volumes that support Kubernetes ownership changes, a pod-level security context can specify an fsGroup.
For example:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
The behavior depends on the volume type and storage implementation.
Do not assume that fsGroup will change permissions for every type of host-backed storage in exactly the same way.
Always test the actual storage mechanism being used.
Read-Only Mounts
If an application only needs to read host data, make the mount read-only.
For example:
volumeMounts:
- name: host-data
mountPath: /data
readOnly: true
This creates an additional protection layer.
The model becomes:
Host Data
|
v
Read-only Mount
|
v
Container
Even if the application is compromised, the mounted location cannot be modified through that mount.
Read-only access should be the default whenever write access is not required.
Example: Read-Only Node Information
Suppose an application needs to read a specific node-provided directory.
A safer pattern is:
apiVersion: v1
kind: Pod
metadata:
name: reader
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
containers:
- name: app
image: example/app:1.0
securityContext:
allowPrivilegeEscalation: false
volumeMounts:
- name: node-data
mountPath: /node-data
readOnly: true
volumes:
- name: node-data
hostPath:
path: /var/lib/my-application
type: Directory
This example demonstrates several useful controls:
Non-root execution
No privilege escalation
Narrow host path
Read-only access
The actual image and path should be replaced with values appropriate for the workload.
Writable Host Paths Need More Scrutiny
A writable host path creates more risk because the workload can modify node-backed data.
For example:
volumeMounts:
- name: app-data
mountPath: /data
readOnly: false
If write access is required, ask:
Why does the application need host-backed storage?
Can a PersistentVolume be used instead?
Can the directory be isolated?
Can the mount be read-only?
Which user owns the files?
What happens if the pod is compromised?
Can another workload access the same directory?
These questions should be answered before production deployment.
Avoid Sharing Host Directories Between Applications
Suppose two applications use:
/var/lib/shared
If both workloads can write to the same location, one compromised application may be able to alter data consumed by another.
A better design is:
/var/lib/app-a
/var/lib/app-b
with separate permissions.
Storage isolation should follow application trust boundaries.
Use Pod Security Controls
Kubernetes provides mechanisms for controlling workloads that request sensitive capabilities.
Pod Security Standards distinguish between security profiles such as Baseline and Restricted.
Workloads that require host filesystem access should be reviewed carefully against the cluster's Pod Security policy.
A namespace with strong controls can prevent workloads from deploying configurations that violate the organization's security baseline.
For example:
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
Whether a particular workload can run under that policy depends on the complete pod specification.
The important point is that storage security should be part of pod security governance rather than handled separately.
Admission Policies Can Add Another Layer
Organizations can also use admission controls to restrict dangerous storage configurations.
A policy can reject workloads that attempt to:
Mount the host root filesystem
Use forbidden host paths
Run as root
Use privileged containers
Mount sensitive node directories
Conceptually:
Pod Request
|
v
Admission Policy
|
+-- Safe ------> Create Pod
|
+-- Unsafe ----> Reject
This is particularly useful in large clusters where manually reviewing every manifest is unrealistic.
Common Mistakes
Mounting /
This exposes far more of the node filesystem than most applications require.
Use a dedicated directory instead.
Running as Root
A root process inside a container increases the consequences of a security failure.
Use a non-root identity whenever possible.
Making Every Mount Writable
If the application only reads data, use:
readOnly: true
Using Broad Host Paths
Avoid:
/var
when the application only needs:
/var/lib/my-application
Assuming fsGroup Always Fixes Permissions
Storage implementations can behave differently.
Test the actual volume type.
Ignoring Node Affinity
Host-backed storage belongs to a specific node.
A workload depending on node-local data may not behave correctly if Kubernetes schedules it elsewhere.
Scheduling and Node Locality
Host-backed storage creates a relationship between a workload and the node containing the data.
For example:
Node A
|
+-- /var/lib/app-data
|
+-- Application data
Pod
|
+-- Must access Node A
If the pod moves to Node B:
Node B
|
+-- /var/lib/app-data
|
+-- Different or missing data
This can cause application failures.
For node-local storage, scheduling rules and storage configuration need to be designed together.
A PersistentVolume with appropriate node affinity may be more suitable than manually assuming a particular node.
Troubleshooting Permission Denied
If the application reports:
Permission denied
check the identity inside the container:
id
Then inspect the mounted directory:
ls -ld /data
ls -la /data
If you have appropriate access on the node, inspect the host directory:
ls -ld /var/lib/my-application
Compare:
Container UID
Container GID
Host directory UID
Host directory GID
Filesystem permissions
Do not immediately solve the problem by running the container as root.
That can hide the actual permission configuration problem.
Troubleshooting Mount Failures
Check the pod events:
kubectl describe pod <pod-name>
Look at the Events section for errors related to:
Missing directories
Invalid volume configuration
Mount failures
Permission problems
You can also inspect the volume definition:
kubectl get pod <pod-name> -o yaml
This is useful when the deployed configuration differs from the manifest you expected.
Troubleshooting Data Disappearing
If data exists when a pod runs on one node but disappears after rescheduling, investigate whether the application is using node-local storage.
Check:
kubectl get pod <pod-name> -o wide
Then identify the node where the pod is running.
If the storage comes from:
Node A -> /var/lib/app-data
moving the workload to Node B can expose a different directory.
This is a storage design issue rather than simply a Kubernetes deployment problem.
When Should You Use hostPath?
A host path can be appropriate when the workload genuinely needs access to node-local resources.
Examples can include:
Node-level monitoring
Specific node integration
Specialized infrastructure workloads
Certain storage implementations
Controlled access to node-provided data
It is generally less appropriate for ordinary application persistence when a PersistentVolume can provide the required storage abstraction.
When Should You Use a PersistentVolume?
Use a PersistentVolume-based design when the application needs persistent data without directly depending on arbitrary node filesystem paths.
A typical architecture is:
Application
|
v
PersistentVolumeClaim
|
v
StorageClass
|
v
Storage Backend
This provides a cleaner separation between the application and the physical storage implementation.
Production Checklist
Before deploying a host-backed mount, check the following:
[ ] Is hostPath actually required?
[ ] Is the mounted directory as narrow as possible?
[ ] Can the mount be read-only?
[ ] Does the container run as non-root?
[ ] Is privilege escalation disabled?
[ ] Are UID/GID permissions correct?
[ ] Is the host directory dedicated to the workload?
[ ] Can another workload access the same directory?
[ ] Does the workload depend on a particular node?
[ ] Are scheduling constraints configured?
[ ] Are Pod Security controls enabled?
[ ] Are admission policies available?
[ ] Have failure and rescheduling scenarios been tested?
Advantages and Disadvantages
Advantages | Disadvantages |
|---|---|
Direct access to node-local data | Creates a strong node dependency |
Simple for certain infrastructure workloads | Can expose host filesystem data |
Useful for specialized workloads | Harder to migrate between nodes |
Can be efficient for local storage | Requires careful permission management |
Easy to understand initially | Broad mounts can create serious security risks |
Best Practices
Prefer the Smallest Possible Mount
Only expose the directory the application actually needs.
Use Read-Only Access When Possible
Read-only mounts reduce the ability of a compromised application to modify node data.
Run as Non-Root
Use a dedicated application UID and GID.
Keep Host Directories Isolated
Do not share sensitive host directories across unrelated workloads.
Use Admission Controls
Prevent dangerous storage configurations before they reach the cluster.
Treat Node Locality as a Design Requirement
If the storage exists on a particular node, make sure scheduling and failure handling account for that dependency.
Prefer Kubernetes Storage Abstractions for Application Data
If the application simply needs persistent storage, evaluate a PersistentVolume rather than exposing the node filesystem directly.
Summary
Bind mounts and hostPath volumes can be useful in Kubernetes, but they create a direct relationship between a workload and the node's filesystem. That relationship makes storage configuration a security concern as well as an operational concern.
The safest approach is to expose the smallest possible directory, use read-only mounts whenever write access is unnecessary, run containers as non-root users, and avoid mounting sensitive node paths.
For ordinary application persistence, PersistentVolumes generally provide a cleaner abstraction than directly exposing host filesystem directories. When node-local storage is required, scheduling, permissions, failure handling, and security policies need to be designed together.
The key rule is simple: if a container does not need access to a host directory, do not mount it. If it does need access, give it the smallest and most restricted mount that satisfies the requirement.
Join the conversation! Your thoughts help the community grow.