Kubernetes
ConfigMap
Volume Mount
Filesystem
Directory Integration

Mount add files to existing directory using configmap volume mount

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

By default, mounting a ConfigMap as a volume in Kubernetes replaces the entire target directory, hiding any existing files. To add files from a ConfigMap to an existing directory without overwriting it, use subPath in the volume mount. Each file must be mounted individually with its own subPath entry. Alternatively, mount the ConfigMap to a separate directory and use an init container to copy files. Understanding the difference between a full volume mount and subPath is essential for configurations like adding config files to /etc/nginx/conf.d/ or adding properties files alongside application JARs.

The Problem: Full Volume Mount Overwrites

yaml
1apiVersion: v1
2kind: ConfigMap
3metadata:
4  name: my-config
5data:
6  app.conf: |
7    server_name example.com;
8    listen 80;
9---
10apiVersion: v1
11kind: Pod
12metadata:
13  name: nginx-pod
14spec:
15  containers:
16    - name: nginx
17      image: nginx
18      volumeMounts:
19        - name: config-volume
20          mountPath: /etc/nginx/conf.d  # Replaces ENTIRE directory
21  volumes:
22    - name: config-volume
23      configMap:
24        name: my-config

This mount replaces all contents of /etc/nginx/conf.d/. Any files that the nginx image ships in that directory (like default.conf) are hidden. Only app.conf from the ConfigMap is visible.

Fix: Use subPath to Add Individual Files

yaml
1apiVersion: v1
2kind: ConfigMap
3metadata:
4  name: my-config
5data:
6  app.conf: |
7    server_name example.com;
8    listen 80;
9  custom.conf: |
10    gzip on;
11    gzip_types text/plain application/json;
12---
13apiVersion: v1
14kind: Pod
15metadata:
16  name: nginx-pod
17spec:
18  containers:
19    - name: nginx
20      image: nginx
21      volumeMounts:
22        - name: config-volume
23          mountPath: /etc/nginx/conf.d/app.conf
24          subPath: app.conf
25        - name: config-volume
26          mountPath: /etc/nginx/conf.d/custom.conf
27          subPath: custom.conf
28  volumes:
29    - name: config-volume
30      configMap:
31        name: my-config

Each subPath mount adds a single file to the directory without affecting other files. The existing default.conf in /etc/nginx/conf.d/ remains intact alongside the new app.conf and custom.conf.

How subPath Works

yaml
1# Without subPath — mounts entire ConfigMap as directory
2volumeMounts:
3  - name: config-volume
4    mountPath: /app/config
5# Result: /app/config/ contains ONLY ConfigMap keys as files
6# Any existing files in /app/config/ are hidden
7
8# With subPath — mounts single file
9volumeMounts:
10  - name: config-volume
11    mountPath: /app/config/settings.yaml
12    subPath: settings.yaml
13# Result: /app/config/settings.yaml is added
14# Other files in /app/config/ are preserved

subPath tells Kubernetes to mount only a specific key from the ConfigMap volume as a file at the exact mountPath, rather than mounting the entire volume as a directory.

Multiple Files with items and subPath

yaml
1apiVersion: v1
2kind: ConfigMap
3metadata:
4  name: app-config
5data:
6  database.yml: |
7    host: postgres
8    port: 5432
9  redis.yml: |
10    host: redis
11    port: 6379
12  app.properties: |
13    debug=false
14    log.level=INFO
15---
16apiVersion: apps/v1
17kind: Deployment
18metadata:
19  name: myapp
20spec:
21  template:
22    spec:
23      containers:
24        - name: app
25          image: myapp:latest
26          volumeMounts:
27            - name: config-volume
28              mountPath: /app/config/database.yml
29              subPath: database.yml
30            - name: config-volume
31              mountPath: /app/config/redis.yml
32              subPath: redis.yml
33            - name: config-volume
34              mountPath: /app/config/app.properties
35              subPath: app.properties
36      volumes:
37        - name: config-volume
38          configMap:
39            name: app-config

Each ConfigMap key is mounted as a separate file. The /app/config/ directory retains any files baked into the container image.

Init Container Approach

yaml
1apiVersion: v1
2kind: Pod
3metadata:
4  name: app-pod
5spec:
6  initContainers:
7    - name: copy-config
8      image: busybox
9      command: ['sh', '-c', 'cp /config-source/* /app/config/']
10      volumeMounts:
11        - name: config-source
12          mountPath: /config-source
13        - name: app-config
14          mountPath: /app/config
15  containers:
16    - name: app
17      image: myapp:latest
18      volumeMounts:
19        - name: app-config
20          mountPath: /app/config
21  volumes:
22    - name: config-source
23      configMap:
24        name: my-config
25    - name: app-config
26      emptyDir: {}

The init container copies ConfigMap files into an emptyDir volume, which the main container then mounts. This avoids the subPath limitation where each file needs its own mount entry, but the directory starts empty (no image-baked files preserved).

Projected Volume for Multiple Sources

yaml
1apiVersion: v1
2kind: Pod
3metadata:
4  name: app-pod
5spec:
6  containers:
7    - name: app
8      image: myapp:latest
9      volumeMounts:
10        - name: combined-config
11          mountPath: /app/config/database.yml
12          subPath: database.yml
13        - name: combined-config
14          mountPath: /app/config/secrets.yml
15          subPath: secrets.yml
16  volumes:
17    - name: combined-config
18      projected:
19        sources:
20          - configMap:
21              name: app-config
22              items:
23                - key: database.yml
24                  path: database.yml
25          - secret:
26              name: app-secrets
27              items:
28                - key: secrets.yml
29                  path: secrets.yml

Projected volumes combine ConfigMaps and Secrets into a single volume, which can then be mounted with subPath to add individual files to existing directories.

Setting File Permissions

yaml
1volumes:
2  - name: config-volume
3    configMap:
4      name: my-config
5      defaultMode: 0644  # rw-r--r-- for all files
6      items:
7        - key: script.sh
8          path: script.sh
9          mode: 0755  # rwxr-xr-x for this file only

defaultMode sets permissions for all files in the ConfigMap volume. Individual files can override with mode. Note that subPath mounts do not receive automatic updates when the ConfigMap changes.

Common Pitfalls

  • subPath files do not auto-update: When a ConfigMap is updated, files mounted with subPath are NOT automatically refreshed in the running pod. Only full directory mounts receive live updates. With subPath, you must restart the pod to pick up changes.
  • Forgetting subPath causes directory replacement: Mounting a ConfigMap to /etc/nginx/conf.d without subPath replaces the entire directory. All original files (like default.conf) disappear. Always use subPath when you need to preserve existing directory contents.
  • Each file needs its own volumeMount: With subPath, you cannot mount all ConfigMap keys at once — each key requires a separate volumeMounts entry. For ConfigMaps with many keys, this becomes verbose. Consider the init container approach instead.
  • File ownership is root by default: ConfigMap files are owned by root with the mode specified in defaultMode (default 0644). If your application runs as a non-root user and needs to write to the same directory, the ConfigMap file's permissions may conflict.
  • ConfigMap size limit: ConfigMaps are limited to 1 MiB of data. For larger configuration files, use a persistent volume or bake the config into the container image. Exceeding the limit causes a creation error.

Summary

  • Default ConfigMap volume mounts replace the entire target directory
  • Use subPath to add individual ConfigMap files to an existing directory without overwriting
  • Each file requires its own volumeMount entry with subPath
  • Files mounted with subPath do not auto-update when the ConfigMap changes — pod restart required
  • Use init containers to copy ConfigMap files when many files are involved
  • Use projected volumes to combine ConfigMaps and Secrets into a single mount source

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.