<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Welcome to My Blog on Alexander's Blog</title><link>https://alexanderbakin.com/</link><description>Recent content in Welcome to My Blog on Alexander's Blog</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Mon, 24 Aug 2026 15:30:11 +0400</lastBuildDate><atom:link href="https://alexanderbakin.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Fixing logrotate's Glob Confusion</title><link>https://alexanderbakin.com/logrotate-fix/</link><pubDate>Thu, 20 Aug 2026 00:00:00 +0000</pubDate><guid>https://alexanderbakin.com/logrotate-fix/</guid><description>&lt;p&gt;A follow-up to &lt;a href="https://alexanderbakin.com/logrotate-wildcard-bug/"&gt;the wildcard bug post&lt;/a&gt; from last year.&lt;/p&gt;
&lt;h2 id="picking-the-thread-back-up"&gt;Picking the Thread Back Up&lt;/h2&gt;
&lt;p&gt;The rename workaround, &lt;code&gt;*.example.com&lt;/code&gt; to &lt;code&gt;_.example.com&lt;/code&gt;, fully resolved the operational problem. Retention became predictable again, and there was no pressing reason to go further. But a workaround isn&amp;rsquo;t a fix, and the actual bug was still sitting in logrotate&amp;rsquo;s own source, waiting to bite the next person who names a file after a wildcard domain. This week I finally sat down and found it properly, instead of just renaming files around it.&lt;/p&gt;</description></item><item><title>About</title><link>https://alexanderbakin.com/about/</link><pubDate>Fri, 14 Aug 2026 00:00:00 +0000</pubDate><guid>https://alexanderbakin.com/about/</guid><description>&lt;p&gt;I&amp;rsquo;m Alexander - a DevOps Engineer with 5 years of experience, passionate about building reliable, scalable, and secure systems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Skills&lt;/strong&gt;: AWS, Terraform, Terragrunt, Kubernetes, Docker, GitHub Actions, GitOps, Monitoring, Security&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Experience&lt;/strong&gt;: Designing and operating production infrastructure. Building platforms that make other engineers more productive. Automating everything that can be automated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interests&lt;/strong&gt;: Cloud native architecture, platform engineering, developer experience, zero-trust security, cost optimization.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This blog&lt;/strong&gt;: Built from scratch with Terraform + Hugo, deployed through GitHub Actions with OIDC, served from CloudFront with S3 origin and geo-restriction. &lt;a href="https://alexanderbakin.com/meta/"&gt;Check out the architecture&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Building a Production-Grade Personal Blog with AWS, Terraform, and Hugo</title><link>https://alexanderbakin.com/meta/</link><pubDate>Fri, 14 Aug 2026 00:00:00 +0000</pubDate><guid>https://alexanderbakin.com/meta/</guid><description>&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;
&lt;p&gt;As a DevOps engineer, your personal website is your portfolio. It should demonstrate not just what you &lt;em&gt;know&lt;/em&gt; but what you &lt;em&gt;build&lt;/em&gt;. This blog is itself an example of a production-grade cloud architecture - every decision documented, every tradeoff explained.&lt;/p&gt;
&lt;h2 id="architecture-overview"&gt;Architecture Overview&lt;/h2&gt;
&lt;p&gt;&lt;img alt="Architecture diagram: Route53 to CloudFront to S3, with GitHub Actions driving Terraform and content deploys via OIDC" loading="lazy" src="https://alexanderbakin.com/images/architecture-diagram.svg"&gt;&lt;/p&gt;
&lt;p&gt;The stack:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Content&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Markdown → Hugo static site&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CI/CD (Infra)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GitHub Actions - PR plan, merge apply&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CI/CD (Content)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GitHub Actions - build, sync, invalidate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State Mgmt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3 backend, concurrency-gated at pipeline level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Origin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3 (private, versioned, encrypted, CloudFront OAC only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CDN&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CloudFront with HTTPS, Brotli/Gzip, security headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DNS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Route 53 alias records (A / AAAA, root + www)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TLS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ACM certificate (auto-renewal, TLSv1.2_2021)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OIDC - no AWS access keys stored anywhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Monitoring&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CloudWatch dashboard + error rate alarm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost Control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS Budgets alert (direct email)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-design-decisions"&gt;Key Design Decisions&lt;/h2&gt;
&lt;h3 id="1-two-separate-pipelines"&gt;1. Two Separate Pipelines&lt;/h3&gt;
&lt;p&gt;Infrastructure changes and content changes have different risk profiles and review requirements. They&amp;rsquo;re handled by separate workflows:&lt;/p&gt;</description></item><item><title>Getting to 99.9%: Kubernetes and Real Observability for 20+ Microservices</title><link>https://alexanderbakin.com/k8s-observability/</link><pubDate>Mon, 15 Sep 2025 00:00:00 +0000</pubDate><guid>https://alexanderbakin.com/k8s-observability/</guid><description>&lt;p&gt;99.9% availability sounds like a slogan until you do the arithmetic: it&amp;rsquo;s a budget of roughly eight and a half hours of downtime for an entire year, across every service on the platform. Once you frame it that way, it stops being an aspiration and becomes an engineering constraint you either design for or blow through by month three. Getting there for a platform of 20+ microservices took deliberate work in two places that don&amp;rsquo;t get enough credit next to &amp;ldquo;just add more replicas&amp;rdquo;: the rollout path, and observability that&amp;rsquo;s actually fast enough to matter.&lt;/p&gt;</description></item><item><title>The Wildcard That Broke Nginx Log Rotation</title><link>https://alexanderbakin.com/logrotate-wildcard-bug/</link><pubDate>Sat, 05 Jul 2025 00:00:00 +0000</pubDate><guid>https://alexanderbakin.com/logrotate-wildcard-bug/</guid><description>&lt;p&gt;One of those infrastructure problems that looks simple, until you realize the filename itself is part of the problem.&lt;/p&gt;
&lt;h2 id="the-setup"&gt;The Setup&lt;/h2&gt;
&lt;p&gt;We had Nginx configured to handle wildcard subdomains: &lt;code&gt;server_name *.example.com;&lt;/code&gt;, with access logs named after the server block. That&amp;rsquo;s a convenient pattern for multi-tenant setups where subdomains get created dynamically and you don&amp;rsquo;t want to hand-maintain a vhost file per tenant. Nginx doesn&amp;rsquo;t care that the resulting log filename contains a literal &lt;code&gt;*&lt;/code&gt; - it just opens the file and writes to it.&lt;/p&gt;</description></item><item><title>From 4 Hours to 20 Minutes: Replacing a Manual Deployment with GitLab CI</title><link>https://alexanderbakin.com/faster-gitlab-ci/</link><pubDate>Tue, 09 Apr 2024 00:00:00 +0000</pubDate><guid>https://alexanderbakin.com/faster-gitlab-ci/</guid><description>&lt;p&gt;Deploying used to mean an engineer, a runbook document, and four hours of undivided attention. Not four hours of a pipeline running in the background, four hours of a person actively driving it: SSH into the target host, pull the latest code, run the build script by hand, stop the old process, start the new one, watch the logs to see if it came up clean, and post a status update. It worked, in the sense that it got code to production. It didn&amp;rsquo;t scale, in the sense that &amp;ldquo;get code to production&amp;rdquo; now depended entirely on one specific person having a free afternoon.&lt;/p&gt;</description></item><item><title>Writing a Prometheus Exporter in Go for Metrics Nobody Was Collecting</title><link>https://alexanderbakin.com/prometheus-exporter/</link><pubDate>Mon, 21 Aug 2023 00:00:00 +0000</pubDate><guid>https://alexanderbakin.com/prometheus-exporter/</guid><description>&lt;p&gt;The platform I operate is a fleet of NGINX instances running a Web Application Firewall module in front of production traffic. The stock NGINX exporter gave us request counts, status codes, and upstream latency, all useful, and all completely blind to the one question that actually mattered for this platform: is the WAF itself doing its job, or is it about to fall over.&lt;/p&gt;
&lt;h2 id="the-gap"&gt;The Gap&lt;/h2&gt;
&lt;p&gt;The WAF module exposes its own internal counters through a local status endpoint: rule hits, rule blocks broken down by rule ID, requests evaluated per second, memory used by the rule engine. None of it reaches Prometheus by default, because it&amp;rsquo;s not something the generic NGINX exporter knows exists. Our dashboards could tell us &amp;ldquo;NGINX is up.&amp;rdquo; They couldn&amp;rsquo;t tell us &amp;ldquo;rule 942100 just started blocking forty times its normal traffic,&amp;rdquo; which is exactly the kind of thing that means either an attack is underway or a rule update just went sideways and started blocking legitimate users. Both are pages. Neither showed up anywhere.&lt;/p&gt;</description></item><item><title>Exposing a Homelab to the Internet Without Exposing Myself</title><link>https://alexanderbakin.com/homelab-zero-trust/</link><pubDate>Sun, 12 Jun 2022 00:00:00 +0000</pubDate><guid>https://alexanderbakin.com/homelab-zero-trust/</guid><description>&lt;p&gt;Once the homelab had more than one service worth using away from home (Nextcloud for files, Jellyfin for media), &amp;ldquo;just port forward it&amp;rdquo; stopped being an option I was willing to consider. I spend enough of my working hours dealing with what happens to services that sit on the open internet unpatched or misconfigured for a week. I wasn&amp;rsquo;t going to do that to my own network.&lt;/p&gt;
&lt;h2 id="layer-one-a-private-network-that-doesnt-need-open-ports"&gt;Layer One: A Private Network That Doesn&amp;rsquo;t Need Open Ports&lt;/h2&gt;
&lt;p&gt;Tailscale solved the &amp;ldquo;I want access from anywhere&amp;rdquo; problem without opening a single inbound port on my router. It&amp;rsquo;s WireGuard under the hood: every device gets a key, joins the same private tailnet, and talks to every other device directly (or through a relay when direct connection isn&amp;rsquo;t possible) over an encrypted tunnel. My phone, laptop, and homelab nodes all land on the same private address space no matter which network they&amp;rsquo;re actually sitting on. For anything only &lt;em&gt;I&lt;/em&gt; need to reach, that&amp;rsquo;s the entire solution: no public DNS record, no exposed port, nothing for an internet-wide scanner to ever find.&lt;/p&gt;</description></item><item><title>Deploying Vaultwarden with Ansible: My Homelab's First Real Service</title><link>https://alexanderbakin.com/vaultwarden-ansible/</link><pubDate>Sun, 18 Jul 2021 00:00:00 +0000</pubDate><guid>https://alexanderbakin.com/vaultwarden-ansible/</guid><description>&lt;p&gt;I started self-hosting for a boring reason: I didn&amp;rsquo;t like that a single cloud account breach could hand someone every password I own. A self-hosted password manager has its own risks, but at least the blast radius is mine to control. Vaultwarden (a lightweight Bitwarden-compatible server) was the obvious first service, and I used it as an excuse to do my homelab properly instead of SSH-ing in and running commands by hand.&lt;/p&gt;</description></item></channel></rss>