Personal infrastructure · AWS ap-south-1
A self-hosted home for the tools I build for myself — three FastAPI services on AWS EKS, provisioned by Terraform, deployed from Git-tracked Kubernetes manifests, and destroyed every single night on purpose.
Three purposes at once, and none of them is a pretext.
Targeted at exactly the stack my organisation is migrating to — EKS, Terraform, Prometheus, Grafana, Jenkins, ArgoCD, n8n — because I am the Principal SRE who has to own it. Where speed and understanding conflict on the infrastructure, understanding wins.
Not a demo. The dashboard is the catalogue of what runs where, and it has to work and stay up. That constraint is what stops the project becoming a pile of tutorials.
The bar is "would this survive a review by someone I want to impress?" — not "does it work on my machine?". Which is why the write-ups below record the failures, not just the outcomes.
Three services, deliberately. gateway exists to prove
service-to-service discovery by Kubernetes DNS name; aggregator
is never publicly reachable, so a call from it is pod-to-pod with neither
end being the front door.
terraform destroy cannot reach it — by construction, not by care.
Run 24×7 this would cost roughly $200/month — the NAT gateway and the EKS control plane bill whether or not anything is deployed. So the cluster is destroyed at the end of every session, and "nothing is deployed" is the normal resting state.
This is why the demo above is a stub rather than the live system. EKS issues a new API endpoint on every cluster creation, so a permanent link to the real thing would be dead most of the time and point at the wrong host the rest.
Honest status. "Planned" means written or not started — not quietly implied to be live.
| Piece | What it does | State |
|---|---|---|
Terraform | VPC, EKS, ECR, IRSA roles. Two stacks — one ephemeral, one never destroyed. | running |
EKS 1.36 | Two t3.medium nodes in private subnets, across two AZs. | running |
FastAPI ×3 | links-service, gateway, aggregator. Read-only root filesystem, non-root, all capabilities dropped. | running |
IRSA | A pod identity for DynamoDB with no stored credential anywhere. | running |
Prometheus + Grafana | Installed by Helm; scrape config is a ServiceMonitor, never a config file. | running |
n8n | Cost watchdog and teardown notifier, running off-cluster. | running |
Ingress + shared ALB | One public entry point; links-service drops to ClusterIP. | written |
Jenkins · ArgoCD | Build and push, then reconcile from the manifests repo. | planned |
Separate projects, not part of app-hub. Live links, with source where it is public.
A polyrepo on purpose: each component has its own history and its own remote, and the umbrella repo tracks only what belongs to no single one.
Every step has a file in learn/ — what was done, why, and
what bit us. They record the mistakes, because the mistakes are where the
understanding came from.
An expired OAuth token, then a timezone that made it check at 2:30 AM,
then a laptop sleep that stopped its scheduler. Every time, the UI
said active. Active means "intends to run", never
"has run".
A wrong --query that returned None instead of
erroring; a grep that counted its own shell. The fix is to
run the same check against something you know is absent —
if both answers match, your check cannot tell the difference.
An EKS version past standard support billed at $0.50/hour instead of $0.10. It was 74% of one session's bill and looked like nothing at all in the Terraform.