Skaffold’s port-forwarding feature makes local development against Kubernetes services feel like you’re running them directly on your machine.

Let’s say you’re developing a microservice frontend that talks to a backend service, both running in Kubernetes. Normally, to test frontend against backend, you’d need to expose backend via a Kubernetes Service of type LoadBalancer or NodePort, or set up an ingress. This adds latency and complexity to your inner dev loop.

Skaffold changes this. When you run skaffold dev, it can automatically forward ports from your local machine to ports on pods running in Kubernetes.

Here’s a typical skaffold.yaml setup for this:

apiVersion: skaffold/v4beta8
kind: Config
metadata:
  name: my-dev-env
build:
  artifacts:
    frontend:
      docker:
        dockerfile: Dockerfile.frontend
  local:
    push: false
deploy:
  kubectl:
    manifests:
      - k8s/frontend.yaml
      - k8s/backend.yaml
profiles:
  - name: dev
    portForward:
      - resourceType: service
        serviceName: backend
        port: 5432
        localPort: 5432
      - resourceType: pod
        deploymentName: frontend-deployment # If your frontend is a deployment
        port: 3000
        localPort: 3000

With this configuration, when you run skaffold dev -p dev, Skaffold will:

  1. Build and deploy your frontend and backend services.
  2. For the backend service, it will find a pod associated with that service and forward local port 5432 to the port 5432 inside the backend pod.
  3. For the frontend deployment, it will find a pod belonging to that deployment and forward local port 3000 to the port 3000 inside the frontend pod.

Now, your local frontend development process (if you’re running it locally, or if you’ve configured frontend itself to be port-forwarded) can connect to localhost:5432, and Skaffold transparently routes that traffic to the backend service’s port 5432 within your Kubernetes cluster.

The magic is that Skaffold establishes these tunnels after your application has deployed. It watches for the pods to become ready and then sets up the forwarding. If a pod restarts, Skaffold automatically re-establishes the connection.

Let’s see this in action. Imagine you have a backend service running in Kubernetes, listening on port 8080. Your frontend application, running locally, needs to talk to this backend.

First, ensure your skaffold.yaml has the port-forwarding rule:

# ... other skaffold config ...
deploy:
  kubectl:
    manifests:
      - k8s/backend.yaml
profiles:
  - name: dev
    portForward:
      - resourceType: service
        serviceName: backend-svc # Name of your Kubernetes Service
        port: 8080                # Port the service listens on IN the pod
        localPort: 8080           # Port you'll access on your local machine

And your k8s/backend.yaml defines the service:

apiVersion: v1
kind: Service
metadata:
  name: backend-svc
spec:
  selector:
    app: backend
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080

Now, run skaffold dev -p dev. Skaffold will deploy backend-svc. Once the backend pods are running, Skaffold will output something like:

...
[backend-svc] Port forwarding (localhost:8080 -> backend-svc:8080)
...

Your local frontend application can now make requests to http://localhost:8080, and Skaffold handles sending those requests to the backend-svc within your cluster.

The resourceType can be service or pod. When you specify service, Skaffold finds a pod that the service selects and forwards to that pod’s port. When you specify pod, you often need to provide a podSelector or deploymentName to pinpoint which pod to forward to. Using service is generally more robust as Skaffold will adapt if the underlying pod changes.

The port is the port inside the Kubernetes pod that your application is listening on. The localPort is the port on your local machine that you will use to access the service. They can be the same, or different if the local port is already in use.

The real power comes when you pair this with live-reload. If you change your backend code and Skaffold rebuilds and redeploys it, the port-forwarding automatically reconnects without you needing to do anything. Your local frontend development loop remains uninterrupted.

It’s crucial to understand that Skaffold establishes a bidirectional tunnel. Not only can your local machine connect to the remote service, but if your remote application needed to initiate a connection back to a service exposed on your local machine (less common, but possible), Skaffold could facilitate that too. This is achieved by Skaffold running a small agent in the pod that listens for connections on the specified port and tunnels them back to your localPort.

Skaffold also handles the lifecycle of these forwards. If you stop skaffold dev, all port-forwarding connections are cleanly closed. If a pod dies and is replaced, Skaffold detects the new pod and re-establishes the forward.

You can also define multiple port-forwarding rules in your skaffold.yaml to expose several services or pods simultaneously. This is incredibly useful for complex microservice architectures where your local client needs to interact with multiple backend dependencies running in Kubernetes.

The common pitfall is mistaking the port (inside the pod) for the targetPort in a Kubernetes Service definition. Skaffold forwards to the port the application is listening on within the pod, which is typically what targetPort in the Service definition points to. If your Service has port: 80 and targetPort: 8080, you should configure Skaffold to forward to 8080.

The next thing you’ll likely encounter is managing port conflicts if multiple Skaffold processes or other local applications try to bind to the same localPort.

Want structured learning?

Take the full Skaffold course →