Solutions Architect · Cloud platforms and DevOps

I design cloud platforms that are cheap to run, safe to change, and easy to explain.

Architecture, infrastructure as code and delivery pipelines across Azure, AWS and Cloudflare, from the first diagram to the pipeline that ships it.

Clouds
Azure · AWS · Cloudflare
Platform and delivery
Kubernetes · Terraform · GitHub Actions
Build with
Go · Python · TypeScript

01 · Selected work

Four systems, and the decisions behind them

Each case study starts with the problem, shows the architecture, and lists the decisions with the reasoning that makes them defensible.

01AWS · Multi-environment GitOps Private repository

Solar Atlas: a multi-environment AWS platform managed entirely through Terraform and pull requests

A flat, prod-only Terraform repository refactored into reusable modules and separate dev and prod stacks, with a GitOps path from pull request to plan, dev apply, and gated promotion to prod.

AWS Cloud · ap-south-11234Pull requestplan as a commentGitHub ActionsOIDC, no stored keysApply devon merge to mainPromotedeliberate, gatedApply prodsame modules, prod varsshared/globalOIDC · DNS zone · certificateslive/dev7 modules · dev.tfvarslive/prod7 modules · prod.tfvarsTerraform stateS3 · native locking · key per stack
  1. 1A pull request triggers terraform plan, and the plan is posted back as a comment for review.
  2. 2Merging to main applies dev. The workflow assumes an AWS role through GitHub OIDC, so no long-lived keys exist.
  3. 3Promotion to prod is a separate, deliberate step.
  4. 4Prod applies the same seven modules with its own variables. Each stack keeps its own state key.

flowsupporting

The problem

One flat repository meant one environment: every change touched production, nothing could be rehearsed, and the structure could not be reused for a second stack.

Design decisions

Modules plus one stack per environment
Seven reusable modules (network, data, iam, lambdas, ecs-ec2-backend, frontend-cdn, observability) are called by live/dev and live/prod with their own variable files, so dev is a true rehearsal of prod.
One state key per stack, with native S3 locking
global, dev and prod each get their own key in an S3 backend. A bad apply in one stack cannot corrupt another, and native locking removes the need for a separate lock table.
GitHub OIDC instead of stored cloud credentials
An account-level OIDC provider lets workflows assume deploy roles for the duration of a run. No long-lived AWS keys live in the repository or its secrets.
Plan on the PR, apply on merge, promote on purpose
Separate workflows for plan, apply-dev, apply-prod, drift detection and hotfix. Reviewers see the plan before merge, and prod only changes through promotion or a deliberate hotfix path.
Private origin behind a CDN
The frontend is a private S3 bucket served through CloudFront with origin access control and a Route 53 alias, so the bucket is never public.
A written go-live order for the billable steps
Delegate DNS first, then per environment: repository, image, then the rest, then secrets out-of-band and a forced redeploy. Ordering mistakes are the expensive ones.
02Azure · Kubernetes platform

A cost-aware Kubernetes platform on Azure: Terraform to AKS, delivered by GitHub Actions

A Next.js frontend, a Go authentication API and a Go WebSocket chat service, provisioned with Terraform on AKS and deployed with Kustomize from a single pipeline, with the cost levers written down.

Azure · resource groupAKS · namespace devops-app1234TerraformRG · ACR · AKSGitHub Actionsbuild · push · applyContainer RegistryAcrPull for kubeletAKS clusterfree tierUsersHTTPSPublic IPone load balanceringress-nginx/ /api /wsfrontendNext.js · :3000auth-apiGo · JWT · :8001chat-serviceGo · WebSocketPostgreSQLStatefulSetRedischat history
  1. 1Terraform runs in the pipeline and creates the resource group, registry and cluster.
  2. 2It grants the cluster's kubelet identity the AcrPull role, so nodes pull images without secrets.
  3. 3The pipeline builds and pushes images, then runs kubectl apply -k with Kustomize image overrides.
  4. 4Users reach a single public IP. ingress-nginx routes / to the frontend, /api to auth and /ws to chat.

flowsupporting

The problem

Show a complete path from an empty subscription to running services, and make the running cost something you can reason about and switch off.

Design decisions

Identity-based image pulls
Terraform grants the AKS kubelet identity the AcrPull role on the registry, so the cluster pulls images without registry passwords or pull secrets.
One ingress, three routes
ingress-nginx sends / to the frontend, /api to the auth service and /ws to the chat service on a single public IP, which keeps the load balancer count and the bill down.
Stateless services, stateful data
The Go services stay stateless and validate JWTs issued by the auth service. Postgres runs as a StatefulSet with init SQL from a ConfigMap; Redis holds recent chat history.
Kustomize image overrides
The pipeline builds and pushes images to ACR, then applies the manifests with the new tags without editing YAML by hand.
Cost as a design input
AKS Free tier, ACR Basic, a 0.1 GB daily Log Analytics quota with 30-day retention, and a stop/start script so compute charges drop when nobody is using it. The docs also cover a full destroy.
03Cloudflare · Product architecture

DevOps Buddy: an AI-native, read-only-by-default DevOps platform running entirely on Cloudflare

A SaaS that maps a team's GitHub Actions and Cloudflare estate into projects and services, explains failures with an AI root cause, and can draft fixes as pull requests that only open after a human approves.

Cloudflare Workers1234Browser appReact · shadcn/uiSign-inBetter Auth · OTPD1state · cache · auditAPIbudgeted viewsRead-only guardGET · HEAD · 3 POSTsAutomationswebhooks · cron · eventsApproval gateallowlist · token · auditOpenAIroot cause · fixesCloudflare API54 products · readGitHubPRs on buddy/* only
  1. 1The browser talks to one Worker. Sign-in uses one-time codes, so no password hashing is needed on the free tier.
  2. 2Every upstream read goes through the guard: GET, HEAD and three listed read-only POSTs. Anything else throws.
  3. 3Automations and AI fixes never write directly. They queue an approval request.
  4. 4After a human approves, the only write path opens a pull request on a buddy/* branch. It never merges.

flowsupporting

The problem

Give companies with many projects one place to see, understand, fix and verify their delivery, without handing an autonomous agent the keys to production.

Design decisions

Read-only by construction
Every upstream call goes through one guard that allows GET and HEAD plus three explicitly listed read-only POSTs. Anything else throws, so monitoring code cannot change anything by accident.
A single, audited write path with approval
Writes use a separate write-scoped connection, an endpoint allowlist, one-time approval tokens and an audit log. Fixes land on buddy/* branches, and Buddy never merges or touches default branches.
A data-driven Cloudflare catalog
Fifty-four Cloudflare products are described as declarative registry entries, so adding one is a data change. The executor respects token permissions and marks unreadable products as no access instead of failing.
Designed around the free tier's limits
Workers Free allows 10 ms of CPU and 50 subrequests per invocation. That ruled out password hashing (sign-in uses one-time codes) and drove incremental views that finish loading over a few requests.
Event-driven, with humans in the loop
Webhooks and polling emit events; automations analyse, draft and notify, but anything that writes waits in an approval inbox. Only opted-in, reversible actions can run on their own.
04CI/CD · Security in the pipeline

A DevSecOps pipeline that fails the build on insecure code and vulnerable images

A FastAPI service with a Celery worker, Redis and PostgreSQL, where linting, static security analysis, tests and container scanning are gates in GitHub Actions rather than afterthoughts.

Docker Compose · health checks and dependency order12Push / PRGitHub Actionsflake8style · fastBanditstatic securitypytestunit · APIBuild imagemulti-stage · non-rootTrivyimage scanPostgreSQLdataFastAPIREST + web UICelery workerasync tasksRedisbroker · cache
  1. 1Checks run in order of cost: style, then static security analysis, then tests.
  2. 2The image is built and scanned before it can be published. A failing gate stops the pipeline.

flowsupporting

The problem

Make secure-by-default the path of least resistance, so a vulnerable image cannot reach a registry unnoticed.

Design decisions

Shift security left, in order of cost
flake8 and Bandit run first because they are cheap and fast; tests come next; the container image is built and scanned with Trivy last, before anything is published.
Hardened images
Multi-stage Dockerfiles with a non-root user and a minimal footprint, plus an .env.example so secrets never enter the repository.
A production-shaped local environment
Docker Compose with health checks and dependency ordering for the API, worker, database and broker, so local runs match how the services start in production.

02 · How I work

From constraints to a decision you can defend

Good architecture is mostly good questions asked early. This is the loop I run on every system.

  1. 01

    Frame

    Requirements, constraints and failure modes first: who uses it, what it may cost, what must never break.

  2. 02

    Compare

    Two or three viable options with their trade-offs, including cost and operational load, not just the favourite.

  3. 03

    Decide

    A short written decision: what was chosen, what was rejected, and what would make me revisit it.

  4. 04

    Build

    Infrastructure as code and a pipeline from day one. The smallest system that meets the constraints.

  5. 05

    Operate

    Observability, drift detection, runbooks and a cost lever, so the design keeps working after hand-over.

Practices, and where you can see them

Multi-environment design
Seven modules called by separate dev and prod stacks with their own variables.
Solar Atlas
Blast radius and state layout
One Terraform state key per stack, so a bad apply stays in its own stack.
Solar Atlas
GitOps promotion paths
Plan on the pull request, apply dev on merge, promote to prod on purpose.
Solar Atlas
Cost modelling and levers
Free-tier AKS, a daily log quota, a stop/start script, and a documented teardown.
AKS platform
Least privilege and approval boundaries
A read-only guard, a separate write path, one-time approvals and an audit log.
DevOps Buddy
Designing to platform limits
Workers Free limits shaped sign-in and how large views load.
DevOps Buddy
Decisions written down
An architecture document with a diagram and a cost document ship with the repository.
AKS platform

03 · Technical skills

The tools behind the designs

Technologies I have used to build and run the systems above, grouped by domain.

Cloud platforms

AWS

  • ECS
  • EC2 Auto Scaling
  • Lambda
  • DynamoDB
  • S3
  • CloudFront
  • Route 53
  • IAM
  • Systems Manager
  • CloudWatch

Azure

  • AKS
  • Container Registry
  • Azure Monitor
  • Log Analytics
  • Load Balancer
  • Managed identities

Cloudflare

  • Workers
  • D1
  • R2 and KV
  • Pages

Containers and orchestration

  • Kubernetes
  • Docker
  • Helm
  • Kustomize
  • ingress-nginx

Infrastructure as code

  • Terraform
  • Ansible

CI/CD and GitOps

  • GitHub Actions
  • GitHub
  • Jenkins

Security and identity

  • OIDC federation
  • Trivy
  • Bandit
  • JWT
  • Secrets: SSM and Kubernetes

Observability

  • Prometheus
  • Grafana
  • Azure Monitor
  • CloudWatch

Data

  • PostgreSQL
  • Redis
  • DynamoDB
  • SQLite and D1

Languages

  • Go
  • Python
  • TypeScript
  • C++

04 · Contact

Let's talk about your architecture.

If you are designing a platform, moving to Kubernetes, or trying to make your cloud bill and your release process calmer, I would like to hear about it.