alau.dev

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.

See the sandbox
Focus
Platform & delivery
Infra style
Ephemeral by default
This page
0 frameworks, 0 trackers
alau@homelab: ~ type help

Type a command such as help, ls, cd projects, neofetch or play, then press Enter. Tab completes, arrow up recalls history.

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
01 / Repositories

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 aks1.0.0.

    used by infra
  • 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.

02 / Architecture

A hub, a spoke and a tunnel.

  1. You
  2. Cloudflare edgeTLS and DNS
  3. Tunneloutbound only
  4. Traefikno public IP
  5. Servicesone namespace
Hub network · shared
  • Container registrycharts and images
  • Key Vaultworkload secrets
  • Private DNS zonefor PostgreSQL
  • Terraform stateone blob per layer

The hub and dev networks are peered.

Dev network · spoke
AKS private API · 1 node
  • 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.

03 / Delivery

From commit to cluster.

  1. 01

    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"
    module
  2. 02

    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
    infra
  3. 03

    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
    helm
  4. 04

    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
    deploy
Cost

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

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.

Live

Trace your request.

  1. Youyour browser
  2. Cloudflare edgenearest data centre
  3. Tunnelcloudflared, outbound only
  4. Nginx :8080Docker on Ubuntu
  5. index.htmlthis page
Round trip
—
Edge location
—
First byte (page load)
—
Page weight
—

Measured in your browser. Nothing is sent anywhere else.

Pipeline

The path to production.

  1. 01

    Push & build

    GitHub Actions builds the static site and the converter.

    CI
  2. 02

    Package & publish

    Images are stored in GitHub Container Registry.

    GHCR
  3. 03

    Bring it home

    Docker Compose pulls and restarts services on Ubuntu.

    HOST
  4. 04

    Hello, internet

    A Cloudflare Tunnel connects you, with no open ports.

    EDGE
See the deployment workflow
docker compose ps

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
  1. 01

    Destroy is a feature

    If an environment is cheap to delete, it is cheap to experiment with.

  2. 02

    Pipelines over checklists

    A deployment that needs a person remembering steps will eventually miss one.

  3. 03

    Small, boring pieces

    Nginx, a container, a tunnel. Fewer moving parts, fewer surprises.

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

Track — / 55

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.

The key configured on the server. Kept in this tab only, never stored.

One video at a time, up to one hour and 150 MB. Only use videos you own or have permission to download.

/data

Saved audio

Enter your access key to load files saved on the server.