Kubernetes & GitOps

Kubernetes that carries real load — on air, on prem, in production.

The clusters I’ve built have carried a live televised sporting event, a Fortune 500 bank’s modernization, billions of distributed ML calculations, and a sub-second real-time event-streaming platform. My working rule is simple: everything reaches the cluster through Git and Flux, and I don’t call a platform done until day-2 is boring — dashboards wired, promotion paths rehearsed, stateful data failover-tested, autoscaling proven under the load it will actually see.

What I Do on Kubernetes

Six disciplines I practice hands-on — each one proven on a cluster that had something to lose.

Platform & Cluster Engineering

I’ve stood up clusters everywhere from bare metal to managed cloud: Talos Linux, Red Hat OpenShift, AWS EKS, Azure AKS, and k3d/k3s locally. My default layer cake is Cilium for CNI, Rook Ceph for storage classes, cert-manager for certificates, and PTP where clocks have to agree. My test: if I can’t rebuild the whole cluster from the repo, it isn’t finished.

GitOps Delivery with Flux

I treat Git as the only interface to the cluster. Flux’s source, kustomize, helm, and image-automation controllers do the applying: new images get noticed in ECR/ACR, tags get committed back to the repo, and drift heals itself on the next reconcile. I stopped letting kubectl apply from a laptop touch anything long ago — and I’ve never missed it.

CI/CD & Environment Promotion

My pipelines build, test, and package OCI images — then they stop. Promotion through dev → staging → production is a Git merge against branch-mapped Kustomize overlays, not a deploy script. That’s why I rarely hear “but it works in dev” anymore: prod is rendered from the identical declarative source.

Observability & Day-2 Ops

I wire Grafana, Prometheus, and Loki/Alloy into every pod before the first real workload lands, because I’ve debugged blind once and won’t do it again. I write the NATS and Postgres runbooks myself, run the failover drills, and hunt down the OOM kills and log-shipping bugs in rehearsal — not while the system is live.

Stateful Workloads & Data

I run the stateful pieces most teams keep off-cluster: high-availability PostgreSQL, NATS JetStream on persistent volumes, Valkey, with Rook Ceph underneath as a real storage class. Everything is Helm-packaged, Flux-deployed, and failover-tested, and Flyway migrates schemas automatically on push — so the database is versioned exactly like the code.

Autoscaling & Distributed Compute

I scale on events, not guesses. KEDA watches queue depth and takes workers from zero to hundreds of pods when a billion-calculation job lands; Ray fans the ML work out across the cluster; replica strategies keep the always-on services highly available. When the queue drains, it all folds back to zero.

16
Game components I deployed to an on-prem cluster for a live broadcast
Sub-second
Event-streaming latency I hold over NATS JetStream
100s of M
ML calculations I spread across EKS with KEDA & Ray
Zero
Deploys done by hand — my clusters follow Git and nothing else

How I Actually Build It

The same paths I sketch on whiteboards — commit to pod, environment to environment, drawn from working systems.

1 · GitOps CI/CD — commit to running pod

When I push, the pipeline builds and tests the image, ships it to the registry, and commits the new tag into the GitOps repo — and then its job is over. Flux sees the commit and reconciles the cluster to match. I built it this way deliberately: CI never holds cluster credentials, because the only thing it can change is Git, and Git is the only thing the cluster listens to.

flowchart LR
    DEV["Developer push"] --> REPO["Git repo -- application source"]
    REPO --> CI["CI pipeline -- build, test, scan"]
    CI --> IMG["OCI image built"]
    IMG --> REG["Container registry -- ECR or ACR"]
    CI --> TAG["Bump image tag in GitOps repo"]
    TAG --> GITOPS["GitOps repo -- Kustomize and Helm manifests"]
    GITOPS --> FLUX["Flux controllers reconcile"]
    REG --> FLUX
    FLUX --> CLUSTER["Kubernetes cluster -- desired state applied"]
    CLUSTER --> OBS["Grafana, Prometheus, Loki -- verify healthy"]
                
CI writes commits; Flux does the applying. The cluster is a follower, never a deploy target.

2 · Branch-to-environment promotion

I map each long-lived branch to a Kustomize overlay and each overlay to a cluster. Merge to develop and dev reconciles itself; cut a release branch and staging follows; tag main and production promotes. Every promotion becomes something I can review, revert, and audit — and I gate each hop on the environment below it being green first.

flowchart LR
    D["develop branch"] --> O1["Kustomize overlay -- dev"] --> C1["Dev cluster"]
    R["release branch"] --> O2["Kustomize overlay -- staging"] --> C2["Staging cluster"]
    M["main branch -- tagged release"] --> O3["Kustomize overlay -- prod"] --> C3["Production cluster"]
    C1 -.->|"promote when green"| C2
    C2 -.->|"promote when green"| C3
                
One set of manifests, one overlay per environment — I promote by merging, never by deploying.

3 · The Flux reconcile loop

Flux is a handful of controllers doing the comparison I used to do by hand: live cluster versus GitOps repo versus registry. Image-automation spots new tags and commits them back to Git; source-controller pulls the repo; the kustomize- and helm-controllers apply it; anything that drifts gets put back on the next reconcile. Once I learned to trust that loop, I stopped touching clusters directly — and stopped getting paged for drift.

flowchart LR
    GIT["GitOps repo -- single source of truth"] --> SRC["source-controller -- pulls repo"]
    REG["Container registry"] --> IAC["image-reflector and image-automation"]
    IAC -->|"write new tag back to Git"| GIT
    SRC --> KUST["kustomize-controller"]
    SRC --> HELM["helm-controller"]
    KUST --> APPLY["Apply manifests to cluster"]
    HELM --> APPLY
    APPLY --> STATE["Cluster reaches desired state"]
    STATE -->|"drift detected on reconcile"| KUST
                
The control loop that replaced my late-night kubectl sessions: reconcile, converge, self-heal.

4 · Distributed ML on AWS EKS — KEDA & Ray

For a Fortune 500 industrial client, I had to spread billions of feature-ranking and forecasting calculations across a cluster. My design: a driver fans work items onto a queue; KEDA scales runner pods off queue depth — all the way down to zero between jobs; Ray parallelizes the LSTM/DeepAR PyTorch models across workers. Results land in PostgreSQL and roll up into the ranked output.

flowchart TD
    USER["Data scientist -- ranking job CLI"] --> DRV["Main driver -- reads config, writes DB"]
    DRV --> Q["RabbitMQ queue -- work items"]
    Q --> KEDA["KEDA scaler -- watches queue depth"]
    subgraph EKS["AWS EKS cluster"]
      KEDA -->|"scale 0 to N"| POD["Target and model runner pods"]
      POD --> RAY["Ray head and workers -- LSTM and DeepAR in PyTorch"]
    end
    POD --> DB[("PostgreSQL -- results")]
    RAY --> DB
    DB --> RANK["Ranked features -- best correlated trends"]
                
KEDA on queue depth plus Ray parallelism — how I get a billion-calculation job to finish.

5 · Platform blueprint — the cluster I bootstrap on Talos

Every cluster I stand up gets the same layers in the same order: immutable Talos Linux nodes, Cilium for networking and policy, Rook Ceph for storage classes, cert-manager and External Secrets for trust and credentials, and Flux on top reconciling the data services, observability, and application workloads. I standardized this stack so I never solve the same bootstrap problem twice — it also sits underneath stealth platform work whose application specifics stay under NDA.

flowchart TD
    OS["Talos Linux -- immutable, API-managed nodes"] --> CNI["Cilium CNI -- networking and policy"]
    CNI --> STORE["Rook Ceph -- storage classes"]
    STORE --> SEC["cert-manager and External Secrets Operator"]
    SEC --> FLUX["Flux -- GitOps reconciliation"]
    FLUX --> DATA["Data services -- PostgreSQL HA, NATS JetStream, Valkey"]
    FLUX --> OBS["Observability -- Grafana, Prometheus, Loki"]
    FLUX --> APPS["Application workloads -- replicas for HA"]
    DATA --> APPS
    OBS --> APPS
                
The same stack, in the same order, on every cluster I run — bootstrapped once, reconciled by Flux from then on.

The Tools on My Bench

What I actually reach for, cluster after cluster — I carry the same kit from engagement to engagement.

Clusters & OS

Kubernetes Talos Linux Red Hat OpenShift AWS EKS Azure AKS k3d k3s minikube talosctl kubectl

GitOps & Packaging

Flux CD GitOps Kustomize Helm Pulumi Terraform Ansible Automation Platform Azure DevOps

Observability & Cluster Ops

Grafana Prometheus Loki Alloy K9s Freelens Application Insights

Networking & Security

Cilium cert-manager External Secrets Operator Tailscale Mesh VPN AWS Secrets Manager ZScaler

Data & Messaging

PostgreSQL (HA) NATS JetStream Valkey RabbitMQ Kafka EMQX MQTT Rook Ceph Flyway

Scaling, Registries & Supply Chain

KEDA Ray Anyscale PTP time sync AWS ECR Azure Container Registry Docker OCI Containers

Languages & Tooling

Rust Python C# / .NET Go Bash Git GitHub

Where I’ve Run Kubernetes for Real

Four platforms, four very different reliability bars — and a lesson I kept from each one.

Live Broadcast · On-Prem

TGL Golf — Game Operations on OpenShift

This was the deploy with a broadcast air date. As Kubernetes architect leading the systems group for a primetime indoor golf league, I put all 16 game components onto Red Hat OpenShift — a development cluster for rehearsal, and the on-prem cluster sitting physically inside the arena on game day. I modeled every piece of provisioning in Red Hat Ansible Automation Platform as one declarative source of truth, proved the whole thing out on a Universal Studios sound stage, then reworked it on-site around the arena’s constrained network. On opening night the deploy was the boring part — which was exactly the point.

See more in the portfolio
Fortune 500 · Banking

Fiserv — Mainframe to Azure on AKS

On a 60+ person mainframe-to-Azure banking program, I was the Business Solutions Architect and the senior technologist on the floor. I designed the service topology on Azure Kubernetes Service (AKS): event-driven services over Event Grid, Service Bus pub/sub, and Cosmos DB, observed through Grafana, Elastic, and Splunk, with Azure AD B2C / OAuth2 handling identity. Defining testable interfaces early is what kept sixty people from stepping on each other, and the KEDA-on-Kubernetes demos I stood up became the pattern the whole team scaled against.

Industrial · Distributed ML

Koch — Distributed Machine Learning on EKS

At the largest private company in the U.S., I built the distribution layer for a feature-ranking and forecasting engine crunching hundreds of millions of calculations. I derisked it the way I usually do: KEDA against RabbitMQ on minikube first, then the same design scaled onto a real AWS EKS cluster with Ray parallelizing the LSTM and DeepAR PyTorch models. I also told management plainly where Kubernetes was earning its keep and where serverless would have been the cheaper call — part of the architect’s job is saying that out loud.

See more in the portfolio
Stealth · Under NDA

Stealth Real-Time Data Platform

This one I can only describe in the generic — it was delivered under a mutual NDA. What I can say is that I built the Kubernetes and GitOps foundation: a self-managed Talos Linux cluster bootstrapped with Flux, promoted across multiple physical environments from a single GitOps repo, running the platform stack I standardize on — Cilium networking, Rook Ceph storage, cert-manager and External Secrets, HA data services, and Grafana/Prometheus/Loki observability throughout. The product it carries, and its application architecture, stay sealed; the cluster engineering and the GitOps delivery pattern underneath are what this page shows.

Fighting a cluster right now? I’ve probably met your problem before.

Platform builds, Flux-driven GitOps, observability, stateful data, autoscaling — I do the hands-on work myself, remote, on a Corp-to-Corp basis.