In modern cloud-native architectures, applications are often decomposed into specialized single-purpose containers. A classic example is the Nginx + PHP-FPM pattern:
Since Nginx and PHP-FPM run in separate, isolated containers, they do not share a common filesystem by default. However, both containers require access to the application’s source code: Nginx needs it to verify static file requests, and PHP-FPM needs it to run the script. To solve this, Kubernetes uses Shared Volumes within a multi-container Pod.
graph TD
subgraph Pod: nginx-phpfpm
direction TB
subgraph nginx-container [nginx-container]
NginxConf["/etc/nginx/nginx.conf <br> (Mounted from ConfigMap)"]
NginxRoot["/var/www/html <br> (Mounts shared-files)"]
end
subgraph php-fpm-container [php-fpm-container]
PhpRoot["/var/www/html <br> (Mounts shared-files)"]
end
emptyDir[("emptyDir Shared Volume <br> (shared-files)")]
emptyDir <-->|Mounted at /var/www/html| NginxRoot
emptyDir <-->|Mounted at /var/www/html| PhpRoot
end
ConfigMap[("ConfigMap <br> (nginx-config)")] -->|subPath mount| NginxConf
emptyDirAn emptyDir volume is created when a Pod is assigned to a node and exists as long as that Pod is running on that node. It starts out empty, and all containers in the Pod can read and write the same files in the emptyDir volume, though that volume can be mounted at the same or different paths in each container.
To run Nginx, configuration files (like nginx.conf) must be injected. Instead of baking custom configurations into the container image, Kubernetes provides ConfigMaps to decouple configuration from containerized applications.
A ConfigMap is an API object used to store non-confidential data in key-value pairs. Pods can consume ConfigMaps as environment variables, command-line arguments, or configuration files in a volume.
subPathBy default, mounting a ConfigMap volume into a container directory overwrites all existing files in that target directory. For example, if you mount a ConfigMap to /etc/nginx/, you will wipe out other files in /etc/nginx/ like mime.types or fastcgi.conf.
To avoid this, use the subPath property. subPath specifies a relative path within the volume, allowing you to overlay a single file inside a container without modifying the rest of the target directory:
volumeMounts:
- name: nginx-config-volume
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
Volume mount and configuration mismatch issues manifest in specific ways. Below is a structured guide to diagnosing and fixing them.
404 Not Found)404 Not Found error.root path in nginx.conf does not match the shared volume mount path. Nginx maps a request (e.g., http://example.com/index.php) and forwards the file path (via SCRIPT_FILENAME) to PHP-FPM. If PHP-FPM mounts the volume to a different path, it cannot find the script to execute.mountPath configurations and the Nginx root directive to the exact same path (e.g., /var/www/html).CreateContainerConfigError / CreateContainerError)CreateContainerConfigError or CreateContainerError state.subPath is missing from the ConfigMap’s data.kubectl get configmap.subPath are not automatically updated when the ConfigMap is modified in the cluster. Kubernetes only syncs directory-level mounts automatically.kubectl describe pod nginx-phpfpm
Mounts section for all containers.Volumes definition section at the bottom.kubectl get configmap nginx-config -o yaml
root directive points to the shared volume mount path.fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;# Nginx container logs
kubectl logs nginx-phpfpm -c nginx-container
# PHP-FPM container logs
kubectl logs nginx-phpfpm -c php-fpm-container
# Verify Nginx configuration syntax inside the container
kubectl exec -it nginx-phpfpm -c nginx-container -- nginx -t
# Check if files exist in the shared volume in Nginx
kubectl exec -it nginx-phpfpm -c nginx-container -- ls -la /var/www/html
# Check if files exist in the shared volume in PHP-FPM
kubectl exec -it nginx-phpfpm -c php-fpm-container -- ls -la /var/www/html
thornginx-phpfpmnginx-configshared-files (uses emptyDir)nginx-config-volume/var/www/html/home/thor/index.phpConnect to the Jump Host and query the pod and ConfigMap configurations:
kubectl describe pod nginx-phpfpm
Observe that the mount paths are misaligned (e.g., Nginx mounts shared-files at /usr/share/nginx/html, but PHP-FPM mounts it at /var/www/html).
Next, inspect the ConfigMap to see what document root Nginx expects:
kubectl get configmap nginx-config -o yaml
Note that the configuration expects the root path to be /var/www/html.
Since Pod configurations are immutable, you must export, modify, and replace the running Pod.
Export the running Pod manifest:
kubectl get pod nginx-phpfpm -o yaml > /tmp/nginx-phpfpm.yaml
Edit /tmp/nginx-phpfpm.yaml to ensure the mountPath is aligned. Update both containers to mount the shared-files volume at /var/www/html:
# Inside nginx-container
volumeMounts:
- name: shared-files
mountPath: /var/www/html
- name: nginx-config-volume
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
# Inside php-fpm-container
volumeMounts:
- name: shared-files
mountPath: /var/www/html
Force delete and reapply the Pod manifest to trigger container recreation:
kubectl delete pod nginx-phpfpm --force --grace-period=0
kubectl apply -f /tmp/nginx-phpfpm.yaml
Ensure the Pod is in a healthy, running status:
kubectl get pods -w
Expected Output:
NAME READY STATUS RESTARTS AGE
nginx-phpfpm 2/2 Running 0 12s
The web server expects index.php to serve content. Since both containers share the emptyDir volume, copying the file into /var/www/html of either container makes it immediately accessible to both.
Copy the PHP index file from the jump host into the Nginx container:
kubectl cp /home/thor/index.php nginx-phpfpm:/var/www/html/index.php -c nginx-container
Verify the file was correctly copied to the shared filesystem on both containers:
kubectl exec nginx-phpfpm -c nginx-container -- ls -la /var/www/html/index.php
kubectl exec nginx-phpfpm -c php-fpm-container -- ls -la /var/www/html/index.php
Expected Output for both commands:
-rw-r--r-- 1 root root ... /var/www/html/index.php
Test the web application by sending a local HTTP request within the Pod:
kubectl exec nginx-phpfpm -c nginx-container -- curl -I http://localhost:8099
Expected Output:
HTTP/1.1 200 OK
Server: nginx/...
Content-Type: text/html; charset=UTF-8
...
The site will now render the dynamic PHP page successfully through Nginx!