KEP-5502: EmptyDir Volume Permission Mode
KEP-5502: EmptyDir Volume Permission Mode
- Release Signoff Checklist
- Summary
- Motivation
- Proposal
- Design Details
- Production Readiness Review Questionnaire
- Implementation History
- Drawbacks
- Alternatives
- Infrastructure Needed (Optional)
Release Signoff Checklist
Items marked with (R) are required prior to targeting to a milestone / release.
- (R) Enhancement issue in release milestone, which links to KEP dir in kubernetes/enhancements (not the initial KEP PR)
- (R) KEP approvers have approved the KEP status as
implementable - (R) Design details are appropriately documented
- (R) Test plan is in place, giving consideration to SIG Architecture and SIG Testing input (including test refactors)
- e2e Tests for all Beta API Operations (endpoints)
- (R) Ensure GA e2e tests meet requirements for Conformance Tests
- (R) Minimum Two Week Window for GA e2e tests to prove flake free
- (R) Graduation criteria is in place
- (R) all GA Endpoints must be hit by Conformance Tests within one minor version of promotion to GA
- (R) Production readiness review completed
- (R) Production readiness review approved
- “Implementation History” section is up-to-date for milestone
- User-facing documentation has been created in kubernetes/website , for publication to kubernetes.io
- Supporting documentation—e.g., additional design documents, links to mailing list discussions/SIG meetings, relevant PRs/issues, release notes
Summary
EmptyDir volumes are always created with mode 0777, and there’s no way to change it. This KEP adds an optional mode field to EmptyDirVolumeSource that lets users set the directory permission bits – anything from 0000 to 01777. This follows the same defaultMode pattern already established by Secret, ConfigMap, and DownwardAPI volume sources.
Motivation
The emptyDir volume creates directories with a hardcoded mode of 0777. This means any process with access can read, write, and delete anything in the volume, regardless of who created it. There is no way to change this today.
This causes real problems:
- Multi-container pods sharing an emptyDir cannot prevent one container from deleting another’s files. The sticky bit (
01777) would solve this, but there’s no way to set it. - For containerized applications (e.g., Ruby apps),
/tmpdirectories without a sticky bit are rejected for security reasons. This means emptyDir can’t reliably serve as/tmp, forcing users to resort to ephemeral volumes or RWX volumes - both significantly more complex to manage and not well supported across providers. - Platform engineers who want tighter permissions (e.g.,
0750for owner+group only) have to use init containers runningchmod, which adds unnecessary complexity.
Secret, ConfigMap, and DownwardAPI volume sources all expose a defaultMode field for controlling permissions. EmptyDir is the only volume type that lacks any permission control.
Goals
- Add an optional
modefield toEmptyDirVolumeSourcethat accepts Unix permission values between0000and01777 - Allow users to set directory permissions at creation time (e.g.,
0750for restricted access,01777for sticky bit) - Keep the default behavior unchanged - mode
0777when the field is not set - Follow the existing
defaultModepattern used by Secret, ConfigMap, and DownwardAPI volume sources
Non-Goals
- Changing the default behavior of existing emptyDir volumes
- Implementing this for volume types other than emptyDir
- Supporting this on platforms that don’t have Unix-style file permissions (e.g., Windows)
Proposal
Add a new optional field mode to EmptyDirVolumeSource. The field takes an integer between 0000 and 01777 representing standard Unix permission bits. When set, the kubelet creates the emptyDir directory with the specified permissions. When not set, the existing behavior (0777) is preserved.
volumes:
- name: app-data
emptyDir:
mode: 0750
User Stories
Story 1: Restricted Directory Permissions
As a platform engineer, I want to restrict an emptyDir volume so that only the application user and its group can access it. Setting mode: 0750 ensures the directory is not world-readable or writable, reducing the attack surface for multi-tenant workloads.
volumes:
- name: app-data
emptyDir:
mode: 0750
containers:
- name: app
volumeMounts:
- name: app-data
mountPath: /data
Story 2: Shared Temporary Storage with Sticky Bit
As a developer running containerized workloads, I need a shared /tmp directory where all users can write but only file owners can delete their own files. Without the sticky bit, emptyDir cannot be reliably used as a /tmp mount - some application frameworks (e.g., Ruby) reject /tmp directories that don’t have sticky bit set, and multi-process workloads risk one process deleting files owned by another.
Setting mode: 01777 gives standard /tmp behavior.
volumes:
- name: tmp
emptyDir:
mode: 01777
containers:
- name: app
volumeMounts:
- name: tmp
mountPath: /tmp
Risks and Mitigations
Risk: Users set incorrect permission values without understanding Unix modes.
Mitigation: The feature is opt-in. Default 0777 is preserved when mode is not set. Documentation will include common examples (0755, 0700, 01777). Validation rejects values outside the 0000-01777 range.
Risk: The mode field may conflict with fsGroup in the pod security context, which modifies group ownership and permission bits on mounted volumes.
Mitigation: This is the same interaction that already exists for SecretVolumeSource.defaultMode and ConfigMapVolumeSource.defaultMode. The API comment will document this caveat, consistent with how those volume sources handle it today.
Risk: Existing workloads could be affected.
Mitigation: The default is unchanged. Existing workloads continue to use mode 0777 unless explicitly configured.
Design Details
API Changes
Add a new optional field to the EmptyDirVolumeSource struct:
type EmptyDirVolumeSource struct {
// ... existing fields (Medium, SizeLimit) ...
// mode specifies the permission bits for the emptyDir directory, in numeric
// notation (e.g., 0777, 01777). Must be a value between 0000 and 01777.
// If not specified, defaults to 0777.
// This might be in conflict with other options that affect the file
// mode, like fsGroup, and the result can be other mode bits set.
// +featureGate=EmptyDirVolumeMode
// +optional
Mode *int32 `json:"mode,omitempty" protobuf:"varint,3,opt,name=mode"`
}
The feature is gated behind EmptyDirVolumeMode. When the gate is disabled:
- kube-apiserver: Strips the
modefield from new pod specs via field dropping (unless already persisted on an existing pod). - Validation: Rejects pods with
modeset outside0000-01777.
Implementation
The implementation is in the emptyDir volume plugin (pkg/volume/emptydir/empty_dir.go):
Read the
modefield fromEmptyDirVolumeSource:perm := os.FileMode(0777) if ed.mode != nil { perm = os.FileMode(*ed.mode) }Apply the permissions when creating the directory:
if err := os.MkdirAll(dir, perm); err != nil { return err } // MkdirAll applies the umask, so explicitly chmod to set the exact mode if err := os.Chmod(dir, perm); err != nil { return err }
The os.Chmod after os.MkdirAll is necessary because MkdirAll applies the process umask, which may strip requested bits. Chmod sets the exact mode regardless of umask.
Test Plan
[x] I/we understand the owners of the involved components may require updates to existing tests to make this code solid enough prior to committing the changes necessary to implement this enhancement.
Prerequisite testing updates
No prerequisite testing updates are required.
Unit tests
Unit tests will cover:
- Directory creation with
mode: 0750results in mode0750 - Directory creation with
mode: 01777results in mode01777 - Directory creation without
modedefaults to0777 - Mode is applied correctly for all emptyDir medium types (default, tmpfs, hugepages)
- Validation rejects values outside
0000-01777 - Feature gate drop test:
modeis stripped when the gate is disabled, preserved when enabled
Coverage targets:
pkg/volume/emptydirpkg/apis/core/validationpkg/api/pod
Integration tests
No integration tests are planned. The feature is a simple permission change at directory creation time and is well covered by unit and e2e tests.
e2e tests
e2e_node tests will verify:
- A pod with emptyDir
mode: 0750has the correct permissions (verified viastat) - A pod with emptyDir
mode: 01777has the correct permissions including the sticky bit - Default behavior is preserved when
modeis not set
Graduation Criteria
Alpha (target 1.37)
- Feature implemented behind
EmptyDirVolumeModefeature gate (disabled by default) - Kubelet applies
modewhen creating emptyDir volumes - Unit tests and
e2e_nodetests completed
Beta
- Feature gate enabled by default
- No major bugs reported during alpha
- Address any feedback from alpha users
- Upgrade/downgrade testing completed
GA
- At least 2 releases in beta with no major bugs
- Feature gate removed
- Conformance tests in place
Upgrade / Downgrade Strategy
Upgrade: Enabling the feature gate allows new pods to use the mode field. Existing running pods are unaffected - their directories are already created.
Downgrade: Disabling the feature gate causes:
- The API server strips
modefrom new pod specs via field dropping. - The kubelet ignores
modeand creates emptyDir volumes with the default0777.
Running pods are not affected by downgrade. Only newly created or rescheduled pods are affected.
Version Skew Strategy
The mode field is optional and additive:
- Apiserver ON, kubelet OFF: The apiserver accepts
mode, but the kubelet doesn’t recognize it and falls back to0777. Safe - matches previous behavior, but the user’s requested permissions are not applied. - Apiserver OFF, kubelet ON: The apiserver strips
mode. The kubelet never sees it. Falls back to0777. - Both ON: Full enforcement.
- Both OFF: Feature disabled, existing behavior.
Production Readiness Review Questionnaire
Feature Enablement and Rollback
How can this feature be enabled / disabled in a live cluster?
- Feature gate (also fill in values in
kep.yaml)- Feature gate name:
EmptyDirVolumeMode - Components depending on the feature gate: kubelet, kube-apiserver
- Feature gate name:
Does enabling the feature change any default behavior?
No. The feature only takes effect when users explicitly set the mode field. Existing emptyDir volumes continue to use mode 0777.
Can the feature be disabled once it has been enabled (i.e. can we roll back the enablement)?
Yes. Set the EmptyDirVolumeMode feature gate to false. The API server will strip mode from new pods, and the kubelet will ignore it. Existing running pods are unaffected - their directories are already created. New pods will get 0777.
What happens if we reenable the feature if it was previously rolled back?
Works as expected for new pods. Existing pods created while the feature was disabled keep 0777 until they are deleted and recreated.
Are there any tests for feature enablement/disablement?
Yes. Unit tests verify that mode is stripped when the gate is disabled and preserved when enabled or when the field is already persisted.
Rollout, Upgrade and Rollback Planning
How can a rollout or rollback fail? Can it impact already running workloads?
Running workloads are not affected. The feature is opt-in, so only pods that explicitly set mode are affected. Rolling back to an older kubelet is safe - mode is ignored and emptyDir volumes get 0777.
What specific metrics should inform a rollback?
Increased pod startup failures or volume mount errors correlated with pods using the mode field.
Were upgrade and rollback tested? Was the upgrade->downgrade->upgrade path tested?
Will be tested manually before beta.
Is the rollout accompanied by any deprecations and/or removals of features, APIs, fields of API types, flags, etc.?
No.
Monitoring Requirements
How can an operator determine if the feature is in use by workloads?
kubectl get pods -A -o json | jq '.items[] | select(.spec.volumes[]?.emptyDir?.mode != null)'
How can someone using this feature know that it is working for their instance?
- Other (treat as last resort)
- Details: Exec into the pod and check directory permissions with
stat -c '%a' /path/to/emptydir. For example, mode0750should output750, and mode01777should output1777.
- Details: Exec into the pod and check directory permissions with
What are the reasonable SLOs (Service Level Objectives) for the enhancement?
No impact on existing SLOs. The change is one argument to a mkdir + chmod system call.
What are the SLIs (Service Level Indicators) an operator can use to determine the health of the service?
- Metrics
- Metric name:
storage_operation_duration_seconds(existing metric) - Components exposing the metric: kubelet
- Metric name:
Are there any missing metrics that would be useful to have to improve observability of this feature?
No.
Dependencies
Does this feature depend on any specific services running in the cluster?
No. It only requires the host OS to support Unix file permissions (standard on Linux).
Scalability
Will enabling / using this feature result in any new API calls?
No.
Will enabling / using this feature result in introducing new API types?
No. It adds a field to an existing type (EmptyDirVolumeSource).
Will enabling / using this feature result in any new calls to the cloud provider?
No.
Will enabling / using this feature result in increasing size or count of the existing API objects?
- API type: Pod (
EmptyDirVolumeSource) - Estimated increase: One int32 field per emptyDir volume that uses the feature.
Will enabling / using this feature result in increasing time taken by any operations covered by existing SLIs/SLOs?
No.
Will enabling / using this feature result in non-negligible increase of resource usage (CPU, RAM, disk, IO, …) in any components?
No.
Can enabling / using this feature result in resource exhaustion of some node resources (PIDs, sockets, inodes, etc.)?
No.
Troubleshooting
How does this feature react if the API server and/or etcd is unavailable?
The feature runs in the kubelet and doesn’t contact the API server or etcd after the pod spec is retrieved.
What are other known failure modes?
None beyond standard emptyDir failure modes.
What steps should be taken if SLOs are not being met to determine the problem?
Check if affected pods use the mode field and review kubelet logs for errors.
Implementation History
- 2025-02-19: Initial implementation started (kubernetes/kubernetes#130277)
- 2025-08-25: KEP issue created (kubernetes/enhancements#5502)
- 2026-01-30: Initial KEP draft with boolean
stickyBitfield - 2026-06-15: KEP redesigned to use
mode *int32field, targeting alpha in v1.37
Drawbacks
- Adds a new API field, slightly increasing API surface.
- Users unfamiliar with Unix permissions may set incorrect values. Mitigated by validation and documentation.
- The
modefield may interact withfsGroupin the pod security context. This is the same caveat that exists for Secret/ConfigMapdefaultModeand is documented in the API comment. - Not supported on Windows, but emptyDir permissions work differently there anyway.
Alternatives
Alternative 1: Boolean stickyBit field
An alternative design is to add an optional StickyBit field to the EmptyDirVolumeSource struct:
type EmptyDirVolumeSource struct {
// ... existing fields ...
// StickyBit sets the emptyDir permission to 01777 instead of 0777.
// When enabled, only the owner of a file can delete or rename it,
// even if the directory is world-writable.
// This is similar to the /tmp directory behavior on Unix systems.
// +optional
StickyBit *bool `json:"stickyBit,omitempty" protobuf:"varint,3,opt,name=stickyBit"`
}
When true, the directory would be created with mode 01777 instead of 0777. This was not chosen because:
- Limited scope: Only solves the sticky bit use case. Every future permission request (e.g., tighter directory ownership) would require a new field.
- Inconsistent with existing patterns: Secret, ConfigMap, and DownwardAPI volume sources all use
defaultMode *int32for permission control. A boolean is inconsistent with this established pattern. - Less flexible: Cannot express permissions like
0750or0700. Themodefield covers all Unix permission use cases with a single field.