# AI Agents on Kubernetes 101: From Laptop Script to Production Pod

> Source: <https://www.tigera.io/blog/ai-agents-on-kubernetes-101-from-laptop-script-to-production-pod/>
> Published: 2026-09-08 20:16:27+00:00

In short, this is a beginner’s guide to deploying an AI agent on Kubernetes. You will containerize an agent, store its API key as a Kubernetes secret, write a deployment with health probes and resource limits, expose it with a service, and lock down its network egress, in that order, with a working manifest at every step. On a local kind cluster the whole walkthrough takes about an hour. At the end: the six mistakes almost every first agent deployment makes, and the questions a 101 deployment leaves open.

Every AI agent starts life the same way; a Python script on someone’s laptop, an API key in a `.env` file, a `while` loop around an LLM call. It works, it demos well. Then someone with a budget says “ship it,” and you, the engineer closest to the script, get to figure out what shipping an agent actually means.

This guide is that path, walked slowly. It assumes you know what a container is and have met `kubectl a` t least once, and it assumes nothing about agents. By the end you will have an agent running in a cluster with its key in a Secret, its resource usage capped, its health checked, and its network access reduced to the short list of places it has any business calling. That is still short of production-grade governance, but it is a deployment you could defend in a code review, which is more than most agents get.

## Why is Kubernetes a good place to run AI agents?

The short answer is, because an AI agent is a workload that needs supervision, and Kubernetes is the most widely used system for supervising workloads. It restarts the agent when it crashes, caps how much CPU and memory it can consume, injects its credentials (e.g. API keys) at runtime instead of baking them into the code, and describes all of it in version-controlled YAML manifest your team can review. Just as important, Kubernetes gives you control over the agent’s environment, such as, what it can reach on the network, what identity it carries, what happens when it misbehaves. That matters more for agents than for ordinary services, because an agent’s behavior is decided at runtime by a model, so the rules have to live outside the agent, where no prompt can talk its way past them. That argument deserves more than a paragraph, so we gave it a full post: [The Safest Place to Run an AI Agent Is a Cluster That Doesn’t Trust It](https://www.tigera.io/blog/the-safest-place-to-run-an-ai-agent-is-on-a-cluster-that-doesnt-trust-it/) makes the case with a year of real incidents as evidence.

## How is deploying an agent different from deploying a web service?

Mechanically, it barely is. An agent is a long-running process that speaks HTTP; Kubernetes has been running those for a decade. The difference is in what the workload does with its freedom:

- **A web service follows its code.** An agent follows a model. You can read a service’s code and know its behavior. An agent’s next action depends on whatever lands in its context window.
- **An agent holds credentials and acts.** It calls APIs, queries databases, sends messages. A wrong answer is a bug; a wrong *action* is an incident.
- **Every input is potentially an instruction.** Prompt injection is unsolved. Text the agent reads, from a user, a document, or another agent, can try to redirect it.

So the deployment steps below are the same ones you would use for any service. The difference is emphasis: the steps most tutorials treat as optional hardening (secrets, limits, egress control) are, for agents, the point.

## What you need before you start

Four things, all free:

1. **A cluster** . Locally,[kind](https://kind.sigs.k8s.io/) runs Kubernetes inside Docker:`kind create cluster --name agents` . Any managed cluster (EKS, GKE, AKS) works the same way.
2. **kubectl and Docker** , installed and talking to that cluster.
3. **An agent that speaks HTTP** . Any framework is fine. LangGraph, CrewAI, Google ADK, etc all wrap into a web server; we compared them in[Six AI Agent SDKs for Enterprise Kubernetes](https://www.tigera.io/blog/six-ai-agent-sdks-for-enterprise-kubernetes-compared/) . The walkthrough uses a generic Python agent served by FastAPI on port 8080, with two routes:`POST /chat` for work and`GET /healthz` that returns 200 when the process is up. If your agent lacks a health route, add one first. It is five lines, and step 4 depends on it.
4. **An LLM API key.** From whichever provider your agent calls.

One namespace keeps the experiment contained:

```
kubectl create namespace agents
```

## Step 1: Containerize the agent

Kubernetes runs containers, so the script becomes an image. A minimal Dockerfile for a Python agent:

```
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
USER 1000
EXPOSE 8080
# change the command (to run agent) according to its framework documentation
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
```

Two lines here are security decisions. `USER 1000` runs the agent as a non-root user, so a compromised agent is not root inside its container. And notice what is *absent*: no API key. The key never enters the image. An image is copied to registries, cached on nodes, and pulled by anyone with access; a key baked into an image is a key you have already leaked, you just don’t know to whom yet.

Build it and, for kind, load it into the cluster:

```
docker build -t support-agent:0.1.0 .
kind load docker-image support-agent:0.1.0 --name agents
```

On a real cluster you would push to a registry instead. Either way, tag with a version (e.g., `0.1.0`), never `latest`. You want to be able to say exactly what is running, and roll back to exactly what was running before.

## Step 2: Put the API key in a Secret

The `.env` file’s job is taken over by a Kubernetes Secret:

```
kubectl -n agents create secret generic support-agent-secrets 
  --from-literal=ANTHROPIC_API_KEY='sk-ant-...'
```

The Secret lives in the cluster, is delivered to the pod as an environment variable at start time, and can be rotated without rebuilding the image. Two honest caveats for later: Secrets are base64-encoded, not encrypted, so real clusters enable encryption at rest and restrict who can read them with RBAC; and the agent’s own process can still read this variable, which matters once you worry about prompt injection. Hold that thought for the end of the article.

## Step 3: Write the Deployment

The Deployment is the contract: which image, how many copies, what resources, what health checks. Save this as `deployment.yaml`:

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: support-agent
  namespace: agents
  labels:
    app: support-agent
spec:
  replicas: 1
  selector:
    matchLabels:
      app: support-agent
  template:
    metadata:
      labels:
        app: support-agent
    spec:
      automountServiceAccountToken: false
      securityContext:
        runAsNonRoot: true
      containers:
        - name: agent
          image: support-agent:0.1.0
          ports:
            - containerPort: 8080
          envFrom:
            - secretRef:
                name: support-agent-secrets
          resources:
            requests:
              cpu: 100m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 512Mi
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 20
          securityContext:
            allowPrivilegeEscalation: false
            capabilities:
              drop: ["ALL"]
```

Apply it with `kubectl apply -f deployment.yaml`, then read it back top to bottom, because every block answers a question a reviewer will ask:

- **`automountServiceAccountToken: false`:** By default every pod gets a token for the Kubernetes API mounted into its filesystem. Your agent doesn’t need to talk to the Kubernetes API, so it doesn’t get the token. For an agent this is not a nicety. It is the difference between “prompt injection stole a chat log” and “prompt injection got a foothold in my cluster’s control plane.”
- **resources** : Requests are what the scheduler reserves; limits are the ceiling. Agents fail in loops. A model that decides to retry forever, or to summarize a document by reading it into memory in one piece, will eat a node if you let it. Limits turn “the cluster is down” into “one pod got throttled.”
- **`readinessProbe` and `livenessProbe`** : Readiness controls whether traffic is sent to the pod; liveness restarts it when the process wedges. Without them, Kubernetes considers a hung agent healthy forever.
- **`envFrom.secretRef`** : The key from step 2 arrives as an environment variable. Code reads`ANTHROPIC_API_KEY` exactly as it did from the`.env` file. Nothing about the agent’s code had to change.

Check it comes up:

```
kubectl -n agents get pods
kubectl -n agents logs deploy/support-agent
```

## Step 4: Expose it with a Service

Pods are ephemeral and their IPs change. A Service gives the agent a stable name:

```
apiVersion: v1
kind: Service
metadata:
  name: support-agent
  namespace: agents
spec:
  selector:
    app: support-agent
  ports:
    - port: 80
      targetPort: 8080
```

Now anything inside the cluster can reach the agent at `http://support-agent.agents.svc`. From your laptop, test through a port-forward:

```
kubectl -n agents port-forward svc/support-agent 8080:80
curl -X POST localhost:8080/chat -H 'Content-Type: application/json' 
  -d '{"message": "hello"}'
```

If you get an answer, you have an AI agent running on Kubernetes. Most tutorials stop here. Do not stop here.

## Step 5: Close the doors it doesn’t need

Right now your agent can open a connection to anything: every pod in the cluster, every address on the internet. For a web service that is untidy. For an agent it is the whole attack surface, because the standard end of a prompt-injection chain is exfiltration: the agent is talked into sending data somewhere it should never call. A NetworkPolicy makes egress deny by default:

```
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: support-agent-egress
  namespace: agents
spec:
  podSelector:
    matchLabels:
      app: support-agent
  policyTypes: ["Egress"]
  egress:
    - to:
        - namespaceSelector: {}
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
    - ports:
        - protocol: TCP
          port: 443
```

This allows DNS lookups plus outbound HTTPS, and nothing else; a connection to another pod, or plaintext HTTP to anywhere, now fails at the network layer (Note for kind users: enforcing NetworkPolicy requires a network plugin that implements it, such as [Calico](https://docs.tigera.io/calico/latest/getting-started/kubernetes/kind); the kind default does not).

The policy still has one hole, and it is an instructive one: “TCP 443 to anywhere” lets the agent reach any HTTPS endpoint on the internet, including an attacker’s. Vanilla NetworkPolicy speaks IPs and ports; it cannot say “only `api.anthropic.com`.” Narrowing egress to named destinations takes either a DNS-aware policy engine (Calico can do this) or, better for agents, an egress gateway that all agent traffic must pass through. Remember this gap. It is where 101 ends and the last section of this article begins.

## The six mistakes every first agent deployment makes

The walkthrough above quietly avoided all six. Here they are in the open, because you will meet them in other people’s manifests:

| **Mistake** | **Why it hurts an agent especially** | **Instead** | 
| API key baked into the image | Images get pulled, cached, and shared; the agent’s key is its power | Kubernetes Secret, injected at runtime | 
| No resource limits | Agents fail in loops; one runaway loop starves the node | Set `requests` and`limits`  | 
| `latest` image tag | You cannot say which agent behavior is running, or roll back | Version tags, ideally digests | 
| No health probes | A wedged agent looks healthy and keeps receiving work | `readinessProbe + livenessProbe` | 
| Default ServiceAccount token mounted | Injected agent inherits a path to the Kubernetes API | `automountServiceAccountToken: false` | 
| Unrestricted egress | Prompt injection ends in exfiltration over open egress | Default-deny NetworkPolicy, then allowlist | 

Read the middle column again. None of these is an exotic agent problem; they are ordinary Kubernetes hygiene. Agents just raise the price of skipping them.

## What a 101 deployment still can’t answer

You now have one agent, contained and supervised. Before you call it production, try to answer three questions about it:

1. **Who is this agent?** Your cluster knows it as a pod with a label. Nothing cryptographically distinguishes it from any other workload, so nothing downstream can grant it permissions*as an agent* , or refuse an impostor.
2. **What exactly is it allowed to do?** Your NetworkPolicy says “HTTPS, somewhere.” It cannot see that the agent is calling an MCP tool, or with what arguments. “May this agent call`delete_records` on the billing server” is not a question any layer you have deployed can even parse. We walked through why the stock building blocks stop short in the[accountability gap post](https://www.tigera.io/blog/the-ai-agent-accountability-gap-why-network-policies-api-gateways-and-rbac-are-not-enough/) .
3. **What did it do last Tuesday?** You have pod logs, written by the agent itself. An agent’s own narration is not an audit trail; the best-documented case of an agent deleting a production database came with the agent confidently reporting that rollback was impossible. It was not.

And this is with one agent. The moment there are two, they start talking to each other (that is the [A2A protocol](https://www.tigera.io/blog/how-ai-agents-communicate-understanding-the-a2a-protocol-for-kubernetes/)), and the questions multiply by every pair.

That missing layer (identity, per-request authorization, and audit across a fleet of agents) is what [Tigera Lynx](https://www.tigera.io/blog/why-we-built-lynx-bringing-control-to-the-age-of-ai-agents/) adds on top of exactly the deployment you just built. Lynx gives every agent a SPIFFE or OIDC workload identity, routes agent-to-agent, MCP, and LLM traffic through a gateway that authorizes each request under Cedar policy, and records every hop in an Agent Trail the agent cannot edit. Recent releases sharpened the credential story from this post’s step 2: provider keys attach at the gateway instead of living in the pod’s environment, per-hop tokens are minted with an audience of one target so a stolen token is nearly worthless, and an OAuth authorization-code flow can issue per-user credentials. Policies can require human approval before a risky MCP call proceeds. On the node, an eBPF detector spots agents nobody registered and can quarantine a compromised one down to blocking its network writes at the kernel, and a policy playground in the UI lets you test a Cedar policy against simulated requests before it ever gates real traffic.

## Frequently asked questions

1. **Do I need a GPU to deploy an AI agent on Kubernetes?** No. An agent is orchestration code that calls a model over an API; the GPUs live with the model provider. You only need GPU nodes if you self-host the model itself, which is a separate project.
2. **Is Kubernetes overkill for a single agent?** For a weekend experiment, yes; run it locally. But the reasons to move to Kubernetes (restarts, secrets, resource caps, network control) show up the first time the agent touches real credentials or real users, which happens earlier than most teams expect.
3. **Can I do all of this on my laptop?** Yes. Everything above runs on a kind cluster in Docker, including the NetworkPolicy if you install a plugin like Calico that enforces it.
4. **Which agent framework works best on Kubernetes?** Any framework that can serve HTTP deploys the same way. The differences show up in observability, state handling, and protocol support; our[six-SDK comparison](https://www.tigera.io/blog/six-ai-agent-sdks-for-enterprise-kubernetes-compared/) covers them.
5. **How do I scale an agent to more replicas?** Set`replicas: 3` and Kubernetes load-balances across them, but only if the agent keeps its conversation state outside the pod (a database or cache), since any replica may serve the next request. Stateless agents scale for free; stateful ones need that refactor first.
6. **How is deploying an agent different from deploying a model?** A model deployment serves inference (weights on GPUs behind an endpoint). An agent deployment runs the loop that calls models and tools to pursue a goal. This guide covers the agent; most teams consume the model as a managed API.

## Ship the pod, then ask the harder question

Back to that laptop script. The distance from `.env` file to the deployment in this article is five manifests and an afternoon, and every step was ordinary Kubernetes, applied with an agent’s failure modes in mind. That is the good news: you do not need new infrastructure to give an agent a safer home than a laptop. You need the infrastructure you already have, used deliberately.

The harder question arrives with agent number two: when they start acting on each other’s behalf, who is checking identity at the door, and where is the record? Deploying the agent was the easy 101. Trusting it is a course Kubernetes alone does not teach.

*Lynx is Tigera’s security and governance platform for AI agents on Kubernetes: identity, policy, detection, and audit for every agent in your cluster. Read [How Lynx Works](https://www.tigera.io/blog/how-lynx-works-a-technical-walkthrough/) or request early access at [tigera.io/demo/](https://www.tigera.io/demo/?product=lynx).*
