DEV Community

Cover image for From Zero to Production-Ready Kubernetes
BuildMintZ Media
BuildMintZ Media

Posted on

From Zero to Production-Ready Kubernetes

How I Built a 3-Node K3s Cluster That Maps Almost 1:1 to AWS EKS and Azure AKS

Building enterprise-grade infrastructure without an enterprise-sized cloud bill.


Introduction

There's a common misconception that "real" Kubernetes only exists in the cloud.
AWS has EKS.
Azure has AKS.
Google has GKE.
Everything else is often dismissed as a learning environment.
I wanted to challenge that assumption.
Rather than renting infrastructure from a cloud provider, I decided to build the same architecture myself using commodity hardware and K3s—the lightweight Kubernetes distribution from Rancher.
The goal wasn't simply to run containers.
The goal was to build something that could realistically host a production SaaS application.

That meant designing for:
• High Availability
• Automated deployments
• GitOps
• Persistent storage
• Monitoring
• Secure ingress
• Container orchestration
• Horizontal scaling
• Cloud portability

Months later, what began as an experiment has become a fully operational Kubernetes platform that hosts real applications and mirrors the architecture many companies pay hundreds of dollars every month to run in AWS or Azure.

The best part?
Every architectural decision maps directly to managed cloud services.
If tomorrow I decide to migrate everything into Amazon EKS or Azure AKS, almost nothing changes.


The Architecture
Your infrastructure should be understandable at a glance.
This diagram represents the entire platform.
It isn't a conceptual drawing.
It's my actual deployment.
(Insert architecture image here)
The left side represents the self-hosted infrastructure.
The middle shows the equivalent deployment inside AWS.
The right demonstrates how the same workloads map directly onto Azure.
The striking thing isn't how different they are.
It's how similar they are.


The Self-Hosted Platform
At the heart of everything sits a three-node K3s Kubernetes cluster.
Rather than treating Kubernetes as a container launcher, every service is deployed exactly as it would be inside a commercial cloud.
**
Cluster Overview**

Component   Technology
Kubernetes  K3s
Nodes   3
Backend ASP.NET Core (3 replicas)
Database    PostgreSQL StatefulSet
Cache   Redis
GitOps  ArgoCD
CI/CD   GitHub Actions
Registry    Docker Hub
Ingress Traefik
Storage Persistent Volumes
Monitoring  Prometheus + Grafana
TLS Automatic
Secrets Kubernetes Secrets
Enter fullscreen mode Exit fullscreen mode

Everything is described declaratively.
Nothing is manually configured after deployment.
If the cluster disappears tomorrow, I can recreate the entire platform from Git.
That is the power of GitOps.


Why Three Nodes?

Running Kubernetes on a single machine teaches Kubernetes.
Running three nodes teaches distributed systems.
My cluster deliberately separates responsibilities across the nodes.

Node 1
• ArgoCD 
• Backend Replica #1 
Node 2
• Backend Replica #2 
Node 3
• PostgreSQL 
• Redis 
• Backend Replica #3 
• Traefik Ingress 
Enter fullscreen mode Exit fullscreen mode

This gives me several advantages.
Backend replicas remain distributed.
Application traffic survives node failures.
Stateful workloads stay isolated.
Ingress remains centralised.
If one node disappears, Kubernetes immediately begins recovering workloads elsewhere.
That behaviour is exactly what you'd expect from EKS or AKS.


The Deployment Pipeline
One of my goals was ensuring developers never need to SSH into servers.
Everything begins with Git.


Developer

↓

Git Push

↓

GitHub Actions

↓

Docker Build

↓

Docker Hub

↓

Helm Chart Update

↓

ArgoCD Detects Change

↓

Automatic Kubernetes Deployment
Enter fullscreen mode Exit fullscreen mode

Every deployment is fully automated.
No manual kubectl commands.
No copying files.
No remote logins.
Git becomes the single source of truth.


GitOps with ArgoCD

GitOps completely changed how I think about infrastructure.
Instead of telling Kubernetes what to do, I simply update Git.
ArgoCD continuously compares the desired cluster state with the actual cluster state.
If someone accidentally changes something manually...
ArgoCD changes it back.
If a deployment fails...
ArgoCD rolls everything back.
If a new image appears...
ArgoCD deploys it automatically.
Infrastructure effectively becomes self-healing.


High Availability Without Enterprise Hardware
Many people assume high availability requires expensive cloud infrastructure.
It doesn't.
The backend runs as three replicas.
Kubernetes distributes them across multiple nodes.
If a node disappears:
• Requests continue
• Pods restart elsewhere
• Services remain reachable
• Deployments continue
The application layer simply keeps working.


Persistent Storage
Containers are disposable.
Databases are not.
PostgreSQL runs as a StatefulSet backed by Persistent Volumes.
That means:
• Pod restarts don't lose data
• Storage survives upgrades
• Deployments remain predictable
• Volume claims remain attached
Redis serves a different purpose.
Rather than simply caching application data, it also stores ASP.NET Core Data Protection keys.
Without shared keys, authentication cookies become invalid whenever requests land on different replicas.
Redis solves that problem elegantly.
Users stay logged in regardless of which pod serves the request.


Observability

Production infrastructure without monitoring is simply guessing.
The cluster continuously exports metrics into Prometheus.

Grafana then visualises:
• CPU utilisation
• Memory consumption
• Node health
• Pod restarts
• Storage usage
• Network traffic
• Application performance

Instead of reacting to outages...
I can usually spot problems before users ever notice them.


Security

Although this is self-hosted, the security model mirrors modern cloud deployments.
The platform includes:

• HTTPS everywhere 
• Kubernetes Secrets 
• JWT Authentication 
• Secure Cookies 
• Redis-backed Data Protection 
• Namespaced workloads 
• Container isolation 
Enter fullscreen mode Exit fullscreen mode

Moving into AWS or Azure would require changing almost none of this.


Mapping Everything to AWS

One of the most interesting exercises was asking a simple question:
If this were running in AWS today, what services would replace each component?
The answer is surprisingly straightforward.
Self Hosted AWS
K3s Amazon EKS
PostgreSQL Amazon RDS
Redis Amazon ElastiCache
Traefik AWS Load Balancer Controller + ALB
Persistent Volumes Amazon EBS CSI
nip.io Route53
Certificates AWS Certificate Manager
Everything else remains exactly the same.
The Helm charts.
The Docker images.
The deployments.
ArgoCD.
GitHub Actions.
Application code.
Almost nothing changes.


Mapping to Azure
Azure follows the same philosophy.
Self Hosted Azure
K3s Azure AKS
PostgreSQL Azure Database for PostgreSQL
Redis Azure Cache for Redis
PVC Storage Azure Managed Disks
DNS Azure DNS
TLS Azure Key Vault Certificates
Load Balancing Azure Application Gateway
Again...
Almost every Kubernetes manifest remains identical.
The cloud changes.
The application doesn't.


Cloud Cost Comparison
One of the biggest motivations behind this project was understanding the

true cost of managed Kubernetes.
AWS
Service Monthly
EKS Control Plane   $73
Three Worker Nodes  ~$90
RDS PostgreSQL  ~$18
ElastiCache Redis   ~$18
Application Load Balancer   ~$18
Storage ~$5
Networking  ~$10
Estimated Total: approximately $230/month
________________________________________
Azure
Service Monthly
AKS Cluster Free Control Plane
Three Worker Nodes  ~$96
PostgreSQL  ~$20
Redis   ~$20
Application Gateway ~$25
Managed Storage ~$5
Networking  ~$10
Estimated Total: approximately $170/month
________________________________________
Enter fullscreen mode Exit fullscreen mode

Meanwhile...
My self-hosted version costs essentially nothing beyond hardware I already own and electricity.
For an early-stage SaaS, that's a meaningful saving.


Performance
People often assume cloud infrastructure is automatically faster.
In reality, architecture matters far more than location.
Performance comes from good engineering.
• Three backend replicas eliminate cold starts.
• Redis avoids unnecessary database work.
• PostgreSQL communicates over a local network.
• K3s has minimal control-plane overhead.
• Traefik is lightweight and efficient.
• ASP.NET Core is extremely fast.
• Razor Pages avoid large client-side JavaScript bundles.
The result is a platform that feels remarkably responsive despite running entirely on self-managed hardware.


What This Project Taught Me

Building this cluster fundamentally changed how I think about infrastructure.
I learned that Kubernetes isn't inherently complicated.
Poor architecture is.
I learned that GitOps dramatically reduces operational risk.
I learned that observability should be designed from day one, not added after something breaks.
Most importantly...
I realised cloud providers don't remove complexity.
They move it.
Understanding the platform underneath managed Kubernetes makes services like EKS and AKS far less intimidating.


Where This Goes Next

This isn't the final version.
Over the coming months I plan to add:
• External Secrets integration
• Longhorn distributed storage
• HashiCorp Vault
• OpenTelemetry tracing
• Loki for log aggregation
• Horizontal Pod Autoscaling
• KEDA event-driven scaling
• Multi-environment GitOps (Dev, Test, Production)
• Automated disaster recovery
• Multi-cluster deployments

The architecture will continue evolving, but the foundation is already solid.


Final Thoughts

This project started as a curiosity.
Could a lightweight Kubernetes distribution really support a production-grade SaaS platform?
The answer was an emphatic yes.
Today I have a three-node Kubernetes cluster with high availability, GitOps, automated deployments, persistent storage, monitoring, secure ingress, and a cloud-ready architecture that maps almost directly to both AWS EKS and Azure AKS.
Perhaps the biggest lesson wasn't technical at all.
Cloud platforms are incredibly powerful—but they're not magic.
Once you understand Kubernetes, the cloud becomes an implementation detail rather than a dependency.
Whether your workloads run in your home lab, a rack in a data centre, or across multiple cloud regions, the principles remain the same.
Build reliable systems. Automate everything. Treat infrastructure as code. Design for failure. And always leave yourself a clear path to scale.
That's exactly what this cluster represents.

________________________________________
BuildMintz

Infrastructure that ships products—not just prototypes.

This Kubernetes platform is part of the broader BuildMintz journey, where I'm documenting the engineering behind building production-ready SaaS infrastructure from the ground up, proving that enterprise architecture doesn't require enterprise budgets.

Top comments (0)