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 sudo or root access
  • Docker installed and running (sudo yum install -y docker && sudo systemctl enable --now docker)
  • kubectl configured 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.