# What can `subPath` do for you? I've always been confused as to what purpose `subPath` serves. I've come across `subPath` whenever a configmap was mounted: it looked like it was used as a way to control which keys in a configmap or secret are projected... but I was never sure. In this page, I'll try to uncover what exactly is happening when using `subPath` and finally get some firm understanding. We will be using Kind to test things out. Let's create a kind cluster: ``` kind create cluster ``` We will need the `tree` utility too: ```bash docker exec kind-control-plane sh -c "apt update && apt install tree -y" ``` Let's consider the following configmap: ```yaml apiVersion: v1 kind: ConfigMap metadata: name: conf data: conf.yaml: "contents" other.yaml: "other" ``` And the following pod manifest; pay close attention to `volumes` and `volumeMounts`: ```yaml apiVersion: v1 kind: Pod metadata: name: example spec: containers: - name: example image: busybox:1.28 command: ['sh', '-c', 'echo "Running!" && tail -f /dev/null'] volumeMounts: - name: conf mountPath: /etc/conf.yaml subPath: conf.yaml volumes: - name: conf configMap: name: conf ``` Now, I'll need to inspect the rootfs of the example container above. I'll use the command `podbash` defined as follows: ```bash #!/bin/bash #shellcheck disable=SC2093 exec docker exec -it kind-control-plane bash -c "$(grep -v 'exec docker' "$0")" podbash "$@" CONTAINER_ID=$(crictl ps --name example -o json | jq -r ".containers[].id") PID="$(ctr -n k8s.io t ls | grep "$CONTAINER_ID" | awk '{print $2}')" cd "/proc/$PID" || exit 1 exec bash "$@" EOF ``` With `podbash`, I can inspect the container's rootfs as well as look at the mountinfo. For example: ```bash # To inspect the container's filesystem: podbash -c 'ls root/etc' # To inspect the mount points: podbash -c 'cat mountinfo' | grep configmap ``` It's interesting to look at the mount information to understand the role of `subPath`. The output of `mountinfo` looks like this: ``` 4031 4028 259:5 /lib/kubelet/pods/84f85/volumes/kubernetes.io~configmap/conf/..2024_07_01_14_15_02.3624755718/conf.yaml /etc/conf.yaml ro,relatime - ext4 /dev/nvme0n1p3 rw,errors=remount-ro ``` To make this a little more readable, I've turned this into a table: | Host volume | Pod mount point | | -------- | -------- | | /lib/kubelet/pods/bc2bf/volumes/kubernetes.io~configmap/conf/..2024_07_01_14_15_02.3624755718/conf.yaml | /etc/conf.yaml | Now, let's make the distinction between `mountPath` and `subPath`: - `mountPath` is about the pod mount point. - `subPath` is about the host volume path. Without `subPath`, the outcome would have been very different. Let's remove the `subPath` from the manifest: ```yaml apiVersion: v1 kind: Pod metadata: name: example spec: containers: - name: example image: busybox:1.28 command: ['sh', '-c', 'echo "Running!" && tail -f /dev/null'] volumeMounts: - name: conf mountPath: /etc/conf.yaml volumes: - name: conf configMap: name: conf ``` The host "volume" (a directory, really) looks like this: ```bash # From the host: $ sudo tree /lib/kubelet/pods/X/volumes/kubernetes.io~configmap/conf ├── conf.yaml -> ..data/conf.yaml └── other.yaml -> ..data/other.yaml ``` The pod mount point looks off: ``` $ podbash -c 'tree root/etc/conf.yaml' root/etc/conf.yaml ├── conf.yaml -> ..data/conf.yaml └── other.yaml -> ..data/other.yaml ``` The problem is that `/etc/conf.yaml` is now a directory rather than a file... That's where `subPath` is useful: when you need to mount a single file rather than an entire folder. More specifically, `subPath` allows you to select which path under the host volume path `/lib/kubelet/pods/bc2bf/volumes/kubernetes.io~configmap/conf.yaml/` needs to be mounted. **Important learnings:** - If the path given in `subPath` isn't found, what's mounted at `mountPath` is a host-mounted volume that is made of an empty directory. The `subPath` name doesn't matter in this case. - If the path given in `subPath` corresponds to a file in the host volume (e.g., it matches a configmap key), that file alone is mounted to `mountPath`. - If the path given in `subPath` corresponds to a directory in the host-mounted volume (only happens when mounting a PV volume), then `mountPath` is a directory that corresponds to the sub- directory `subPath` in the host-mounted volume. If your concern is that you want to mount multiple configmap keys to a directory with existing files (such as `/etc`), the following will cause `/etc` to be replaced by the host-mounted volume: ```yaml volumeMounts: - name: conf mountPath: /etc ``` The `subPath` is exactly what you want in this case. You will have to have one volume mount per file, though: ```yaml volumeMounts: - name: conf mountPath: /etc/conf.yaml subPath: conf.yaml - name: conf mountPath: /etc/other.yaml subPath: other.yaml ``` ## What about mistakenly using a `subPath` that doesn't exist in the host's volume? On the host, since `baz` doesn't exist in the configmap `myconf`, the host volume is an empty directory: ```console $ sudo ls -al /lib/kubelet/pods/bc2bf/volumes/kubernetes.io~configmap/myconf/foo drwxrwxrwx 2 root root 4096 Jul 1 15:56 . ``` On the host, when `foo` exists in the configmap `myconf`, it appears as a file in the host volume: ```console $ sudo ls -al /lib/kubelet/pods/bc2bf/volumes/kubernetes.io~configmap/myconf/..2024_07_01_14_15_02.3624755718/foo -rw-r--r-- 1 root root 8612 Jul 1 16:15 /lib/kubelet/pods/bc2bf/volumes/kubernetes.io~configmap/myconf/..2024_07_01_14_15_02.3624755718/foo ```