Personal infrastructure · AWS ap-south-1

app-hub

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.

Why it exists

Three purposes at once, and none of them is a pretext.

A learning vehicle

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.

Software I actually use

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.

Something worth showing

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.

How a request travels

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.

EKS cluster · app-hub-eks · v1.36 Internet ALB Ingress gateway proxy + dashboard links-service CRUD · ClusterIP aggregator liveness probes DynamoDB survives teardown your tools probed, not proxied links-service:80 aggregator:8002
DynamoDB sits outside the dashed boundary deliberately: it lives in a second Terraform stack with its own state file, so the nightly terraform destroy cannot reach it — by construction, not by care.

Nothing runs overnight, and that is the design

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.
~$0.28per hour, cluster up
23:30automated nightly teardown
59resources destroyed, then rebuilt
$0resting cost

The stack, and what is actually running

Honest status. "Planned" means written or not started — not quietly implied to be live.

PieceWhat it doesState
TerraformVPC, EKS, ECR, IRSA roles. Two stacks — one ephemeral, one never destroyed.running
EKS 1.36Two t3.medium nodes in private subnets, across two AZs.running
FastAPI ×3links-service, gateway, aggregator. Read-only root filesystem, non-root, all capabilities dropped.running
IRSAA pod identity for DynamoDB with no stored credential anywhere.running
Prometheus + GrafanaInstalled by Helm; scrape config is a ServiceMonitor, never a config file.running
n8nCost watchdog and teardown notifier, running off-cluster.running
Ingress + shared ALBOne public entry point; links-service drops to ClusterIP.written
Jenkins · ArgoCDBuild and push, then reconcile from the manifests repo.planned

Seven repositories

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.

The write-ups are the point

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.

A monitor that died three times

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".

"I cannot see it" ≠ "it is not there"

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.

A five-times price in one string

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.

Read all 32 write-ups →