Introduction
The earlier deployment labs used images that already existed in a registry or in the cluster cache. Application developers also need to understand the local image workflow: build an image from application files, make that image available to the Kubernetes node, and tell a Deployment to use it.
In this lab, you will build a small web application image locally with Minikube, deploy it with imagePullPolicy: Never, and then rebuild the application as a second image tag for a rolling update. This mirrors a common CKAD-style task: connect the application artifact to the Kubernetes workload that runs it.
Prepare a Local Image Workspace
In this step, you will prepare a clean namespace and confirm that the image build path is available.
Start in /home/labex/project. You will create the application files, image build files, and Kubernetes manifests in this directory.
mkdir -p /home/labex/project
cd /home/labex/project
Check that the Kubernetes API server is reachable. If the VM has just started and the node is not ready yet, wait a few seconds and run the command again.
kubectl get nodes --request-timeout=5s
Expected output includes:
NAME STATUS ROLES AGE VERSION
labex-v135 Ready control-plane ... v1.35...
Wait until the cluster networking Pod is Ready. The application will be tested through a Service, so networking needs to be healthy.
kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s
Expected output:
pod/calico-node-... condition met
Create a namespace named ckad-prep-local-image and set it as the current namespace:
kubectl create namespace ckad-prep-local-image
kubectl config set-context --current --namespace=ckad-prep-local-image
Confirm the namespace and check the Minikube image command. The minikube image build command builds an image directly for the Minikube profile, so Pods can use it without pushing to a registry.
kubectl config view --minify --output 'jsonpath={..namespace}'; echo
minikube image build --help | sed -n '1,4p'
Expected output includes:
ckad-prep-local-image
Build a container image...
Build the First Local Image
In this step, you will create a small web application and build it as local-message-app:v1.
The image will use BusyBox httpd to serve one static file. The file content is the visible application version, so you can later prove which image tag is running in Kubernetes.
Create the application directory and the first web page:
mkdir -p local-message-app/www
cat > local-message-app/www/index.html <<'EOF'
local-image-v1
EOF
Create a Dockerfile. The COPY instruction puts your local www/index.html file into the image, and CMD starts the BusyBox web server on port 8080 when a container runs from the image.
cat > local-message-app/Dockerfile <<'EOF'
FROM busybox:1.36
COPY www/index.html /www/index.html
EXPOSE 8080
CMD ["httpd", "-f", "-p", "8080", "-h", "/www"]
EOF
Build the image for the active Minikube profile. The -t option gives the image a local tag, and the final path is the build context directory.
minikube image build --profile labex-v135 -t local-message-app:v1 ./local-message-app
Expected output includes:
naming to docker.io/library/local-message-app:v1 done
List images known to Minikube and look for your new tag:
minikube image ls --profile labex-v135 | grep local-message-app
Expected output:
docker.io/library/local-message-app:v1
Deploy the Local Image
In this step, you will run the local image in Kubernetes.
Because local-message-app:v1 is not pushed to a remote registry, the Deployment uses imagePullPolicy: Never. This tells kubelet to use an image that already exists on the node instead of trying to pull it from the network.
Create local-image-app.yaml:
cat > local-image-app.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: local-message-app
spec:
replicas: 2
selector:
matchLabels:
app: local-message-app
template:
metadata:
labels:
app: local-message-app
spec:
terminationGracePeriodSeconds: 0
containers:
- name: web
image: local-message-app:v1
imagePullPolicy: Never
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 1
periodSeconds: 2
---
apiVersion: v1
kind: Service
metadata:
name: local-message-app
spec:
selector:
app: local-message-app
ports:
- port: 80
targetPort: 8080
EOF
Apply the manifest and wait for the Deployment:
kubectl apply -f local-image-app.yaml
kubectl rollout status deployment/local-message-app --timeout=120s
Expected output includes:
deployment.apps/local-message-app created
service/local-message-app created
deployment "local-message-app" successfully rolled out
Create a client Pod and test the Service:
kubectl run image-client --image=busybox:1.36 --restart=Never --command -- sleep 3600
kubectl wait --for=condition=Ready pod/image-client --timeout=120s
kubectl exec image-client -- wget -qO- http://local-message-app
Expected output:
local-image-v1
Rebuild and Roll Out a New Local Image
In this step, you will rebuild the application as a second image tag and roll the Deployment to that tag.
Changing a file on disk does not change a running image. You must build a new image tag and update the Deployment so Kubernetes starts new Pods from that tag. This keeps the version change visible and avoids relying on mutable local image contents.
Replace the web page content with version two:
cat > local-message-app/www/index.html <<'EOF'
local-image-v2
EOF
Build the second image tag:
minikube image build --profile labex-v135 -t local-message-app:v2 ./local-message-app
Expected output includes:
naming to docker.io/library/local-message-app:v2 done
Update the Deployment image and wait for the rollout:
kubectl set image deployment/local-message-app web=local-message-app:v2
kubectl rollout status deployment/local-message-app --timeout=120s
Expected output includes:
deployment.apps/local-message-app image updated
deployment "local-message-app" successfully rolled out
Confirm both the image tag and application response:
kubectl get deployment local-message-app -o jsonpath='{.spec.template.spec.containers[0].image}'; echo
kubectl exec image-client -- wget -qO- http://local-message-app
Expected output:
local-message-app:v2
local-image-v2
Summary
You built a small application image for the Minikube profile, deployed it with imagePullPolicy: Never, tested it through a Kubernetes Service, then rebuilt the application as a new image tag and rolled the Deployment forward. The key workflow is to make the image available to the node before expecting Pods to run it.



