|
| 1 | +:_content-type: PROCEDURE |
| 2 | +:description: Share Claude Code configuration across {prod-short} workspaces by using a dedicated PVC with automount. |
| 3 | +:keywords: user-guide, claude-code, ai, configuration, persistence, pvc, automount |
| 4 | +:navtitle: Sharing configuration with a dedicated PVC |
| 5 | +:page-aliases: |
| 6 | + |
| 7 | +[id="sharing-claude-code-configuration-with-a-dedicated-pvc"] |
| 8 | += Sharing Claude Code configuration across workspaces with a dedicated PVC |
| 9 | + |
| 10 | +A dedicated PVC in the user {orch-namespace} shares Claude Code configuration across all workspaces. The {devworkspace} Operator automatically mounts PVCs labeled with `controller.devfile.io/mount-to-devworkspace: true` into every workspace pod. |
| 11 | + |
| 12 | +Two options are available: |
| 13 | + |
| 14 | +* *Option A: Direct mount* — both `{prod-home}/.claude/` and `{prod-home}/.claude.json` are mounted directly from the PVC. All changes persist automatically. Requires a one-time init pod to pre-create `.claude.json`. |
| 15 | +* *Option B: Full `/tmp` copy* — the entire PVC is mounted at `/tmp/claude`. A `postStart` command copies everything into the home directory. No init pod is required, but changes must be synced back manually before stopping the workspace. |
| 16 | + |
| 17 | +|=== |
| 18 | +| Feature | Option A: Direct mount | Option B: Full `/tmp` copy |
| 19 | + |
| 20 | +| Init pod required |
| 21 | +| Yes (one-time) |
| 22 | +| No |
| 23 | + |
| 24 | +| `postStart` required |
| 25 | +| No |
| 26 | +| Yes |
| 27 | + |
| 28 | +| Manual sync before stop |
| 29 | +| No |
| 30 | +| Yes |
| 31 | + |
| 32 | +| AZ affinity risk |
| 33 | +| Yes (RWO on multi-AZ clusters) |
| 34 | +| Yes (RWO on multi-AZ clusters) |
| 35 | +|=== |
| 36 | + |
| 37 | +For an overview of all approaches, see xref:persisting-claude-code-configuration.adoc[]. |
| 38 | + |
| 39 | +.Prerequisites |
| 40 | + |
| 41 | +* An active `{orch-cli}` session with permissions to create PVCs in your {orch-namespace}. See {orch-cli-link}. |
| 42 | + |
| 43 | +* Claude Code installed in the workspace container image. |
| 44 | + |
| 45 | +* For concurrent workspaces: a storage class that supports `ReadWriteMany` (RWX) access mode, such as AWS EFS or NFS. Standard block storage (gp2, gp3) supports only `ReadWriteOnce` (RWO). |
| 46 | + |
| 47 | +== Option A: Direct mount |
| 48 | + |
| 49 | +All Claude Code configuration is mounted directly from the PVC. Changes persist automatically without any `postStart` commands or manual sync steps. |
| 50 | + |
| 51 | +=== Creating the PVC |
| 52 | + |
| 53 | +. Create a file `claude-config-pvc.yaml` with the following PVC definition: |
| 54 | ++ |
| 55 | +==== |
| 56 | +[source,yaml,subs="+quotes,+attributes"] |
| 57 | +---- |
| 58 | +kind: PersistentVolumeClaim |
| 59 | +apiVersion: v1 |
| 60 | +metadata: |
| 61 | + name: claude-config |
| 62 | + annotations: |
| 63 | + controller.devfile.io/mount-path: >- |
| 64 | + [{"path":"{prod-home}/.claude","subPath":".claude"}, |
| 65 | + {"path":"{prod-home}/.claude.json","subPath":".claude.json"}] |
| 66 | + labels: |
| 67 | + controller.devfile.io/mount-to-devworkspace: 'true' |
| 68 | +spec: |
| 69 | + accessModes: |
| 70 | + - ReadWriteMany |
| 71 | + resources: |
| 72 | + requests: |
| 73 | + storage: 1Gi |
| 74 | +---- |
| 75 | +==== |
| 76 | + |
| 77 | +. Apply the PVC to your {orch-namespace}: |
| 78 | ++ |
| 79 | +[subs="+quotes,+attributes"] |
| 80 | +---- |
| 81 | +$ {orch-cli} apply -f claude-config-pvc.yaml -n __<your_{orch-namespace}>__ |
| 82 | +---- |
| 83 | + |
| 84 | +=== Initializing the PVC with an init pod |
| 85 | + |
| 86 | +The `{prod-home}/.claude.json` subPath target must exist as a *file* on the PVC before any workspace starts. Without this step, {kubernetes} kubelet creates a directory instead of a file, and Claude Code fails to parse it. |
| 87 | + |
| 88 | +. Create a file `claude-config-init-pod.yaml` with the following pod definition: |
| 89 | ++ |
| 90 | +==== |
| 91 | +[source,yaml,subs="+quotes,+attributes"] |
| 92 | +---- |
| 93 | +apiVersion: v1 |
| 94 | +kind: Pod |
| 95 | +metadata: |
| 96 | + name: claude-config-init |
| 97 | +spec: |
| 98 | + containers: |
| 99 | + - image: registry.access.redhat.com/ubi9/ubi-minimal |
| 100 | + name: init |
| 101 | + command: |
| 102 | + - sh |
| 103 | + - -c |
| 104 | + - | |
| 105 | + mkdir -p /mnt/.claude |
| 106 | + echo '{}' > /mnt/.claude.json |
| 107 | + echo "=== Initialization complete ===" |
| 108 | + ls -la /mnt/ |
| 109 | + tail -f /dev/null |
| 110 | + volumeMounts: |
| 111 | + - mountPath: /mnt |
| 112 | + name: vol |
| 113 | + volumes: |
| 114 | + - name: vol |
| 115 | + persistentVolumeClaim: |
| 116 | + claimName: claude-config |
| 117 | +---- |
| 118 | +==== |
| 119 | + |
| 120 | +. Apply the init pod, wait for it to initialize, verify, and delete: |
| 121 | ++ |
| 122 | +[subs="+quotes,+attributes"] |
| 123 | +---- |
| 124 | +$ {orch-cli} apply -f claude-config-init-pod.yaml -n __<your_{orch-namespace}>__ |
| 125 | +$ {orch-cli} wait pod/claude-config-init --for=condition=Ready --timeout=120s |
| 126 | +$ {orch-cli} logs claude-config-init |
| 127 | +$ {orch-cli} delete pod claude-config-init |
| 128 | +---- |
| 129 | + |
| 130 | +=== Using workspaces |
| 131 | + |
| 132 | +Start any workspace. The PVC auto-mounts into every workspace pod. Claude Code finds its configuration at `{prod-home}/.claude/` and `{prod-home}/.claude.json` on startup. All changes, including new MCP servers, plugins, and settings, are written directly to the PVC and available in every workspace. |
| 133 | + |
| 134 | +No `postStart` commands or manual sync steps are required. |
| 135 | + |
| 136 | +[WARNING] |
| 137 | +==== |
| 138 | +On multi-AZ clusters using `WaitForFirstConsumer` storage classes (gp2, gp3), the init pod may bind the PV to a different availability zone than workspace pods. See xref:troubleshooting-claude-code-configuration-persistence.adoc#pvc-scheduling-failure-after-init-pod-setup[PVC scheduling failure after init pod setup]. |
| 139 | +==== |
| 140 | + |
| 141 | +=== Verification |
| 142 | + |
| 143 | +. Start a workspace and configure Claude Code (add an MCP server, install a plugin, or modify settings). |
| 144 | +. Stop and restart the workspace. Verify the configuration is preserved. |
| 145 | +. Start a different workspace. Verify the same configuration is available. |
| 146 | + |
| 147 | +== Option B: Full `/tmp` copy |
| 148 | + |
| 149 | +The entire PVC is mounted at `/tmp/claude`. A `postStart` command copies everything into the home directory. No init pod is required and there are no subPath limitations. |
| 150 | + |
| 151 | +=== Creating the PVC |
| 152 | + |
| 153 | +. Create a file `claude-config-pvc.yaml` with the following PVC definition: |
| 154 | ++ |
| 155 | +==== |
| 156 | +[source,yaml,subs="+quotes,+attributes"] |
| 157 | +---- |
| 158 | +kind: PersistentVolumeClaim |
| 159 | +apiVersion: v1 |
| 160 | +metadata: |
| 161 | + name: claude-config |
| 162 | + annotations: |
| 163 | + controller.devfile.io/mount-path: '[{"path":"/tmp/claude","subPath":"claude"}]' |
| 164 | + labels: |
| 165 | + controller.devfile.io/mount-to-devworkspace: 'true' |
| 166 | +spec: |
| 167 | + accessModes: |
| 168 | + - ReadWriteMany |
| 169 | + resources: |
| 170 | + requests: |
| 171 | + storage: 1Gi |
| 172 | +---- |
| 173 | +==== |
| 174 | + |
| 175 | +. Apply the PVC to your {orch-namespace}: |
| 176 | ++ |
| 177 | +[subs="+quotes,+attributes"] |
| 178 | +---- |
| 179 | +$ {orch-cli} apply -f claude-config-pvc.yaml -n __<your_{orch-namespace}>__ |
| 180 | +---- |
| 181 | + |
| 182 | +=== Adding a `postStart` command to copy configurations |
| 183 | + |
| 184 | +Add a `postStart` command to your devfile to copy all configuration from the PVC into the home directory: |
| 185 | + |
| 186 | +==== |
| 187 | +[source,yaml,subs="+quotes,+attributes"] |
| 188 | +---- |
| 189 | +commands: |
| 190 | + - id: init-claude-config |
| 191 | + exec: |
| 192 | + commandLine: | |
| 193 | + mkdir -p {prod-home}/.claude |
| 194 | + cp -a /tmp/claude/.claude/. {prod-home}/.claude/ 2>/dev/null || true |
| 195 | + cp /tmp/claude/.claude.json {prod-home}/.claude.json 2>/dev/null || true |
| 196 | + component: __<editor_container>__ # <1> |
| 197 | +events: |
| 198 | + postStart: |
| 199 | + - init-claude-config |
| 200 | +---- |
| 201 | +<1> Replace with the name of your editor container component in the devfile. |
| 202 | +==== |
| 203 | + |
| 204 | +=== Syncing changes before stopping |
| 205 | + |
| 206 | +Changes to both `{prod-home}/.claude/` and `{prod-home}/.claude.json` are written to the home directory, not the PVC. Sync everything back before stopping: |
| 207 | + |
| 208 | +[subs="+quotes,+attributes"] |
| 209 | +---- |
| 210 | +$ cp -a {prod-home}/.claude/. /tmp/claude/.claude/ && \ |
| 211 | + cp {prod-home}/.claude.json /tmp/claude/.claude.json |
| 212 | +---- |
| 213 | + |
| 214 | +=== Verification |
| 215 | + |
| 216 | +. Start a workspace and configure Claude Code. |
| 217 | +. Sync changes back to the PVC. |
| 218 | +. Start a different workspace. |
| 219 | +. Verify that the Claude Code configuration from the first workspace is available. |
| 220 | + |
| 221 | +== Shared options for both variants |
| 222 | + |
| 223 | +=== Filtering by workspace name |
| 224 | + |
| 225 | +Control which workspaces mount the PVC by adding annotations: |
| 226 | + |
| 227 | +[source,yaml,subs="+quotes,+attributes"] |
| 228 | +---- |
| 229 | +annotations: |
| 230 | + controller.devfile.io/mount-to-devworkspace-include: '__<pattern>__' # <1> |
| 231 | + controller.devfile.io/mount-to-devworkspace-exclude: '__<pattern>__' # <2> |
| 232 | +---- |
| 233 | +<1> Mount the PVC only to workspaces whose names match the pattern. |
| 234 | +<2> Mount the PVC to all workspaces except those whose names match the pattern. |
| 235 | + |
| 236 | +Supported patterns: `*` (all), `prefix*`, `*suffix`, `*contains*`, `exact-name`. Matching is on the {devworkspace} resource name. |
| 237 | + |
| 238 | +=== Concurrent workspaces |
| 239 | + |
| 240 | +|=== |
| 241 | +| Access mode | Behavior |
| 242 | + |
| 243 | +| ReadWriteOnce (RWO) |
| 244 | +| One workspace at a time. Two workspaces on different nodes cause a multi-attach error. |
| 245 | + |
| 246 | +| ReadWriteMany (RWX) |
| 247 | +| Concurrent workspaces work. Requires a storage class that supports RWX, such as AWS EFS or NFS. Standard block storage (gp2, gp3) does not support RWX. |
| 248 | +|=== |
| 249 | + |
| 250 | +== Limitations |
| 251 | + |
| 252 | +* Requires manual PVC setup per user {orch-namespace}. |
| 253 | +* Can be combined with `persistUserHome`. The dedicated PVC at `{prod-home}/.claude` and the persistent home PVC at `{prod-home}/` coexist correctly as nested mounts. |
| 254 | +* RWX storage may not be available on all clusters. |
| 255 | +* On clusters with only block storage and multi-AZ topology, the Option A init pod may cause scheduling failures. Use Option B instead. |
| 256 | + |
| 257 | +.Additional resources |
| 258 | + |
| 259 | +* xref:persisting-claude-code-configuration.adoc[] |
| 260 | +* xref:sharing-claude-code-configuration-with-gitops.adoc[] |
| 261 | +* xref:troubleshooting-claude-code-configuration-persistence.adoc[] |
| 262 | +* xref:requesting-persistent-storage-for-workspaces.adoc[] |
0 commit comments