Share Claude Code configuration with PVC direct mount

Both /home/user/.claude/ and /home/user/.claude.json are mounted directly from a dedicated PVC. All changes persist automatically.

The direct mount flow:

  1. A one-time init pod creates an empty skeleton on the PVC: {} in .claude.json and an empty .claude/ directory.

  2. The first workspace starts. The PVC mounts to /home/user/.claude/ and /home/user/.claude.json. Claude Code sees valid, empty configuration.

  3. You configure Claude Code — install plugins, add MCP servers, change settings. All writes go directly to the PVC.

  4. The second workspace starts. The same PVC mounts, and all configuration from the previous workspace is available.

For a comparison with other approaches, see Persist Claude Code configuration in Che workspaces.

Prerequisites
  • An active kubectl session with permissions to create PVCs in your namespace. See Overview of kubectl.

  • Claude Code installed in the workspace container image.

  • 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).

Procedure
  1. Create a file claude-config-pvc.yaml with the following PVC definition:

    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: claude-config
      annotations:
        controller.devfile.io/mount-path: >-
          [{"path":"/home/user/.claude","subPath":".claude"},
           {"path":"/home/user/.claude.json","subPath":".claude.json"}]
      labels:
        controller.devfile.io/mount-to-devworkspace: 'true'
    spec:
      accessModes:
        - ReadWriteMany
      resources:
        requests:
          storage: 1Gi
  2. Apply the PVC to your namespace:

    $ kubectl apply -f claude-config-pvc.yaml -n <your_namespace>
  3. Create the init pod to initialize the PVC.

    The /home/user/.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.

    Create a file claude-config-init-pod.yaml:

    apiVersion: v1
    kind: Pod
    metadata:
      name: claude-config-init
    spec:
      restartPolicy: Never
      containers:
        - image: registry.access.redhat.com/ubi9/ubi-minimal
          name: init
          command:
            - sh
            - -c
            - |
              mkdir -p /mnt/.claude
              echo '{}' > /mnt/.claude.json
              echo "=== Initialization complete ==="
              ls -la /mnt/
          volumeMounts:
            - mountPath: /mnt
              name: vol
      volumes:
        - name: vol
          persistentVolumeClaim:
            claimName: claude-config
  4. Apply the init pod:

    $ kubectl apply -f claude-config-init-pod.yaml -n <your_namespace>
  5. Wait for the pod to complete:

    $ kubectl wait pod/claude-config-init --for=condition=Ready --timeout=120s
  6. Verify the initialization:

    $ kubectl logs claude-config-init
  7. Delete the init pod:

    $ kubectl delete pod claude-config-init
  8. Start any workspace. The PVC auto-mounts into every workspace pod.

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 PVC scheduling failure after init pod setup.

Verification
  1. Start a workspace and configure Claude Code (add an MCP server, install a plugin, or modify settings).

  2. Stop and restart the workspace. Verify the configuration is preserved.

  3. Start a different workspace. Verify the same configuration is available.