Skaffold can manage resource limits for your development containers, but most people use it completely wrong.

Let’s see Skaffold in action. Imagine a simple Node.js app:

// server.js
const http = require('http');
const port = process.env.PORT || 8080;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World!\n');
});

server.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

And a package.json:

{
  "name": "my-app",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Now, let’s configure Skaffold to build and deploy this. We’ll use a skaffold.yaml:

apiVersion: skaffold.dev/v2beta25
kind: Config
build:
  local:
    push: false
  artifacts:
    - image: my-dev-app
      docker:
        dockerfile: Dockerfile
deploy:
  kubectl:
    manifests:
      - k8s/deployment.yaml
      - k8s/service.yaml
profiles:
  - name: dev-with-resources
    build:
      local:
        push: false
      artifacts:
        - image: my-dev-app
          docker:
            dockerfile: Dockerfile
    deploy:
      kubectl:
        manifests:
          - k8s/deployment.yaml
          - k8s/service.yaml
    # This is where the magic happens for resource limits
    portForward:
      - resourceType: pod
        port: 8080
        localPort: 8080
        containerPort: 8080
        pods:
          - "my-dev-app-*" # Skaffold will find the pod name
    # Skaffold's intended mechanism for dev-time resource management
    # This applies the specified resource limits to the pod definition
    # when deploying for the 'dev-with-resources' profile.
    customResources:
      - name: resource-limits
        sources:
          - k8s/deployment.yaml # Skaffold will modify this file in memory
        patches:
          - path: spec.template.spec.containers[0].resources.limits
            value:
              cpu: "200m"
              memory: "256Mi"
          - path: spec.template.spec.containers[0].resources.requests
            value:
              cpu: "100m"
              memory: "128Mi"

And a Dockerfile:

FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm install
CMD ["npm", "start"]

Finally, a k8s/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-dev-app
spec:
  selector:
    matchLabels:
      app: my-dev-app
  template:
    metadata:
      labels:
        app: my-dev-app
    spec:
      containers:
        - name: my-dev-app
          image: my-dev-app
          ports:
            - containerPort: 8080

And k8s/service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: my-dev-app-service
spec:
  selector:
    app: my-dev-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: LoadBalancer

When you run skaffold dev --profile dev-with-resources, Skaffold will:

  1. Build the my-dev-app image using your Dockerfile.
  2. Crucially, it will take your k8s/deployment.yaml, and in memory, it will patch the spec.template.spec.containers[0].resources.limits and spec.template.spec.containers[0].resources.requests fields with the values defined in customResources. This modified YAML is what gets applied to your Kubernetes cluster.
  3. Deploy the modified application to your cluster.
  4. Set up port-forwarding for port 8080 on the pod to your local machine.

Now, if you hit http://localhost:8080 in your browser, you’ll see "Hello World!".

The mental model here is that Skaffold, when using customResources in a profile, acts as a YAML transformer before applying manifests. It doesn’t have a separate "resource limit" setting; it manipulates your existing Kubernetes resource definitions. This is powerful because it means you’re working with standard Kubernetes resources.limits and resources.requests objects, just like you would in production, but with values tailored for development.

The real power comes from the customResources block. Skaffold uses a JSON patch-like syntax to modify your deployment manifests before sending them to kubectl. This allows you to dynamically inject resource requests and limits into your development deployments without altering your source k8s/deployment.yaml. The path field uses a JSONPath-like expression to pinpoint exactly where in the YAML structure the change should occur.

What most people miss is that Skaffold doesn’t magically set resource limits in a vacuum. It patches your Kubernetes manifests. This means your k8s/deployment.yaml can be identical for dev and prod, with the profile in skaffold.yaml being the differentiator for resource settings. This keeps your Kubernetes manifests clean and source-controlled, while your development experience is optimized.

The next thing you’ll likely want to tackle is managing multiple containers within the same deployment, ensuring each gets its appropriate resource limits.

Want structured learning?

Take the full Skaffold course →