A namespace with a dozen manifests is easy to deploy and hard to explain. The YAML tells the API server everything it needs, but it does not tell a new teammate which Service fronts which Deployment, or why the checkout API talks to payments. This guide is a repeatable method for going from Kubernetes YAML to a diagram by hand: inventory the objects, read the relationships out of specific fields, decide what to leave out, lay it out, and keep it current. At the end we rebuild the result in Archboard with the Kubernetes pack.
There is no magic here: you read fields and draw edges. That is the point. The diagram should capture intent, and intent is something you add.
Step 1: Inventory the objects
Start with the rendered manifests, not the templates. If you use Helm or Kustomize, render first so you see what actually gets applied:
helm template shop ./charts/shop -f values-prod.yaml > rendered.yaml
kubectl kustomize overlays/prod > rendered.yaml
If the workload is already running, ask the cluster and be explicit about kinds:
kubectl get deploy,svc,ing,cm,secret,pvc,hpa,networkpolicy -n shop
Do not rely on kubectl get all. The all category covers Pods, Services, Deployments, ReplicaSets, StatefulSets, DaemonSets, Jobs, CronJobs and HPAs, but it skips Ingress, ConfigMap, Secret, PVC, NetworkPolicy and ServiceAccount. Several of those carry exactly the edges you want to draw.
For the objects that hold relationships, pull the full spec:
kubectl get ingress -n shop -o yaml
kubectl get svc -n shop -o custom-columns=NAME:.metadata.name,SELECTOR:.spec.selector
kubectl get pods -n shop --show-labels
Write the inventory down as a flat list: kind, name, namespace. That list is your checklist for the drawing.
Example: Kubernetes YAML to diagram for one service
Here is a small but typical set for one service in the shop namespace:
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout-api
namespace: shop
spec:
selector:
matchLabels:
app: checkout-api
template:
metadata:
labels:
app: checkout-api
tier: backend
spec:
containers:
- name: api
image: registry.example.com/shop/checkout-api:1.14.2
ports:
- containerPort: 8080
resources:
requests:
cpu: 250m
envFrom:
- configMapRef:
name: checkout-config
- secretRef:
name: checkout-db-credentials
---
apiVersion: v1
kind: Service
metadata:
name: checkout-api
namespace: shop
spec:
selector:
app: checkout-api
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
namespace: shop
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /api/checkout
pathType: Prefix
backend:
service:
name: checkout-api
port:
number: 80
---
apiVersion: v1
kind: ConfigMap
metadata:
name: checkout-config
namespace: shop
data:
PAYMENTS_URL: http://payments.shop.svc.cluster.local
LOG_LEVEL: info
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: checkout-api
namespace: shop
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: checkout-api
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Five objects, one Secret referenced but not shown, and one dependency hiding in a config value.
Step 2: Find relationships in the fields
Kubernetes objects rarely point at each other by ID. They point by name or, more often, by label. Labels and selectors are the glue; the Kubernetes labels and selectors docs are worth a re-read before you start.
Walk the list in traffic order:
- Ingress → Service.
backend.service.nameandport.numberin each path name the Service directly. Host and path become the edge label. - Service → Deployment. A Service never names a Deployment. Its
spec.selectormatches the labels in the Deployment'sspec.template.metadata.labels. Hereapp: checkout-apimatches, so draw the edge. If you are unsure,kubectl get endpointslices -n shopshows the Pod addresses the Service actually selected; no endpoints means the selector matches nothing, or the matching Pods have no IP yet. - Deployment → ConfigMap / Secret / PVC. Look at
envFrom,env[].valueFromandvolumes[](configMap,secret,persistentVolumeClaim.claimName). - HPA → Deployment.
spec.scaleTargetRefnames the target. - NetworkPolicy.
spec.podSelectorpicks the Pods it protects;ingress[].fromsays who may talk to them andegress[].towhere they may connect. - Hidden dependencies.
PAYMENTS_URLpoints atpayments.shop.svc.cluster.local. No field links checkout to payments, but the call is real. This is the edge that tools miss and humans need most.
YAML field to diagram element
| YAML field | Diagram element |
|---|---|
metadata.namespace |
Namespace frame around the objects |
kind: Deployment |
Deployment node, replicas as a label |
Ingress rules[].host + paths[].path |
Ingress node, host/path on the outgoing edge |
Ingress backend.service.name |
Edge Ingress → Service |
Service selector ↔ template labels |
Edge Service → Deployment |
Service port → targetPort |
Edge label, e.g. 80 → 8080 |
envFrom / volumes[].configMap |
Edge ConfigMap → Deployment |
secretRef / volumes[].secret |
Edge Secret → Deployment |
persistentVolumeClaim.claimName |
Edge PVC → Deployment |
HPA scaleTargetRef |
HPA node next to the Deployment, 3–10 label |
NetworkPolicy podSelector, ingress[].from |
NetworkPolicy node beside the workload it guards |
| Service DNS names in config values | Edge to the dependent Service |
Step 3: Decide what to leave out
A diagram that shows everything shows nothing. Rules that hold up well:
- Replica counts are labels, not boxes. Draw one Deployment node and write
3–10 replicas (HPA)on it. Three identical Pod boxes add noise and are wrong as soon as the HPA scales. - Hide ReplicaSets. They are an implementation detail of rollouts.
- Draw Pods only when they matter, for example standalone Pods, or when you explain scheduling onto Nodes.
- Collapse boilerplate. ServiceAccounts, default RBAC and image pull Secrets usually belong in a note, not on the canvas.
- Keep what explains a decision. If a NetworkPolicy is the reason checkout cannot reach the database directly, it stays.
Step 4: Lay it out
Put north-south traffic left to right: clients → Ingress → Service → Deployment → downstream Services and data stores. Configuration (ConfigMap, Secret) goes below the workload it feeds; scaling (HPA) goes above it. Draw each namespace as a frame, so cross-namespace calls stand out as edges that leave a frame. Label edges with protocol and port, not just arrows.
Automated visualizers vs a drawn diagram
Some tools in the ecosystem read live cluster state from the API server and render it as a graph. They are accurate about what is running right now and useful when debugging. The trade-off: they show every ReplicaSet, Pod and endpoint, they cannot see a dependency that lives in an environment variable, and they carry no intent, such as why something exists or what is planned. A drawn diagram is less complete and more useful for onboarding, design reviews and incident context. Many teams use both.
Step 5: Keep it current
A diagram is only trustworthy if it changes with the manifests:
- Store the diagram file next to the manifests and review it in the same pull request.
- Add a line to the PR template: "Changes an Ingress, Service selector, HPA or NetworkPolicy? Update the diagram."
- Re-run the inventory commands from step 1 during periodic reviews and compare with the drawing.
Rebuilding the example in Archboard
In Archboard you draw from the inventory you just built, deciding what belongs on the diagram, with nodes that match Kubernetes kinds:
- Sign in with Google or GitHub and create a diagram.
- From the Kubernetes pack, add a Namespace. It is drawn as a frame; name it
shop. - Outside the frame on the left, add a Core Box or Text for clients.
- Inside the frame, place Ingress, Service and Deployment left to right. Connect their ports and label the edges
shop.example.com/api/checkoutand80 → 8080. - Add ConfigMap and Secret below the Deployment and connect them. Add an HPA above it labeled
3–10, CPU 70%. - Add a second Service for
paymentsand connect it from the Deployment, so the hidden dependency is visible. - Put a Markdown block on the canvas with the key YAML snippet in a fenced code block, or a short "why" note.
- Export the diagram as JSON and commit it beside the manifests; export SVG or PNG for the README.
Diagram contents
- shop
- Clients
- checkout-api
- checkout-config
- checkout-db-credentials
- payments
- HTTPS
- shop.example.com/api/checkout
- 80 → 8080
- PAYMENTS_URL
- CPU 70%
- envFrom
This fits within the Free plan's 15 diagram nodes per diagram; notes and text count separately, up to 150. For reference layouts, open /examples/kubernetes-ingress-flow and /examples/kubernetes-microservices; Use this example copies either one into your workspace, as Open in Archboard does for the diagram above. More patterns are on /use-cases/kubernetes.
FAQ
Can Archboard import my Kubernetes YAML?
Yes, as a starting point. Paste rendered manifests or kubectl get -o yaml output into the free Kubernetes YAML to diagram tool: it draws namespaces as frames and connects Ingress, Services, workloads, ConfigMaps, Secrets and volumes as the YAML defines them. Viewing needs no account; with a free account you open the result in Archboard as a new diagram, or use Import from YAML… in the editor to add it to an existing one. It does not stay in sync with your YAML, so the steps in this post still apply for checking the result and keeping it current.
Should I draw every Pod? Usually not. One Deployment node with a replica label is clearer and stays correct as the HPA scales.
What about Helm charts?
Render them with helm template first and diagram the output, not the templates.
How do I find dependencies that are not in any field?
Search ConfigMaps and env values for svc.cluster.local and other hostnames, then confirm with the service owners.