alau.dev / v2 — served from a living room
I build systems that
stay up.
DevOps engineer and software developer. I work across .NET, Azure, Terraform and Kubernetes, and I run a small Ubuntu box at home that is serving you this page right now.
- Focus
- Platform & delivery
- Infra style
- Ephemeral by default
- This page
- 0 frameworks, 0 trackers
Type a command such as help, ls, cd projects, neofetch or play, then press Enter. Tab completes, arrow up recalls history.
Hub-and-spoke networking, a private AKS cluster and PostgreSQL, delivered by Terraform and Argo CD. The cluster sleeps after four hours awake.
Running This site, at homeNginx in Docker on Ubuntu, published through a Cloudflare Tunnel. Every push ships a new image.
Listening 55 tracks on rotationA soundtrack for side projects. Put one on and let shuffle take the lead.
The toolbox
From commit to cluster.
- Write
- .NET / C#
- Python
- JavaScript
- Ship
- GitHub Actions
- Docker
- GHCR / ACR
- Provision
- Terraform
- Azure
- OIDC identities
- Run
- AKS
- Argo CD
- Helm
- Edge
- Cloudflare
- Traefik
- cert-manager
01 / Sandbox
A platform I’m
allowed to break.
A personal Azure environment laid out like a real non-production platform: hub-and-spoke networking, a private Kubernetes cluster, a private database and GitOps delivery. I have full access here, so this is where new features get built and strange problems get reproduced.
- Region
- centralus hub and dev networks
- Cluster
- 1 node AKS D2s v3 · private API
- Database
- PostgreSQL 15 B1ms · private subnet only
- Budget alert
- $90 per month · alerts, not a cap
Six repositories, one platform.
Hover a repository to trace what it works with.
-
Terraform
module
Reusable Terraform modules for networks, AKS, PostgreSQL, Key Vault and more. Each module is released with its own tag, such as
used by infraaks1.0.0. -
Terraform
infra
The hub and dev environments, split into layers with one state file each. Pull requests plan; the main branch applies.
pins module tags · runs jobs on runner -
Helm
helm
Shared charts for services, namespace secrets and runners, packaged and pushed to the container registry as OCI artifacts.
consumed by deploy -
Argo CD
deploy
GitOps configuration: an app-of-apps for the cluster add-ons and an ApplicationSet that turns every service folder into an app.
uses helm · deploys runner and sandbox-test-app -
Docker
runner
A GitHub Actions runner image that lives inside the cluster, for jobs that need to reach private endpoints.
deployed by deploy · serves infra -
Node.js
sandbox-test-app
A small diagnostics service that checks config, secrets, the file share and the database connection end to end.
deployed by deploy
All repositories are private.
A hub, a spoke and a tunnel.
- You
- Cloudflare edgeTLS and DNS
- Tunneloutbound only
- Traefikno public IP
- Servicesone namespace
- Container registrycharts and images
- Key Vaultworkload secrets
- Private DNS zonefor PostgreSQL
- Terraform stateone blob per layer
The hub and dev networks are peered.
- Argo CD
- Traefik
- cloudflared
- external-dns
- External Secrets
- GitHub runners
- services
- PostgreSQL flexible serverdelegated subnet, public access off
- Storage accountfile share for services
- Managed identitiesfederated to the cluster
Running: one node hosts Argo CD, Traefik and the services. Requests arrive through an outbound tunnel, so nothing listens on a public IP.
From commit to cluster.
-
01
module
Version a module
A module change gets a new tag for that module only. An environment adopts it by changing one ref.
source = "…//modules/aks?ref=aks1.0.0" -
02
infra
Apply the layers
Hub: foundation → security → storage. Dev: foundation → storage → computational → security. The database step runs on the in-cluster runner because PostgreSQL has no public endpoint.
push main → terraform apply, one job per layer -
03
helm
Publish charts and images
Charts go to the registry as OCI artifacts. The runner and test app images land in the same registry.
helm push → oci://registry/helm -
04
deploy
Hand over to GitOps
The cluster API is private, so the workflow installs Argo CD from inside the cluster and sets up the tunnel. From then on, a merge to main is a deployment, and external-dns gives every new Ingress host its DNS record.
az aks command invoke → Argo CD → app-of-apps
Keeping it cheap.
- The cluster sleeps. Every four hours a check stops AKS if it has been up longer than four hours. Starting it again buys another session.
- Small by default. One node on the free control-plane tier, a burstable database and a Basic registry.
- No public ingress. The tunnel dials out, so Traefik needs no load balancer IP of its own.
- Early warnings. Budget emails at 50%, 80% and 100%, plus a forecast alert.
Keeping it closed.
- Private cluster API. Admin commands run through Azure, not a public endpoint.
- Private data. PostgreSQL is reachable only from inside the networks.
- No stored keys in the cluster. External Secrets reads Key Vault with workload identity.
- Runners inside. Jobs that touch private endpoints run on a self-hosted runner in the cluster.
Honest numbers. Budget alerts send email; they do not stop anything. The uptime limit is what keeps the bill small.
02 / Behind the scenes
Less cloud.
More living room.
This site lives on my own Ubuntu server. The homelab is where I experiment, automate and get hands-on with the systems that make software work.
Trace your request.
- Youyour browser
- Cloudflare edgenearest data centre
- Tunnelcloudflared, outbound only
- Nginx :8080Docker on Ubuntu
- index.htmlthis page
- Round trip
- —
- Edge location
- —
- First byte (page load)
- —
- Page weight
- —
Measured in your browser. Nothing is sent anywhere else.
The path to production.
- 01CI
Push & build
GitHub Actions builds the static site and the converter.
- 02GHCR
Package & publish
Images are stored in GitHub Container Registry.
- 03HOST
Bring it home
Docker Compose pulls and restarts services on Ubuntu.
- 04EDGE
Hello, internet
A Cloudflare Tunnel connects you, with no open ports.
What is running.
- alau
nginx:stable-alpine · static site · port 8080 on the LAN
- converter
python 3.13 + yt-dlp + ffmpeg · 768 MB · 1 CPU · no host port
- cloudflared
tunnel to the edge · outbound connection only
The converter runs as a non-root user with no-new-privileges and is only reachable through Nginx.
03 / A bit about me
Curious by default.
An engineer by practice.
I’m Alau Bolatov, a DevOps engineer and software developer working with .NET, Azure and infrastructure automation.
I like understanding how things fit together, from the code to the container to the machine it runs on. Most of my side projects start with a question like “what would it take to run this myself?” and end with a workflow file and a README.
When the terminal closes, the music starts- 01
Destroy is a feature
If an environment is cheap to delete, it is cheap to experiment with.
- 02
Pipelines over checklists
A deployment that needs a person remembering steps will eventually miss one.
- 03
Small, boring pieces
Nginx, a container, a tunnel. Fewer moving parts, fewer surprises.
- 04
Honest numbers
An estimate is labelled as an estimate. A budget alert is not a cap.
04 / Off the clock
A soundtrack for
the side projects.
Familiar favourites and a few new directions. Pick a track or see where shuffle takes you.
The player loads only when you press play. It uses YouTube’s privacy-enhanced mode.
05 / Your collection, to go
A link in.
An MP3 out.
Paste a YouTube video link to save its audio as an MP3 on the home server, then download it to your favourite music player.
Saved audio
Enter your access key to load files saved on the server.