Kubernetes v1.33 has finally brought Image Volume to β status! (※disabled by default)
This feature allows you to mount OCI images directly as read-only volumes – isn’t this revolutionary?
🧠 What Makes Image Volume So Amazing?
Say goodbye to the old way of “cramming everything into one container”, and hello to:
- Application code
- Runtime
- Libraries
…all split into separate images and mounted independently!
This is especially powerful for scripting language users (Node.js/Python/etc.)!
Example: Mount a “code-only” image containing your app, and run it with node:22.15.1
.
🧱 Building an Ultra-Lightweight “Code-Only” Image
The beauty of Image Volume is creating ultra-lightweight OCI images containing only code and config files.
Let’s build a simple example with just Node.js application code.
📁 index.js
– The App
const http = require('http');
http.createServer((_, res) => res.end('Hello from OCI volume!n'))
.listen(process.env.PORT || 80);
A simple HTTP server in Node.js that returns “Hello from OCI volume!”
🐳 Dockerfile
– The Shocking “FROM scratch”
FROM scratch
COPY app /
Starting FROM scratch
(= nothing) and just COPY
ing the code.
That’s right – not even Node.js is included.
What does this mean? The runtime (Node.js) is provided by a separate container,
and this image is used purely as a “code repository.”
📏 The Size? A Mere 134 Bytes!
This OCI image, composed of just these 3 files, weighs in at only 134 bytes!
docker images | grep hello-app
localhost:5001/hello-app 799466d0 c4bd064f6fff About a minute ago 134B
- Builds in an instant ✅
- Pushes to registry almost instantly ✅
- Minimal CI/CD load ✅
As long as the code doesn’t change, this image can be reused, improving cache hit rates.
This approach of “packaging only application code as an image” brings a completely different worldview
from traditional “all-in-one containers.”
With Image Volume, you can completely separate the application code lifecycle from the runtime lifecycle.
📦 Instant Base Image Patches! See the Diff
With ImageVolume, following security patches for base images becomes surprisingly easy.
✅ Pattern 1: Fixed Tags with Manual Updates
For example, when a vulnerability patch is released for the node:22.15.0
base image:
- image: mirror.gcr.io/node:22.15.0
+ image: mirror.gcr.io/node:22.15.1
Just update the Pod
‘s image
and redeploy – that’s it!
No changes needed to the “app code” image!
No CI rebuilds needed. Just change one line of YAML, and security patches are applied instantly.
🔄 Pattern 2: Floating Tags (node:22
) for Automatic Updates
For a more “fully automated” approach, use major version tags (node:22
) instead of minor versions,
and set imagePullPolicy: Always
. Patches are applied with just a Pod restart.
image: node:22
imagePullPolicy: Always
This way, updates from 22.15.0 → 22.15.1
→ 22.16.0
are automatically applied with just Pod restarts.
:::caution ⚠️ Caution
While this method always incorporates the latest patches,
there’s also the risk of immediately hitting regressions (bugs in new versions).
Careful consideration is needed for production environments where stability is paramount.
Explicit tags are safer for production.
:::
💡 Summary
Strategy | Benefits | Considerations |
---|---|---|
Fixed tags (node:22.15.1 ) |
Stable, predictable behavior | Manual updates required |
Floating tags + Pull Always (node:22 ) |
Latest patches auto-applied | May upgrade to unintended versions |
Choose a tag strategy that balances risk and automation based on your situation and environment.
🧪 Try It Now! Complete Automated Script Included
For those eager to experience the Image Volume world, here’s a one-shot verification script:
✅ Full Verification Script:
#!/usr/bin/env bash
set -euox pipefail
# Create a temporary file for the upstream script
TMP_SCRIPT=$(mktemp)
echo "Downloading upstream kind-with-registry.sh to $TMP_SCRIPT"
# Download the upstream script
curl -fsSL https://kind.sigs.k8s.io/examples/kind-with-registry.sh -o "$TMP_SCRIPT"
# Patch and execute the script
# This adds the ImageVolume feature gate after the apiVersion line
echo "Patching and executing the script with ImageVolume feature gate enabled"
awk '
/^apiVersion: kind.x-k8s.io/v1alpha4/ {
print
print "featureGates:n ImageVolume: true"
next
}
{print}
' "$TMP_SCRIPT" | bash -s
##############################################################################
# 2. Build & push "code-only" OCI image
##############################################################################
WORK=$(mktemp -d)
echo "Workdir: $WORK"
mkdir -p "$WORK/app"
cat > "$WORK/app/index.js" <<'JS'
const http = require('http');
http.createServer((_, res) => res.end('Hello from OCI volume!n'))
.listen(process.env.PORT || 80);
JS
cat > "$WORK/Dockerfile" <<'DOCKER'
FROM scratch
COPY app /
DOCKER
RAND_TAG=$(openssl rand -hex 4)
FULL_IMAGE="localhost:5001/hello-app:${RAND_TAG}"
docker build -f "$WORK/Dockerfile" -t "${FULL_IMAGE}" "$WORK"
docker push "${FULL_IMAGE}"
##############################################################################
# 4. Apply Pod manifest (referencing ImageVolume)
##############################################################################
cat > "$WORK/node-demo.yaml" <<EOF
apiVersion: v1
kind: Pod
metadata:
name: node-demo
spec:
containers:
- name: runtime
image: mirror.gcr.io/node:22-slim
command: ["node", "/usr/src/app/index.js"]
workingDir: /usr/src/app
ports: [{containerPort: 80}]
volumeMounts:
- mountPath: /usr/src/app
name: app-src
volumes:
- name: app-src
image:
reference: ${FULL_IMAGE}
pullPolicy: IfNotPresent
EOF
sleep 10
kubectl apply -f "$WORK/node-demo.yaml"
kubectl wait --for=condition=Ready pod/node-demo --timeout=120s
##############################################################################
# 5. Verify it works
##############################################################################
kubectl port-forward pod/node-demo 3000:80 >/dev/null &
PF=$!
sleep 2
echo -n "curl → "
curl http://localhost:3000
kill $PF
cat <<EOM
✅ Done! Node.js is running with code mounted via ImageVolume.
Cleanup:
kind delete cluster
rm -rf ${WORK}
EOM
Just copy-paste and run this script to launch a Node.js app using Image Volume in your local environment!
🎯 Use Cases: Where This Shines
Use Case | Traditional Method | With Image Volume |
---|---|---|
Node.js code distribution (e.g., node:22.15.1 ) |
Rebuild with code baked into base | Separate code image, just mount it |
Python library sharing (e.g., python:3.11-slim ) |
Build for each app | Fix common env as base, manage apps separately |
AI models or certificate deployment | Pack everything into huge images | Share models/certs as Image Volumes |
✅ Conclusion: This is the Future Default
- “Code-only” images mean faster builds & better cache hits
- Base image updates = instant patches
- Slimmer CI/CD, improved security
Even if “mounting images” didn’t quite click before,
experience firsthand how this mechanism will become the next-generation best practice.