# Ship config as an OCI artifact and mount it as an image volume

> hello 2.1 crashes: its config came from a gitRepo volume, gone in Kubernetes 1.36. Mount the config artifact CI publishes as an image volume instead.

Level: Intermediate · Topic: Storage · About 10 minutes
Try it in the browser: https://k8wiz.com/playground/mount-config-from-an-oci-image-volume

The playground is a simulated Kubernetes cluster that runs in the browser. kubectl output and errors follow the real ones.

## The situation

hello 2.1 reads /etc/hello/config.yaml. The file used to come from a gitRepo volume. Kubernetes 1.36 removed that volume type for good, so it was taken out of the Deployment, and the Pods now crash on startup.

CI already publishes the config as an OCI artifact, `ghcr.io/k8wiz/hello-config:2.1`. Image volumes, stable since Kubernetes 1.36, mount the files of an OCI image or artifact into a Pod, read-only. Mount it at /etc/hello.

## Objectives

1. Read why hello crashes (`kubectl logs deploy/hello`)
2. Mount ghcr.io/k8wiz/hello-config:2.1 at /etc/hello as an image volume
3. hello runs 3 ready replicas
4. See the greeting the mounted config sets (`kubectl exec client -- wget -qO- http://hello`)

## Hints

1. `kubectl logs deploy/hello` shows the panic: /etc/hello/config.yaml doesn't exist. `images` lists the OCI artifacts the cluster can pull.
2. In the Pod template, add a volume `- name: config` with `image: {reference: ghcr.io/k8wiz/hello-config:2.1, pullPolicy: IfNotPresent}`, and mount it in the container at `/etc/hello` with `readOnly: true`. hello.yaml in the Editor has the Deployment.
3. `kubectl exec deploy/hello -- cat /etc/hello/config.yaml` shows the file, and `kubectl exec client -- wget -qO- http://hello` shows the greeting it sets.

## Solution

```bash
kubectl logs deploy/hello
```
```bash
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      volumes:
      - name: config
        image:
          reference: ghcr.io/k8wiz/hello-config:2.1
          pullPolicy: IfNotPresent
      containers:
      - name: hello
        image: ghcr.io/k8wiz/hello:2.1
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: config
          mountPath: /etc/hello
          readOnly: true
EOF
```
```bash
kubectl rollout status deployment/hello --timeout=60s
```
```bash
kubectl exec deploy/hello -- cat /etc/hello/config.yaml
```
```bash
kubectl exec client -- wget -qO- http://hello
```

## What it teaches

- Kubernetes 1.36 removed the gitRepo volume for good, because it could let an attacker run code as root on the node. Replace it with an init container that clones the repo, or with an image volume.
- An image volume mounts the files of an OCI image or artifact, read-only, without baking them into the app's image. The kubelet pulls it like a container image, with the same pull policy and events.
- Config, models and static content can ship through your registry (for example with `oras push`) and roll out by changing a tag.
- Image volumes need a container runtime that supports them, such as containerd 2.1 or later.
