DEV Community

Timevolt
Timevolt

Posted on

Kubernetes: Taking the Red Pill from Local to Production

The Quest Begins (The "Why")

Honestly, I remember the first time I tried to run a micro‑service on my laptop and then wondered, “How the heck do I get this thing running in the cloud without rewriting everything?” I had a simple Node.js API, a Dockerfile that built fine locally, and a dream of scaling to thousands of users. When I pushed the image to a managed cluster and watched the pods crash because they couldn’t find their config, I felt like Neo staring at the blinking cursor—what is the matrix?

That moment was my dragon: the gap between docker compose up on my machine and a resilient, production‑grade deployment. I needed a way to describe what I wanted to run, not how to start it manually each time. Enter Kubernetes, the orchestrator that promised to turn my local chaos into repeatable, self‑healing magic.

The Revelation (The Insight)

The biggest “aha!” for me was realizing Kubernetes isn’t about memorizing a ton of YAML; it’s about declaring the desired state of your system and letting the control plane figure out the rest. Think of it like giving a GPS a destination instead of turn‑by‑turn instructions—you tell it “I want three replicas of this app, exposed on port 8080, with this config map,” and Kubernetes does the heavy lifting to make it happen, rescheduling pods if a node dies, rolling out updates safely, and scaling when traffic spikes.

Once I grasped that shift—from imperative scripts to declarative manifests—everything clicked. The same files that worked on my laptop could be applied to a dev cluster, a staging environment, and finally production, with only a few environment‑specific tweaks.

Wielding the Power (Code & Examples)

Before: The Local Struggle

Here’s the Dockerfile I used for my API (nothing fancy):

# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Enter fullscreen mode Exit fullscreen mode

And a simple docker-compose.yml for local dev:

version: "3.8"
services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DB_HOST=localhost
Enter fullscreen mode Exit fullscreen mode

Running docker compose up worked great on my laptop, but moving to a cluster meant I had to manually kubectl run, kubectl expose, and keep track of config maps and secrets—tedious and error‑prone.

After: Declarative Kubernetes Manifests

I turned those same ideas into three Kubernetes objects: a Deployment, a Service, and a ConfigMap. The best part? I can keep them in Git and apply them with a single kubectl apply -f k8s/.

k8s/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
  labels:
    app: api
spec:
  replicas: 3                     # <-- desired number of pods
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: myrepo/api:latest   # <-- image built locally or pushed from CI
          ports:
            - containerPort: 3000
          envFrom:
            - configMapRef:
                name: api-config   name: api-config
          env:
            - name: NODE_ENV
              valueFrom:
                secretKeyRef:
                  name: api-secrets
                  key: NODE_ENV
          resources:
            requests:
              memory: "64Mi"
              cpu: "250m"
            limits:
              memory: "128Mi"
              cpu: "500m"
Enter fullscreen mode Exit fullscreen mode

k8s/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: api-service
spec:
  selector:
    app: api
  ports:
    - protocol: TCP
      port: 80          # exposed inside the cluster
      targetPort: 3000  # forwards to containerPort
  type: ClusterIP
Enter fullscreen mode Exit fullscreen mode

k8s/configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
data:
  DB_HOST: "db-service"
  LOG_LEVEL: "info"
Enter fullscreen mode Exit fullscreen mode

k8s/secret.yaml (sealed with kubectl create secret or a secret management tool)

apiVersion: v1
kind: Secret
metadata:
  name: api-secrets
type: Opaque
stringData:
  NODE_ENV: "production"
  DB_PASSWORD: "super-secret"
Enter fullscreen mode Exit fullscreen mode

Applying the Stack

# Build and push your image (example with Docker)
docker build -t myrepo/api:$(git rev-parse --short HEAD) .
docker push myrepo/api:$(git rev-parse --short HEAD)

# Apply all manifests
kubectl apply -f k8s/
Enter fullscreen mode Exit fullscreen mode

Watch the magic:

kubectl get deployments
kubectl get pods -w   # see pods spin up, stay healthy, and reschedule if a node fails
Enter fullscreen mode Exit fullscreen mode

Traps to Avoid (The “Boss Fights”)

  1. Hard‑coding image tags – If you always deploy latest, you can’t roll back cleanly. Tag your images with a Git SHA or version number and update the Deployment manifest accordingly.
  2. ** forgetting resource requests/limits** – Without them, a pod can hog a node and starve neighbors. Start modest, monitor with kubectl top pods, then adjust.

Why This New Power Matters

Now I can push a single commit, have CI build and push the image, and let Argo CD or Flux sync the manifests to any cluster—dev, staging, prod—without touching a shell. Scaling to handle a traffic spike is as easy as editing replicas: 5 and running kubectl apply -f k8s/deployment.yaml. Rolling back a bad release? Just revert the image tag and reapply.

The biggest win is confidence. I no longer fear “it works on my machine” because the machine is now the cluster itself, and the cluster enforces the same rules everywhere. It feels like I’ve leveled up from a solo adventurer to a commander of a fleet, with the orchestration handling the grunt work while I focus on shipping features.

If you’ve ever felt stuck copying Docker commands between environments, give Kubernetes a shot. Start small—maybe just a Deployment and a Service for your API—then gradually add ConfigMaps, Secrets, and Ingress. The learning curve is real, but each step feels like unlocking a new ability in an RPG.

Your challenge: Take one of your existing docker‑compose services, write a Deployment manifest for it, and deploy it to a local kind cluster (kind create cluster). Share what surprised you the most in the comments—did the self‑healing pod restart feel like a boss‑fight victory?

Happy clustering, and may your pods always be in the desired state! 🚀

Top comments (0)