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:
- Build and deploy your
frontendandbackendservices. - For the
backendservice, it will find a pod associated with that service and forward local port5432to the port5432inside thebackendpod. - For the
frontenddeployment, it will find a pod belonging to that deployment and forward local port3000to the port3000inside thefrontendpod.
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.