home / skills / laurigates / claude-plugins / skaffold-orbstack

skaffold-orbstack skill

/container-plugin/skills/skaffold-orbstack

This skill helps you configure Skaffold with OrbStack for port-forward-free local Kubernetes workflows, enabling LoadBalancer or Ingress access.

npx playbooks add skill laurigates/claude-plugins --skill skaffold-orbstack

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
8.6 KB
---
model: haiku
created: 2025-12-16
modified: 2026-02-06
reviewed: 2025-12-16
name: skaffold-orbstack
description: |
  OrbStack-optimized Skaffold workflows for local Kubernetes development without port-forward.
  Use when configuring Skaffold with OrbStack, accessing services via LoadBalancer or Ingress,
  or when the user mentions OrbStack, k8s.orb.local, service access, or eliminating port-forward.
allowed-tools: Bash, Read, Grep, Glob
---

# Skaffold with OrbStack - Port-Forward-Free Development

## Overview

OrbStack provides superior local Kubernetes networking compared to other tools (minikube, kind, Docker Desktop). Services are accessible directly from macOS without port-forward.

## Key OrbStack Advantages

| Feature | OrbStack | minikube/kind |
|---------|----------|---------------|
| LoadBalancer auto-provision | ✅ Yes | ❌ Needs MetalLB |
| Wildcard DNS (`*.k8s.orb.local`) | ✅ Yes | ❌ No |
| cluster.local from host | ✅ Yes | ❌ No |
| Pod IP direct access | ✅ Yes | ❌ No |
| Auto HTTPS certificates | ✅ Yes | ❌ No |

## Service Access Methods

### Method 1: LoadBalancer Services (Simplest)

Change service type from ClusterIP to LoadBalancer:

```yaml
apiVersion: v1
kind: Service
metadata:
  name: my-app
spec:
  type: LoadBalancer  # OrbStack auto-provisions external IP
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: my-app
```

**Access**: `curl http://my-app.default.svc.cluster.local` from macOS

### Method 2: Ingress with Wildcard DNS (Recommended)

**One-time setup - Install Ingress controller:**

```bash
# Ingress-NGINX (recommended)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/cloud/deploy.yaml

# OR Traefik
helm repo add traefik https://traefik.github.io/charts
helm install traefik traefik/traefik
```

**Create Ingress for your service:**

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
spec:
  ingressClassName: nginx
  rules:
    - host: my-app.k8s.orb.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app
                port:
                  number: 80
```

**Access**: `http://my-app.k8s.orb.local` (auto-resolves)

### Method 3: Direct Service DNS (cluster.local)

OrbStack exposes cluster DNS to macOS:

```bash
# Access any service directly
curl http://my-app.default.svc.cluster.local:8080

# Full DNS pattern
curl http://<service>.<namespace>.svc.cluster.local:<port>
```

## Skaffold Configuration for OrbStack

### Minimal skaffold.yaml (No Port-Forward Needed)

```yaml
apiVersion: skaffold/v4beta11
kind: Config
metadata:
  name: my-app

build:
  local:
    push: false
    useBuildkit: true
  artifacts:
    - image: my-app
      docker:
        dockerfile: Dockerfile

deploy:
  kubeContext: orbstack
  kubectl:
    manifests:
      - k8s/*.yaml
  statusCheck: true
  statusCheckDeadlineSeconds: 180

# Port-forward REMOVED - use LoadBalancer/Ingress instead
```

### Profile: Local with Ingress

```yaml
profiles:
  - name: local-ingress
    deploy:
      kubeContext: orbstack
      kubectl:
        manifests:
          - k8s/base/*.yaml
          - k8s/ingress/*.yaml  # Ingress resources
```

### Profile: Services-Only (Frontend Local Dev)

```yaml
profiles:
  - name: services-only
    build:
      artifacts: []  # Don't build frontend
    deploy:
      kubeContext: orbstack
      kubectl:
        manifests:
          - k8s/namespace.yaml
          - k8s/database/*.yaml
          - k8s/api/*.yaml
```

Access backend at `http://api.k8s.orb.local` while running `npm run dev` locally.

## Kubernetes Manifest Templates

### LoadBalancer Service Template

```yaml
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ .name }}
  labels:
    app: {{ .name }}
spec:
  type: LoadBalancer
  ports:
    - name: http
      port: 80
      targetPort: {{ .containerPort | default 8080 }}
  selector:
    app: {{ .name }}
```

### Ingress Template

```yaml
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .name }}
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: {{ .name }}.k8s.orb.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: {{ .name }}
                port:
                  number: 80
```

## Migration: Port-Forward to LoadBalancer

### Before (Traditional)

```yaml
# skaffold.yaml with port-forward
portForward:
  - resourceType: service
    resourceName: api
    port: 8080
    localPort: 8080
    address: 127.0.0.1
  - resourceType: service
    resourceName: frontend
    port: 3000
    localPort: 3000
    address: 127.0.0.1
```

```bash
skaffold dev  # Services at localhost:8080, localhost:3000
```

### After (OrbStack Native)

```yaml
# k8s/services.yaml - Change service types
apiVersion: v1
kind: Service
metadata:
  name: api
spec:
  type: LoadBalancer  # Changed from ClusterIP
  ports:
    - port: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  type: LoadBalancer  # Changed from ClusterIP
  ports:
    - port: 3000
```

```yaml
# skaffold.yaml - Remove portForward section entirely
deploy:
  kubeContext: orbstack
  kubectl:
    manifests:
      - k8s/*.yaml
# No portForward needed!
```

```bash
skaffold dev  # Services at api.default.svc.cluster.local:8080
              #            frontend.default.svc.cluster.local:3000
```

## Common Patterns

### Database Access

```yaml
# k8s/postgresql.yaml
apiVersion: v1
kind: Service
metadata:
  name: postgresql
spec:
  type: LoadBalancer  # Access from local tools (DBeaver, pgAdmin)
  ports:
    - port: 5432
```

**Connection string**: `postgres://user:[email protected]:5432/db`  <!-- gitleaks:allow -->

### Multi-Service Application

```yaml
# k8s/ingress.yaml - Single Ingress for all services
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: api.k8s.orb.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api
                port:
                  number: 8080
    - host: web.k8s.orb.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend
                port:
                  number: 3000
    - host: admin.k8s.orb.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: admin-panel
                port:
                  number: 8000
```

## Security Considerations

### Default: Localhost Only

OrbStack restricts services to localhost by default - safe on untrusted networks.

### Expose to LAN (Use with Caution)

Settings → Kubernetes → "Expose services to local network devices"

Only enable when:
- Testing from mobile devices on same network
- Sharing local environment with team
- On trusted network

## Troubleshooting

### Service Not Accessible

1. Check service type: `kubectl get svc`
2. Verify LoadBalancer has EXTERNAL-IP (not `<pending>`)
3. Test DNS: `nslookup my-app.default.svc.cluster.local`

### Ingress Not Working

1. Verify Ingress controller is running:
   ```bash
   kubectl -n ingress-nginx get pods
   ```
2. Check Ingress controller has LoadBalancer IP:
   ```bash
   kubectl -n ingress-nginx get svc
   ```
3. Verify Ingress resource:
   ```bash
   kubectl describe ingress my-app
   ```

### DNS Resolution Issues

```bash
# Test cluster DNS from macOS
nslookup my-service.default.svc.cluster.local

# If short names fail, use full domain
# ❌ my-service.default.svc
# ✅ my-service.default.svc.cluster.local
```

### Pod IP Direct Access (Debugging)

```bash
# Get pod IP
kubectl get pods -o wide

# Connect directly (OrbStack routes pod network to macOS)
curl http://10.42.0.15:8080
```

## Quick Setup Checklist

1. [ ] Install Ingress controller (one-time)
2. [ ] Change service types to LoadBalancer
3. [ ] Create Ingress resources for pretty URLs
4. [ ] Remove `portForward` from skaffold.yaml
5. [ ] Set `kubeContext: orbstack` in deploy config
6. [ ] Update local .env/config to use `.k8s.orb.local` URLs

## Commands Reference

```bash
# Start development (no --port-forward needed)
skaffold dev --kube-context=orbstack

# Run specific profile
skaffold dev -p services-only --kube-context=orbstack

# Check service accessibility
kubectl get svc -o wide

# Verify Ingress
kubectl get ingress
```

Overview

This skill provides OrbStack-optimized Skaffold workflows to run local Kubernetes development on macOS without relying on port-forward. It shows how to replace port-forward with LoadBalancer services and Ingress-backed hostnames (k8s.orb.local), plus ready-to-use skaffold.yaml profiles and manifest templates. Use it to simplify service access, enable TLS, and route traffic from your host to cluster services directly.

How this skill works

The skill inspects typical Skaffold and Kubernetes manifests and recommends changing ClusterIP services to LoadBalancer or adding Ingress resources. It configures Skaffold deploy profiles that target the orbstack kubeContext and removes port-forward entries. It also documents DNS, testing commands, and troubleshooting steps for OrbStack features like wildcard DNS and exposed cluster DNS on macOS.

When to use it

  • Developing on macOS with OrbStack and you want services reachable without port-forward.
  • You need hostnames like my-app.k8s.orb.local or auto-provisioned LoadBalancer IPs.
  • Migrating an existing Skaffold project away from port-forward.
  • Running frontend locally while accessing backend services in-cluster.
  • Testing databases or tools (pgAdmin, DBeaver) against cluster services from your host.

Best practices

  • Change service type to LoadBalancer for services you need accessible from macOS.
  • Install an Ingress controller once and use host rules for friendly URLs (.k8s.orb.local).
  • Remove portForward from skaffold.yaml and set kubeContext: orbstack in deploy blocks.
  • Keep ingress and service manifests in distinct folders and use Skaffold profiles for variants.
  • Use full cluster DNS names (service.namespace.svc.cluster.local:port) when short names fail.

Example use cases

  • Local API development: run frontend locally and point it to api.k8s.orb.local served by in-cluster backend.
  • Database tooling: expose PostgreSQL as LoadBalancer and connect from pgAdmin using postgresql.default.svc.cluster.local:5432.
  • Multi-service app: serve api, web, and admin under separate .k8s.orb.local hostnames via a single Ingress.
  • CI local testing: use a skaffold profile that deploys only services and databases without building frontend images.
  • Debugging: curl pod IPs directly from macOS to inspect behavior without recreating port-forwards.

FAQ

Do I still need port-forward with OrbStack?

No — OrbStack exposes LoadBalancer IPs, wildcard DNS, and cluster DNS to macOS so port-forward is typically unnecessary.

How do I access services from macOS if DNS fails?

Use the full cluster DNS name (service.namespace.svc.cluster.local:port) or curl the LoadBalancer IP reported by kubectl get svc.