Scope
An end-to-end Kubernetes platform on AWS, built without a managed control plane — every component provisioned, configured and debugged by hand, then packaged for repeatability.
The point was not to get a cluster running. It was to understand every layer well enough to diagnose it when it broke, which it did, repeatedly.
What it covers
Network foundation. A custom VPC across two availability zones, with public and private subnets, NAT gateways for outbound traffic from private nodes, and security groups scoped per tier. Nodes sit in private subnets; nothing in the cluster is directly reachable from the internet.
Cluster. kubeadm-provisioned control plane with containerd as the runtime and Flannel for the pod network — no managed control plane, so certificate handling, component flags and cluster bootstrapping were all explicit.
Ingress. AWS Load Balancer Controller provisioning an ALB from Ingress resources, with host-based routing to different services and ACM certificates for TLS on a custom domain.
Applications. Multi-service deployment packaged as Helm charts — an application backend plus PostgreSQL — with images stored in ECR.
Observability and scaling. Prometheus and Grafana with Kubernetes dashboards, and a Horizontal Pod Autoscaler whose behaviour was verified by generating load rather than assumed from the manifest.
The cluster, running
containerd, and every control-plane component visible as a pod — etcd, apiserver, controller-manager, scheduler — because nothing here is managed for me.
Host header, all returning {"status":"ok"} — host-based routing resolving to pods spread across both worker nodes.
What actually took the time
The build was straightforward. The debugging was the education:
ImagePullBackOff— ECR authentication and node IAM permissions, not the image- ALB 504 gateway timeouts — target group health checks pointing at a path the application didn’t serve
- Ingress routing not matching — host rules and ingress class resolution
- TLS certificate mismatch — certificate domain against the hostname actually being requested
- DNS resolution — record propagation and the gap between what the ALB advertised and what the domain resolved to
Each of those failed in a way where the symptom pointed somewhere other than the cause. That is the part of running Kubernetes that no tutorial teaches, and the reason I built the cluster the hard way rather than clicking through a managed service.
Why kubeadm rather than EKS
For production I would generally take the managed control plane. For learning, EKS hides exactly the parts worth understanding — etcd, API server configuration, certificate rotation, node bootstrapping. Building it by hand meant that when something broke I had to know which component owned the problem.
It is also the substrate the CKA exam tests, which made it double as preparation.