Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Local Kubernetes Dev with Tilt and Skaffold

By Kokil Thapa | Last reviewed: September 2026

Your Laravel API passes every PHPUnit run on the laptop. Then it fails in staging because the Ingress path, probe timing, or ConfigMap key differs from Docker Compose. Local Kubernetes Dev with Tilt and Skaffold closes that gap. Both tools watch your source tree, rebuild images, and redeploy into a local cluster such as kind or Minikube. You exercise the same Deployments, Services, and Secrets you will ship. On production Laravel and booking systems I maintain, that mismatch between compose files and cluster manifests caused more late-night fixes than application bugs. This guide walks through a practical setup you can copy today.

What is Local Kubernetes Dev with Tilt and Skaffold?

Local Kubernetes development means running your application inside a real cluster on your machine. Docker Compose simulates services with containers on a single host. Kubernetes adds scheduling, Services, Ingress, and volume claims. Tools like Tilt and Skaffold remove the manual kubectl apply cycle after every code save.

Both sit between your editor and the cluster API. They detect file changes, build container images, push or load them into the cluster, and update running Pods. They also stream logs and expose port forwards. The goal is parity: what you test locally should match what GitOps deploys to staging.

Local Kubernetes Dev StackDeveloperIDE + GitSource treeTilt / SkaffoldBuild + syncPort forwardLocal Clusterkind / MinikubePods + IngressApp workloads inside clusterWeb (PHP-FPM)Queue workerMySQL / RedisSame manifests as staging and production
Local Kubernetes Dev with Tilt and Skaffold connects your editor to a real cluster running production-like manifests.

A typical stack for a PHP team includes these pieces:

  • A local cluster driver: kind, Minikube, k3d, or Docker Desktop Kubernetes.
  • Container images built with Docker or BuildKit.
  • Manifests in plain YAML, Kustomize overlays, or Helm charts.
  • Skaffold or Tilt orchestrating the inner dev loop.

If you still run Laravel on Compose alone, read local Laravel dev with Sail and Docker first. Sail is excellent for feature work. Move to Kubernetes locally when ingress rules, sidecars, or multi-replica behaviour matter.

How do you set up a local Kubernetes cluster for development?

Before Tilt or Skaffold can deploy anything, you need a running cluster and kubectl context pointed at it. kind is my default on Ubuntu laptops. It spins up fast and tears down cleanly in CI.

Create a kind cluster with ingress

# Install kind (Linux amd64 example)
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.26.0/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind

# Cluster config with extra port mappings for HTTP
cat > kind-config.yaml <<'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"
    extraPortMappings:
      - containerPort: 80
        hostPort: 8080
        protocol: TCP
      - containerPort: 443
        hostPort: 8443
        protocol: TCP
EOF

kind create cluster --name dev --config kind-config.yaml
kubectl cluster-info --context kind-dev

Minikube remains a solid choice when you want a built-in tunnel or driver abstraction. The comparison in our Minikube vs kind guide covers RAM usage and CI fit. Allocate at least 4 GB RAM and two CPU cores for a three-service app with MySQL.

Install ingress and metrics basics

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=120s

Verify the cluster before adding Skaffold or Tilt. A broken context wastes an hour of cryptic build errors. Run kubectl get nodes and confirm Ready status.

How do you configure Skaffold for local Kubernetes development?

Skaffold reads a skaffold.yaml at the repo root. It defines build artifacts, deploy method, and optional file sync rules. Google maintains it. Teams that already use Cloud Build or GKE often adopt Skaffold because the same config runs in CI with skaffold run.

Minimal skaffold.yaml for a Laravel-style app

apiVersion: skaffold/v4beta11
kind: Config
metadata:
  name: laravel-dev
build:
  local:
    push: false
  artifacts:
    - image: myapp/web
      context: .
      docker:
        dockerfile: Dockerfile
      sync:
        manual:
          - src: "app//*.php"
            dest: /var/www/html/app
          - src: "routes//*.php"
            dest: /var/www/html/routes
          - src: "resources/views/**/*.blade.php"
            dest: /var/www/html/resources/views
    - image: myapp/queue
      context: .
      docker:
        dockerfile: Dockerfile.queue
deploy:
  kubectl:
    manifests:
      - k8s/base/*.yaml
portForward:
  - resourceType: service
    resourceName: web
    port: 80
    localPort: 8080

Start the dev loop with one command:

skaffold dev --port-forward

Skaffold watches files, rebuilds when Docker layers change, and syncs PHP files without a full image rebuild when sync rules match. That mirrors how Docker Compose hot paths feel, but against real Deployments.

Skaffold Dev LoopFile saveBuild imageor sync filesDeployPod readyParallel actions while dev runsLog tailPort forwardStatus watchCtrl+C tears down deployed resources (dev mode)
Skaffold dev mode automates build, deploy, and port-forward on every source change during local Kubernetes development.

Skaffold profiles for staging parity

Use profiles when staging runs Helm but local dev uses plain manifests:

profiles:
  - name: local
    activation:
      - command: dev
    deploy:
      kubectl:
        manifests:
          - k8s/overlays/local/*.yaml
  - name: ci
    activation:
      - env: CI=true
    build:
      local:
        push: true
    deploy:
      helm:
        releases:
          - name: myapp
            chartPath: charts/myapp

Validate YAML syntax with our JSON and YAML formatter before debugging Skaffold parse errors. A trailing tab in skaffold.yaml fails silently until runtime.

How do you configure Tilt for local Kubernetes development?

Tilt uses a Tiltfile written in Starlark. It excels when you run five or more services and need one dashboard for logs, builds, and health. The web UI at localhost:10350 shows which resource is failing and why.

Example Tiltfile for web, queue, and Redis

# Tiltfile
allow_k8s_contexts('kind-dev')

docker_build('myapp/web', '.',
  dockerfile='Dockerfile',
  live_update=[
    sync('./app', '/var/www/html/app'),
    sync('./routes', '/var/www/html/routes'),
    sync('./resources/views', '/var/www/html/resources/views'),
    run('php artisan view:clear', trigger=['./resources/views']),
  ]
)

docker_build('myapp/queue', '.',
  dockerfile='Dockerfile.queue',
  live_update=[
    sync('./app', '/var/www/html/app'),
  ]
)

k8s_yaml(['k8s/base/web.yaml', 'k8s/base/queue.yaml', 'k8s/base/redis.yaml'])

k8s_resource('web',
  port_forwards=8080,
  resource_deps=['redis'],
)

k8s_resource('queue',
  resource_deps=['redis'],
)

Run tilt up in the repo root. Tilt builds images, loads them into kind, applies manifests, and opens the UI. Resource dependencies prevent the web Pod from starting before Redis is reachable.

For Laravel specifically, pair this with the patterns in Kubernetes for Laravel getting started. Set APP_KEY and database URLs through Secrets, not hard-coded env blocks in the Tiltfile.

Tilt extensions worth enabling

  • helm_resource for charts you already ship to production.
  • local_resource for asset builds when Vite 8.x runs on the host.
  • ci_settings to disable the browser UI in headless pipelines.

I have used Tilt on multi-service booking platforms where web, worker, scheduler, and Redis each had separate Deployments. One screen beat four terminal panes of kubectl logs.

Tilt vs Skaffold: which tool fits your team?

Both solve Local Kubernetes Dev with Tilt and Skaffold as the headline workflow. They differ in config style, UI, and CI story. Neither replaces a production GitOps controller such as Argo CD.

CriterionSkaffoldTilt
Config formatskaffold.yaml (YAML)Tiltfile (Starlark/Python-like)
Developer UITerminal output; optional IDEsBuilt-in web dashboard at :10350
Deploy targetskubectl, Kustomize, Helm, Cloud Runkubectl, Helm, custom scripts
Live syncmanual sync rules in YAMLlive_update blocks in Tiltfile
CI reuseStrong: skaffold run / renderPossible; often dev-focused
Learning curveLower if team knows YAMLSteeper; pays off with many services
Best forYAML-first teams, GKE pipelinesPolyglot monorepos, fast feedback

Pick Skaffold when your platform team owns skaffold.yaml and wants identical render steps in GitLab CI. Pick Tilt when developers juggle many interdependent services and need visible build graphs. Running both in one repo is rare and usually a migration artifact, not a goal.

Choose Tilt or SkaffoldHow many services?1–3 services4+ servicesSkaffoldYAML configCI parityTiltLive UIDependency graph
Decision tree for Local Kubernetes Dev with Tilt and Skaffold based on service count and team workflow.

What are common gotchas when running apps locally on Kubernetes?

Local clusters expose problems that Compose hides. Treat these as normal learning steps, not signs the tooling failed.

Image pull and load failures

kind and Minikube often require push: false and local image load. Skaffold handles this when build.local.push is false. Tilt loads via docker_build defaults. If you see ImagePullBackOff, the tag in the Deployment does not match the built image name.

Probe timing during boot

Laravel containers need time for migrations and cache warmup. A readiness probe hitting / too early causes endless restarts. Tune initialDelaySeconds and align with resource limits and requests so PHP-FPM is not OOMKilled during composer install.

Volume permissions on Linux

Host-mounted volumes with www-data UID mismatches break file writes. On Ubuntu dev machines I set fsGroup in the Pod securityContext. This matches fixes I apply on production nodes during Linux server administration engagements.

Database seed data and migrations

Run migrations as a Kubernetes Job or an init container. Do not rely on artisan migrate inside the web container startup script without idempotency guards. Duplicate migrate calls from HPA scale events corrupt schema state.

Local K8s GotchasBefore (broken)ImagePullBackOffCrashLoop probe failVolume UID mismatchAfter (fixed)Local image loadTuned readiness probefsGroup securityContextDebug commandskubectl describe pod / kubectl logs -fSee CrashLoopBackOff field guide on the blog
Typical local Kubernetes Dev with Tilt and Skaffold failures and the configuration fixes that resolve them.

When Pods still fail after config fixes, follow the steps in debug a CrashLoopBackOff and the broader Kubernetes troubleshooting field guide.

Bridging local dev to production pipelines

Local Kubernetes dev should feed the same manifests GitOps applies. A workable path looks like this:

  1. Develop with Tilt or Skaffold against k8s/overlays/local.
  2. Run skaffold render or kubectl kustomize build for staging output.
  3. Commit rendered manifests or Kustomize bases to Git.
  4. Let Argo CD or your CI pipeline deploy to staging clusters.
  5. Keep Compose or Sail for quick unit-test loops if the team prefers both.

On a Laravel eCommerce project like Quick And Easy Nepalese Grocery, delivery-zone logic lived in PHP. Running it behind real Ingress locally caught path-prefix bugs before QA. That saved a redeploy cycle staging would have paid for.

For API-heavy platforms, the same workflow pairs with API development practices: versioned OpenAPI specs, contract tests, and rate-limit headers enforced at the Ingress layer.

Teams building custom booking engines often ask whether local Kubernetes is worth the RAM cost. If you deploy to Kubernetes in production, the answer is yes. Compose cannot simulate pod anti-affinity, NetworkPolicy, or horizontal scaling behaviour. The hour spent on kind plus Skaffold pays back the first time a probe misconfiguration is caught locally.

Hardware matters on Nepal dev laptops with 8 GB RAM. Close browser tabs, cap cluster workers at one node, and run only the services you are editing. Use profiles to disable heavy dependencies like Elasticsearch until needed.

Compare this approach with Laravel Sail vs Docker Compose when pitching the stack to stakeholders. Sail stays cheaper for solo feature work. Tilt or Skaffold enters when staging mirrors production topology.

Enterprise clients evaluating microservices should read enterprise application development scope docs before mandating Kubernetes everywhere. A monolith on managed PHP hosting may outship a premature cluster.

The official Kubernetes documentation remains the reference for API objects you deploy through either tool. Neither Tilt nor Skaffold abstracts away understanding Deployments and Services.

Key Takeaways

  • Local Kubernetes Dev with Tilt and Skaffold runs real manifests on kind or Minikube instead of compose-only approximations.
  • Skaffold fits YAML-centric teams that want skaffold render output in CI; Tilt fits multi-service repos needing a live dependency dashboard.
  • Configure live sync for PHP, Blade, and route files to avoid full image rebuilds on every save.
  • Fix ImagePullBackOff, probe timing, and volume UID issues early—they dominate first-week setup time.
  • Point local overlays at the same Kustomize or Helm bases GitOps deploys to staging and production.
  • Keep Sail or Compose for fast unit tests; use Kubernetes locally when ingress, scaling, or policies matter.

People Also Ask

Do I need Kubernetes locally if I already use Docker Compose?

Not for every project. Compose is enough when production also runs Compose or plain VMs. You need local Kubernetes when production runs Kubernetes and you must test Ingress, Secrets, probes, or NetworkPolicy before merge.

Can Tilt and Skaffold work with kind on Apple Silicon and Linux?

Yes. kind supports arm64 and amd64. Build images for the node architecture or use buildx with the correct platform flag. Skaffold and Tilt both delegate to Docker or BuildKit for builds.

How much RAM does a local Kubernetes dev environment need?

Budget 4 GB for the cluster plus 2 GB for Docker builds on a three-service app. Add 1–2 GB per extra database or message broker. Close unused services through Skaffold profiles or commented k8s_resource blocks in Tilt.

Does local Kubernetes replace staging?

No. Local dev catches manifest and probe mistakes early. Staging still validates shared databases, TLS certificates, external webhooks, and load at scale. Treat local Kubernetes as the first gate, not the last.

Ship with confidence from laptop to cluster

Local Kubernetes Dev with Tilt and Skaffold turns manifest guesswork into a repeatable inner loop. Start with kind, add Skaffold or Tilt to match your team’s config style, and wire overlays into the same Git paths your pipeline already uses. If you want help designing that path for a Laravel, API, or booking platform, see the Adventure Third Pole Trek case study and reach out via contact us for architecture review. For broader context on my stack choices, visit about me or browse related posts on the blog.

Frequently Asked Questions

It means running your app inside a real local cluster—kind, Minikube, k3d, or Docker Desktop Kubernetes—using the same Deployments, Services, and Secrets you ship to staging. Tilt and Skaffold watch your source tree, rebuild images, sync or redeploy, and stream logs so you skip the manual kubectl apply cycle after every save.

Not always. Compose is enough when production also runs Compose or plain VMs. Use local Kubernetes when production runs Kubernetes and you must test Ingress paths, Secrets, probe timing, NetworkPolicy, or multi-replica behaviour before merge.

Budget at least 4 GB for the cluster plus 2 GB for Docker builds on a three-service app. Add 1–2 GB per extra database or message broker. On 8 GB Nepal dev laptops, cap workers to one node and disable heavy dependencies via Skaffold profiles or commented Tilt resources.

Install kind, create a cluster with ingress-ready node labels and host port mappings for HTTP on 8080, then apply the ingress-nginx kind manifest and wait until the controller pod is Ready. Run kubectl get nodes and confirm Ready status before starting Skaffold or Tilt—a broken kubectl context wastes an hour on cryptic build errors.

Add a skaffold.yaml at the repo root with build.local.push set to false, artifact images for web and queue services, manual sync rules for app PHP files, routes, and Blade views, kubectl deploy manifests under k8s/base, and portForward mapping the web Service port 80 to local 8080. Start the loop with skaffold dev --port-forward. Skaffold rebuilds when Docker layers change and syncs PHP without a full rebuild when sync rules match.

Write a Tiltfile in Starlark: allow_k8s_contexts for your kind context, docker_build entries with live_update sync blocks for app, routes, and views, k8s_yaml for web, queue, and Redis manifests, and k8s_resource with port_forwards and resource_deps so web waits for Redis. Run tilt up to build, load images into kind, apply manifests, and open the dashboard at localhost:10350.

Pick Skaffold when your platform team owns YAML config and wants skaffold render or skaffold run to match GitLab CI and GKE pipelines—lower learning curve if the team already knows YAML. Pick Tilt when developers juggle five or more interdependent services and need the built-in web UI showing build graphs, logs, and failing resources. Neither replaces Argo CD or another production GitOps controller. Running both in one repo is usually a migration artifact, not a goal.

kind and Minikube often require images built locally without pushing to a registry. Skaffold needs build.local.push set to false; Tilt loads via docker_build defaults. ImagePullBackOff almost always means the image tag referenced in your Deployment does not match the name and tag the dev tool actually built and loaded into the cluster.

Readiness probes hitting / too early are the usual culprit. Laravel containers need time for migrations, Composer dependencies, and cache warmup. Tune initialDelaySeconds on readiness probes and set resource requests and limits so PHP-FPM is not OOMKilled during composer install. Align probe timing with how long your container actually needs before it can serve traffic.

Skaffold uses manual sync rules in skaffold.yaml mapping src paths like app/*.php and resources/views to container destinations. Tilt uses live_update sync blocks in the Tiltfile, optionally paired with run commands such as php artisan view:clear when views change. Both approaches mirror Docker Compose hot-reload feel while deploying against real Kubernetes Deployments.

Yes. kind supports both arm64 and amd64. Build images for your node architecture or use buildx with the correct platform flag. Skaffold and Tilt both delegate builds to Docker or BuildKit, so the cluster driver choice does not lock you to one operating system.

No. Local dev catches manifest mismatches, Ingress path-prefix bugs, and probe misconfiguration before merge. Staging still validates shared databases, TLS certificates, external webhooks, and load at scale. Treat local Kubernetes as the first gate in your pipeline, not the last one before production.

Profiles switch build and deploy behaviour by activation rules—for example, a local profile activated during skaffold dev deploys k8s/overlays/local manifests, while a ci profile activated when CI=true pushes images and deploys via Helm. Use profiles when staging runs Helm but local dev uses plain YAML, or when you need to disable heavy dependencies like Elasticsearch until a developer actually needs them.

Host-mounted volumes with www-data UID mismatches break file writes inside PHP containers. On Ubuntu dev machines, set fsGroup in the Pod securityContext so the container process can write to mounted directories. This same class of fix applies on production Linux nodes and is one of the first-week gotchas that dominates setup time alongside ImagePullBackOff and probe timing.

Develop against k8s/overlays/local using Tilt or Skaffold, then run skaffold render or kubectl kustomize build to produce staging output. Commit rendered manifests or Kustomize bases to Git and let Argo CD or your CI pipeline deploy to staging clusters. Keep Laravel Sail or Compose for fast unit-test loops if the team prefers both workflows side by side.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: