Part of the series Kubernetes basics
Kubernetes Deployments, Sidecars, and Storage: A simple example
One app writes a message. One helper reads it. Follow this small example to understand how Kubernetes keeps apps running and shares their files.
Imagine you have an app that writes this message into a file every five seconds:
Hello from the app
You want a helper to read that file and show the messages in the logs.
We will use this one example throughout the post. First, let’s understand where the app and helper live.
1. A Pod holds the app and its helper
A container runs a program. In our example, one container runs the app and another runs the helper.
A Pod holds these two containers together. They run on the same machine, called a node.
One Pod
├── App container: writes a message
└── Helper container: reads the message
The helper is called a sidecar because it runs beside the main app and helps it.
For example, the main app might run an online shop. Its sidecar might read the shop’s log files and send them to a logging system. The app and helper have different jobs, but they work together.
2. A Deployment keeps the Pod running
Suppose you want one copy of this Pod running all the time.
A Deployment stores that instruction: “Keep one copy of my Pod running.” If the Pod is deleted, Kubernetes creates a replacement through a controller called a ReplicaSet.
The Deployment also stores the instructions for building the Pod: which containers to run and which volumes to give them. These instructions are called the Pod template.
spec:
replicas: 1
Here, replicas: 1 means one copy. With replicas: 2, you ask for two Pods, each with its own app and helper.
Keeping the app running and keeping its files are separate jobs. A replacement Pod starts fresh unless you connect it to storage that already holds the files.
3. A volume lets the containers share a file
The app writes to /output/report.log. How can the helper read it?
Containers have separate filesystems. Being in the same Pod does not automatically give them access to each other’s files.
We give both containers the same volume: a place to store files. Each container accesses it through a mount, which makes the volume appear at a directory inside that container.
In our example:
- The app sees the volume at
/output. - The helper sees the same volume at
/input. - The app’s
/output/report.logis the helper’s/input/report.log.
Think of two doors opening into the same cupboard. The doors have different names, but the contents are the same.
Open the diagram. On a phone, swipe sideways inside it.
The app writes a message, the shared volume holds it, and the sidecar reads it.
What the YAML means
At Pod level, define a volume:
volumes:
- name: shared-files
emptyDir: {}
Inside the app container, connect it to /output:
volumeMounts:
- name: shared-files
mountPath: /output
Inside the helper container, connect the same volume to /input:
volumeMounts:
- name: shared-files
mountPath: /input
readOnly: true
The matching name, shared-files, connects both mounts to the same volume. readOnly: true lets the helper read files without changing them through that mount.
These are small pieces of a larger YAML file. The complete example is below.
4. emptyDir keeps files while this Pod exists
emptyDir is a temporary volume. It starts empty when the Pod starts using it.
For our example, it is enough: the app and helper just need a shared working folder.
| What happens? | Does the file stay? |
|---|---|
| The app container restarts inside the same Pod | Yes. |
| The helper container restarts inside the same Pod | Yes. |
| The Pod is deleted and replaced | No. The new Pod gets an empty volume. |
A container restart and a Pod replacement are different. The volume belongs to the Pod, so it survives a container restart inside that Pod. It does not move into a replacement Pod.
Also, two Pods have two separate emptyDir volumes. They do not share one folder.
5. Use a PVC when the files must survive Pod replacement
Imagine the file contains customer uploads. You need those files after the Pod is replaced.
Use persistent storage through a PersistentVolumeClaim, usually shortened to PVC.
The names sound complicated, but their jobs are small:
| Name | Meaning in plain language |
|---|---|
| PVC | The app’s request: “I need some storage.” |
| PV | The Kubernetes object representing the storage assigned to that request. |
| StorageClass | Instructions for creating storage when a new claim needs it. |
The flow is:
App needs storage → PVC requests it → PV provides it → Pod mounts it
A replacement Pod can mount the same claim and use the existing files. The storage system and permissions still need to allow that access.
For a temporary report, emptyDir may be fine. For files you must keep, use suitable persistent storage and keep backups. Deleting a PVC can also delete its backing storage, depending on its reclaim policy.
6. Run the small example
You need kubectl connected to a practice Kubernetes cluster. Download report-demo.yaml, then run:
kubectl apply -f report-demo.yaml
kubectl rollout status deployment/report-demo -n sidecar-demo
The file creates a namespace named sidecar-demo and a Deployment named report-demo. A namespace is a named group for Kubernetes resources.
The app repeats this command every five seconds:
echo "Hello from the app" >> /output/report.log
>> adds the message to the end of the file.
The helper waits until the file exists, then reads it continuously:
tail -n+1 -f /input/report.log
Here, -n+1 starts at the first line. -f keeps watching for new lines. The wait matters because either container can start first in this example.
See the complete YAML file
apiVersion: v1
kind: Namespace
metadata:
name: sidecar-demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: report-demo
namespace: sidecar-demo
spec:
replicas: 1
selector:
matchLabels:
app: report-demo
template:
metadata:
labels:
app: report-demo
spec:
containers:
- name: writer
image: busybox:1.37.0
command: ["sh", "-c"]
args:
- |
while true; do
echo "Hello from the app" >> /output/report.log
sleep 5
done
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
memory: 64Mi
volumeMounts:
- name: shared-files
mountPath: /output
- name: reader
image: busybox:1.37.0
command: ["sh", "-c"]
args:
- |
until [ -f /input/report.log ]; do
sleep 1
done
exec tail -n+1 -f /input/report.log
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
memory: 64Mi
volumeMounts:
- name: shared-files
mountPath: /input
readOnly: true
volumes:
- name: shared-files
emptyDir:
sizeLimit: 64Mi
The resources settings reserve a little CPU and memory and put a limit on memory use. The volume’s sizeLimit sets a maximum size for this temporary folder. Those settings are included in the download; focus first on containers, volumes, and volumeMounts.
See the helper’s output
kubectl logs deployment/report-demo -c reader -n sidecar-demo --tail=5
You should see repeated messages:
Hello from the app
Hello from the app
Hello from the app
-c reader chooses the helper container. The helper prints the file’s contents to its standard output, which kubectl logs can show. Kubernetes does not automatically show every file in a container as a log.
Prove both containers see the same files
Write a second file through the app:
kubectl exec deployment/report-demo -c writer -n sidecar-demo -- \
sh -c 'echo shared-file-test > /output/proof.txt'
Read it through the helper:
kubectl exec deployment/report-demo -c reader -n sidecar-demo -- \
cat /input/proof.txt
Expected output:
shared-file-test
This shows the same file through two different mount paths.
See what happens when the Pod is replaced
In this practice workload, ask the Deployment to replace its Pod:
kubectl rollout restart deployment/report-demo -n sidecar-demo
kubectl rollout status deployment/report-demo -n sidecar-demo
Read the test file again:
kubectl exec deployment/report-demo -c reader -n sidecar-demo -- \
cat /input/proof.txt
You should get a “No such file or directory” error. The new Pod has a new emptyDir.
The app starts writing report.log again, but it never recreates proof.txt. That is why the test file is useful: it shows that the old files were lost.
When finished, remove the practice Deployment:
kubectl delete deployment report-demo -n sidecar-demo
7. If something does not work
Start with these three checks:
| Problem | What to check |
|---|---|
| The Pod does not start | Run kubectl get pods -n sidecar-demo, then describe the Pod to read its events. |
| The helper cannot find the file | Check that both containers mount shared-files and use the correct paths. |
| The file exists but logs are empty | Check the reader’s command and use -c reader when reading logs. |
For a PVC, Pending means the claim is still waiting for storage or for information needed to assign it. Read the claim’s events with kubectl describe pvc <claim-name> -n <namespace>.
You may also see the word attach. Attaching connects some kinds of storage to a node. Mounting makes that storage available at a directory. Kubernetes can record attachment status in a VolumeAttachment object. You usually inspect it only when an attachment error needs investigation.
Remember the example
The Deployment keeps the Pod running. The Pod holds the app and its helper. The sidecar reads the app’s file. The volume shares that file, and each mount gives a container a path to it.
Use emptyDir when losing the files with the Pod is acceptable. Use a PVC with suitable persistent storage when the files must survive Pod replacement.
For more detail, see the official guides to Deployments, sidecars, volumes, and persistent storage.