Ceph Storage for Cross-OS Shared Workloads
Windows pods cannot use the native CephFS CSI node plugin directly. In K2s, Windows consumes CephFS through SMB.
Use this guide when both Linux and Windows containers need to communicate through the same shared storage.
Scope and constraints
- This path uses the Ceph SMB provisioner
smb.csi.k8s.iovia StorageClassceph-smb. storage cephandstorage smbcannot be enabled together.- Disabling Ceph removes the full Ceph cluster created by the addon, including OSD resources and Ceph-backed PVs from this setup.
For mixed Linux and Windows workloads, use Ceph with -w: Linux workloads consume ceph-cephfs, while Windows workloads consume ceph-smb.
Performance recommendation
For maximum Ceph performance in mixed OS environments, prefer:
- multiple OSDs with larger capacity,
- multiple dedicated OSD nodes,
- OSD placement not limited to the master/control-plane node.
Ceph references
Prerequisites
Enable Ceph with Windows SMB integration:
This creates:
ceph-smbStorageClass (provisionersmb.csi.k8s.io)- SMB CSI controller and node plugins in
smb.namespace(defaultstorage-smb-ceph) - A Ceph
mgr/smbshare backed by a CephFS subvolume
The Windows SMB user is created automatically by the Ceph mgr/smb setup. If no custom smb.userName is configured in config/ceph-config.json, the default user name is smbuser. K2s then generates a random password and stores it in the Kubernetes Secret smbcreds in the same namespace used for the SMB CSI manifests (default storage-smb-ceph). The Secret contains username and password, and the ceph-smb StorageClass uses those credentials.
Offline setup using addon export/import
Use this when the target environment is air-gapped.
- On a connected K2s environment, export the Ceph addon artifact:
-
Transfer the exported OCI artifact to the offline environment.
-
Import it before enable:
- If
clusterHost.nodeis a Linux worker node, use--nodeso Ceph bootstrap prerequisites are staged on the actual Ceph host:
- Enable Ceph with Windows integration:
Verify SMB path
Expected pods include csi-smb-controller, csi-smb-node, and csi-smb-node-win.
Example: Cross-OS read/write validation with Ceph SMB
This example validates real shared-volume behavior by mounting the same ceph-smb PVC from:
- one Linux pod that writes and reads files,
- one Windows pod that writes and reads files.
Windows workloads in K2s must include both a Windows node selector and the OS=Windows:NoSchedule toleration.
apiVersion: v1
kind: Namespace
metadata:
name: ceph-crossos-test
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ceph-crossos-pvc
namespace: ceph-crossos-test
spec:
accessModes:
- ReadWriteMany
storageClassName: ceph-smb
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: ceph-crossos-linux
namespace: ceph-crossos-test
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: writer-reader
image: docker.io/alpine:3.20
command:
- /bin/sh
- -c
- |
set -eu
echo "hello-from-linux" > /data/linux.txt
while true; do
date -Iseconds >> /data/linux-heartbeat.log
if [ -f /data/windows.txt ]; then
echo "linux sees windows.txt:"
cat /data/windows.txt
fi
sleep 5
done
volumeMounts:
- name: shared
mountPath: /data
volumes:
- name: shared
persistentVolumeClaim:
claimName: ceph-crossos-pvc
---
apiVersion: v1
kind: Pod
metadata:
name: ceph-crossos-windows
namespace: ceph-crossos-test
spec:
nodeSelector:
kubernetes.io/os: windows
tolerations:
- key: "OS"
operator: "Equal"
value: "Windows"
effect: "NoSchedule"
containers:
- name: writer-reader
image: mcr.microsoft.com/windows/servercore:ltsc2025
command:
- powershell.exe
- -Command
- |
New-Item -ItemType Directory -Path C:\data -Force | Out-Null
'hello-from-windows' | Set-Content -Path C:\data\windows.txt
while ($true) {
(Get-Date).ToString('o') | Add-Content -Path C:\data\windows-heartbeat.log
if (Test-Path C:\data\linux.txt) {
Write-Output 'windows sees linux.txt:'
Get-Content C:\data\linux.txt
}
Start-Sleep -Seconds 5
}
volumeMounts:
- name: shared
mountPath: C:\\data
volumes:
- name: shared
persistentVolumeClaim:
claimName: ceph-crossos-pvc
Apply and verify:
kubectl apply -f ceph-crossos-test.yaml
kubectl get pvc -n ceph-crossos-test ceph-crossos-pvc
kubectl get pods -n ceph-crossos-test -o wide
kubectl logs -n ceph-crossos-test ceph-crossos-linux --tail=40
kubectl logs -n ceph-crossos-test ceph-crossos-windows --tail=40
kubectl exec -n ceph-crossos-test ceph-crossos-linux -- ls -la /data
The pod-created files can be seen in the below path:
Cleanup:
For SMB addon-specific behavior and non-Ceph SMB examples, see the SMB addon README at addons/storage/smb/README.md.