This episode modernizes Samba deployment: running it in containers with images like linuxserver and dperson, storing state in mounted volumes, and deploying to Kubernetes as a StatefulSet. You learn the correct pattern — stateless containers, state on storage — and the traps to avoid.

For 18 episodes, Samba ran on "bare" servers. Episode 19 touches the reality of modern infrastructure: containers and Kubernetes. Running Samba in a container isn't just style — it gives reproducibility (config in the image, not on the machine) and orchestration. But Samba is a stateful service: data and config must live outside the container. Understanding where that "state" lives is the heart of this episode.
Two most popular community images:
linuxserver/samba: an Alpine-based image with environment variables (USER, PASS, SHARE) — good for simple NAS setups.dperson/samba: a flexible image with command-line arguments (-u user -p pass -s "share;/path;yes;no;no") — easy to script.Both provide ready-to-use smbd + nmbd. An example with dperson/samba:
docker run -d --name samba \
-p 445:445 -p 139:139 \
-v /srv/data:/data \
-v samba-config:/config \
dperson/samba -u arman -p S3curePass! \
-s "data;/data;yes;no;no"Key points:
-p 445:445 -p 139:139: expose the SMB ports from the container to the host.-v /srv/data:/data: file-share data goes on a host volume — data survives even if the container is removed.-v samba-config:/config: Samba configuration and state (e.g. passdb.tdb) in a named volume.-u arman -p ... -s "...": dperson arguments for user, password, and share definition.Warning
First principle of stateful containers: never store state inside the container layer. If smbpasswd is run and the password database only exists in the container's filesystem, deleting the container deletes all users. Always mount passdb.tdb (or the state directory) to a volume. The golden rule: the container can disappear at any time without losing data.
A stateful service with a stable identity — fixed name, hostname, and storage — is the textbook use case for a StatefulSet (not a Deployment, which is stateless with changeable identity). A manifest skeleton:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: samba
spec:
serviceName: samba
replicas: 1
selector:
matchLabels: { app: samba }
template:
metadata:
labels: { app: samba }
spec:
containers:
- name: samba
image: dperson/samba:latest
ports:
- containerPort: 445
name: smb
volumeMounts:
- name: data
mountPath: /data
- name: config
mountPath: /config
args:
- -u
- arman
- -p
- $(SAMBA_PASS)
env:
- name: SAMBA_PASS
valueFrom:
secretKeyRef: { name: samba-secret, key: password }
volumeClaimTemplates:
- metadata: { name: data }
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests: { storage: 100Gi }
- metadata: { name: config }
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests: { storage: 1Gi }Important points in this manifest:
volumeClaimTemplates creates a separate PVC per pod — storage is guaranteed persistent and created automatically when the pod is born.valueFrom), not hardcoded in the manifest — a mandatory security habit.data (share contents) and config (Samba state) — both persistent.Samba must be reachable by clients via a stable IP — use a Service with the SMB ports:
apiVersion: v1
kind: Service
metadata:
name: samba
spec:
selector: { app: samba }
ports:
- name: smb
port: 445
targetPort: 445
protocol: TCP
type: LoadBalancerLoadBalancer provides a single external IP (e.g. from MetalLB/cloud LB) that forwards to the Samba pod. Clients only need to know \\<IP>\share — just like using a physical server.
Tip
You'll be tempted to raise replicas: 3 for HA. Don't — standalone Samba (without CTDB, episode 18) can't be replicated that way: three pods would fight over the same ReadWriteOnce PVC and locks would be inconsistent. For HA on Kubernetes, use storage that supports ReadWriteMany and run CTDB, or keep one pod with a ReadWriteOnce PVC and handle failover at the infra level. One reliable StatefulSet beats three pods that corrupt each other.
A summary of the correct pattern:
/data) and config/user DB (/config) on persistent volumes.LoadBalancer/NodePort) exposes ports 445/139.replicas > 1 for fake HA: multi-pod standalone Samba = lock conflicts; use CTDB + RWX if you truly need scale.readinessProbe can use tcpSocket on 445, not httpGet.latest tag isn't reproducible; pin the image version in production.nmbd and NetBIOS broadcast don't work well over Kubernetes overlay networks — modern clients via DNS/445 only.Beyond the patterns above, note:
hostNetwork vs Service: using hostNetwork: true avoids IP translation (SNAT) that can break old SMB connections, but binds the pod to one node. For simple production, a Service with correct MTU is usually enough.learn-velero series).Key takeaways:
linuxserver/samba and dperson/samba images run Samba easily; state must be on volumes, not the container layer.replicas > 1 without CTDB is fake HA — lock conflicts, not resilience.In episode 20 next, we'll cover performance & monitoring — tuning aio read size, use sendfile, NIC bonding, monitoring with smbstatus and Prometheus node_exporter, plus log analysis. A fast and monitored Samba is a production-worthy Samba!