How to Use Skaffold for Kubernetes Development on RHEL 7
One of the most tedious aspects of Kubernetes development is the inner loop: make a code change, build a container image, push it to a registry, update the Kubernetes manifest, apply it to the cluster, and wait for the rolling update — repeated dozens of times per day. Skaffold automates this entire cycle. Developed by Google, Skaffold is an open-source command-line tool that watches your source files and, on every change, rebuilds the image (or syncs files directly into a running container), pushes the new artifact, and re-deploys to your cluster in seconds. On RHEL 7, installing Skaffold is a single binary download. This tutorial covers the full workflow: downloading and installing the Skaffold binary, understanding the skaffold.yaml configuration file, using skaffold dev for hot-reload development, skaffold run for CI/CD, skaffold build for artifact-only pipelines, file sync for sub-second iteration, integrating with Helm and raw kubectl manifests, and using Jib to build Java images without a local Docker daemon.
Prerequisites
- RHEL 7 server with
sudoor root access - Docker installed and running (
sudo yum install -y docker && sudo systemctl enable --now docker) kubectlconfigured to point to a running Kubernetes cluster (minikube, kind, or a remote cluster)- Access to a container registry (Docker Hub, Quay.io, or a local registry)
- For Java/Jib builds: JDK 11+ installed (
sudo yum install -y java-11-openjdk-devel) - For Helm integration: Helm 3 installed (
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash)
Step 1: Download and Install the Skaffold Binary
Skaffold is distributed as a standalone binary — no package manager required:
curl -Lo skaffold
https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64
chmod +x skaffold
sudo mv skaffold /usr/local/bin/
# Verify the installation
skaffold version
# v2.11.0
Skaffold also supports tab completion for bash:
skaffold completion bash | sudo tee /etc/bash_completion.d/skaffold > /dev/null
source /etc/bash_completion.d/skaffold
Step 2: Initialise a Project with skaffold init
For an existing project with a Dockerfile and Kubernetes manifests, skaffold init generates a skaffold.yaml automatically by inspecting the workspace:
cd ~/my-app
skaffold init --generate-manifests
If you are starting from scratch, create the project structure manually:
mkdir -p ~/skaffold-demo/k8s
cd ~/skaffold-demo
# Sample Python Flask app
cat > app.py <<'EOF'
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello from Skaffold on RHEL 7!n'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
EOF
cat > requirements.txt <<'EOF'
flask==3.0.3
EOF
cat > Dockerfile <<'EOF'
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
EOF
Create a Kubernetes Deployment and Service manifest:
cat > k8s/deployment.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app
ports:
- containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- port: 5000
targetPort: 5000
type: ClusterIP
EOF
Step 3: Write the skaffold.yaml Configuration
The skaffold.yaml file ties together the build, test, and deploy stages. Create it in the project root:
cat > skaffold.yaml <<'EOF'
apiVersion: skaffold/v4beta11
kind: Config
metadata:
name: my-app
build:
artifacts:
- image: my-app
docker:
dockerfile: Dockerfile
local:
push: false # Don't push to registry when using local cluster
useBuildkit: true # Use Docker BuildKit for faster builds
# Uncomment for a remote registry:
# tagPolicy:
# gitCommit: {}
deploy:
kubectl:
manifests:
- k8s/*.yaml
# File sync: copy changed Python files directly into the running container
# without a full rebuild
portForward:
- resourceType: service
resourceName: my-app
port: 5000
localPort: 5000
profiles:
- name: production
build:
artifacts:
- image: quay.io/your-org/my-app
docker:
dockerfile: Dockerfile
tagPolicy:
gitCommit: {}
deploy:
kubectl:
manifests:
- k8s/*.yaml
- k8s/production/*.yaml
- name: staging
patches:
- op: replace
path: /deploy/kubectl/manifests
value:
- k8s/*.yaml
- k8s/staging/*.yaml
EOF
Step 4: skaffold dev — Hot Reload Development Loop
skaffold dev is the core development command. It builds the image, deploys to the cluster, streams logs to your terminal, and watches for file changes to trigger a rebuild and redeploy:
skaffold dev --port-forward
The --port-forward flag automatically port-forwards the service to your localhost so you can test at http://localhost:5000. When you save a change to app.py, Skaffold detects the change, rebuilds the Docker image, pushes it (if configured), updates the Deployment, and redeploys — all within seconds. Press Ctrl+C to stop; Skaffold automatically tears down the deployed resources.
Step 5: File Sync for Sub-Second Iteration
A full Docker build on every file change is too slow for tight feedback loops. Skaffold’s file sync feature copies changed files directly into a running container without rebuilding the image, achieving sub-second updates. Add a sync block to the artifact definition in skaffold.yaml:
build:
artifacts:
- image: my-app
docker:
dockerfile: Dockerfile
sync:
infer:
- '**/*.py' # Sync Python files directly; skip rebuild
manual:
- src: 'templates/**'
dest: /app/templates
With infer mode, Skaffold examines the Dockerfile COPY instructions to determine destination paths automatically. With manual mode, you specify the source glob and the container destination path explicitly. For interpreted languages (Python, Ruby, JavaScript with nodemon), file sync provides a near-native development experience inside Kubernetes.
Step 6: skaffold run for CI/CD Pipelines
skaffold run performs a one-shot build-and-deploy without watching for changes. It is designed for CI/CD pipelines where you want to build, push, and deploy once then exit:
# Build and deploy using the production profile
skaffold run --profile production
--default-repo quay.io/your-org
--tag "$(git rev-parse --short HEAD)"
# Build only — don't deploy
skaffold build --profile production
--file-output build-artifacts.json
# Deploy a previously built set of artifacts
skaffold deploy --profile production
--build-artifacts build-artifacts.json
The --build-artifacts pattern is particularly useful for separating the build and deploy stages across different pipeline jobs — the build job produces a JSON file listing image digests, and the deploy job consumes it without needing to rebuild.
Step 7: Integration with Helm
Replace the deploy.kubectl section with deploy.helm to deploy via a Helm chart:
deploy:
helm:
releases:
- name: my-app
chartPath: helm/my-app
valuesFiles:
- helm/my-app/values.yaml
setValues:
image.repository: my-app
image.tag: "" # Skaffold fills this in automatically
upgradeOnChange: true
Skaffold automatically injects the newly built image tag into the Helm release, so your values.yaml does not need to be updated between builds.
Step 8: Jib for Java Builds (No Docker Daemon Required)
Jib is a Google-developed Maven/Gradle plugin that builds container images from Java source without needing a Docker daemon. Skaffold has native Jib support:
# In skaffold.yaml, replace the docker builder with jib:
build:
artifacts:
- image: quay.io/your-org/my-java-app
jib:
project: my-module # Maven module name, or omit for single-module
args:
- '-Djib.container.jvmFlags=-Xms512m,-Xmx1024m'
Add the Jib Maven plugin to pom.xml:
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.4.2</version>
<configuration>
<to>
<image>quay.io/your-org/my-java-app</image>
</to>
<container>
<mainClass>com.example.Main</mainClass>
<ports>
<port>8080</port>
</ports>
</container>
</configuration>
</plugin>
Jib builds are reproducible and layer-optimised — only changed layers (dependencies, resources, classes) are rebuilt and pushed, making incremental builds extremely fast. This makes it an excellent choice for Java microservices in CI pipelines where build speed directly impacts developer throughput.
Skaffold transforms the Kubernetes development experience on RHEL 7 from a slow, manual cycle into an automated tight loop that rivals the speed of local development. By combining file sync for instant code updates, profile-based configuration for environment-specific builds, and first-class integration with Helm and Jib, Skaffold scales from a solo developer’s laptop to a team’s shared CI/CD pipeline without requiring changes to the core skaffold.yaml. As your project matures, explore Skaffold’s verify stage for post-deployment smoke tests, the render command for GitOps-style manifest generation, and the integration with Cloud Code in VS Code and IntelliJ for an IDE-native Kubernetes development experience.