<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Go Deployment &amp; Operations on Atharva Pandey</title><link>https://atharva.page/series/go-deployment--operations/</link><description>Recent content in Go Deployment &amp; Operations on Atharva Pandey</description><generator>Hugo</generator><language>en-us</language><copyright>Copyright ©</copyright><lastBuildDate>Sun, 15 Jun 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://atharva.page/series/go-deployment--operations/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 8: Zero-Downtime Deploys — Rolling updates without dropping requests</title><link>https://atharva.page/post/go/go-deploy-zero-downtime/</link><pubDate>Sun, 15 Jun 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-deploy-zero-downtime/</guid><description>&lt;p&gt;The first rolling deployment I did without graceful shutdown handling produced about 200 errors for in-flight requests. Kubernetes sent &lt;code&gt;SIGTERM&lt;/code&gt; to the old pods, the Go process exited immediately, and every active request was terminated mid-flight. The users got 502s. The monitoring dashboard lit up red for about 30 seconds per deploy, every time.&lt;/p&gt;
&lt;p&gt;Kubernetes&amp;rsquo;s rolling update strategy replaces pods one at a time and can achieve zero request drops — but only if your application cooperates. The contract is: Kubernetes sends &lt;code&gt;SIGTERM&lt;/code&gt;, your application finishes in-flight requests, then exits. If you ignore &lt;code&gt;SIGTERM&lt;/code&gt; and exit immediately, Kubernetes kills you with &lt;code&gt;SIGKILL&lt;/code&gt; after the grace period anyway, and you drop the requests. The work is in your application code, not in the Kubernetes config.&lt;/p&gt;</description></item><item><title>Lesson 7: Profiling in Containers — pprof works in Kubernetes too</title><link>https://atharva.page/post/go/go-deploy-profiling-containers/</link><pubDate>Sat, 12 Apr 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-deploy-profiling-containers/</guid><description>&lt;p&gt;The first time I needed to profile a Go service in production, I assumed I&amp;rsquo;d have to deploy a special build, reproduce the problem locally, or use some heavyweight APM product. Then I learned that Go&amp;rsquo;s &lt;code&gt;net/http/pprof&lt;/code&gt; package can serve live profiling data from a running process via HTTP — in a container, in Kubernetes, right now, without redeployment.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pprof&lt;/code&gt; is the built-in Go profiler. It can capture CPU profiles (what functions are consuming CPU time), heap profiles (what&amp;rsquo;s allocated in memory and by whom), goroutine profiles (how many goroutines exist and what they&amp;rsquo;re doing), and block/mutex profiles (where goroutines are waiting). All of this is available as HTTP endpoints that you can scrape with &lt;code&gt;go tool pprof&lt;/code&gt; from your laptop.&lt;/p&gt;</description></item><item><title>Lesson 6: Race Detector in CI — Run -race on every PR or ship bugs</title><link>https://atharva.page/post/go/go-deploy-race-ci/</link><pubDate>Wed, 12 Feb 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-deploy-race-ci/</guid><description>&lt;p&gt;A data race is a memory safety bug. Two goroutines access the same variable without synchronization, at least one is writing, and the Go memory model makes no guarantees about what happens. In practice: values get corrupted, counters drift, maps panic at runtime. These bugs are intermittent — they appear under load, on fast machines, during deploys — and they&amp;rsquo;re nearly impossible to reproduce deterministically.&lt;/p&gt;
&lt;p&gt;The Go race detector is one of the most powerful debugging tools in any language ecosystem. It instruments every memory access at the compiler level and reports races precisely: the exact goroutine stacks at the moment of the conflicting accesses. But it only helps if you run it. Most teams run it once after a bug report. The right approach is running it on every PR, in CI, before the code is ever merged.&lt;/p&gt;</description></item><item><title>Lesson 5: CI/CD for Go — GitHub Actions that actually catch bugs</title><link>https://atharva.page/post/go/go-deploy-cicd/</link><pubDate>Sun, 15 Dec 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-deploy-cicd/</guid><description>&lt;p&gt;I&amp;rsquo;ve set up CI pipelines for Go services about a dozen times, and every iteration taught me something about what the pipeline should actually catch. The first version ran &lt;code&gt;go test ./...&lt;/code&gt; and called it done. That caught compilation errors and test failures, but not data races, not staticcheck warnings, not formatting drift, not license issues, not security vulnerabilities in dependencies. Each of those failure categories has caused a production incident at some point. The current version catches most of them before the PR is merged.&lt;/p&gt;</description></item><item><title>Lesson 4: Config Injection — Environment variables, flags, files — in that order</title><link>https://atharva.page/post/go/go-deploy-config-injection/</link><pubDate>Sat, 02 Nov 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-deploy-config-injection/</guid><description>&lt;p&gt;Configuration management is one of those topics that seems solved until you maintain a service that runs in four different environments, has twenty configurable parameters, and needs its secrets rotated without a redeployment. I&amp;rsquo;ve gone through several evolutionary stages on this: hardcoded values (the naive phase), a single &lt;code&gt;config.yaml&lt;/code&gt; file, then twelve &lt;code&gt;config-{env}.yaml&lt;/code&gt; files, and finally landing on what the 12-factor app methodology describes — environment variables as the source of truth for deployment-specific configuration.&lt;/p&gt;</description></item><item><title>Lesson 3: Health Checks — Readiness vs liveness, and why both matter</title><link>https://atharva.page/post/go/go-deploy-health-checks/</link><pubDate>Sun, 22 Sep 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-deploy-health-checks/</guid><description>&lt;p&gt;I&amp;rsquo;ve seen two types of health check implementations in production: the ones that always return 200 OK, and the ones that actually check something. The first kind is theater — Kubernetes thinks the pod is healthy and routes traffic to it, but the pod is actually deadlocked or its database connection pool is exhausted. The second kind is what saves you at 3am.&lt;/p&gt;
&lt;p&gt;Health checks are Kubernetes&amp;rsquo;s mechanism for deciding when a pod is ready to receive traffic and when it needs to be restarted. Get them right and Kubernetes becomes a genuinely reliable self-healing system. Get them wrong and you have an elaborate system that sends traffic to broken pods and restarts healthy ones.&lt;/p&gt;</description></item><item><title>Lesson 2: Docker for Go — Multi-stage builds that produce tiny images</title><link>https://atharva.page/post/go/go-deploy-docker/</link><pubDate>Sat, 10 Aug 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-deploy-docker/</guid><description>&lt;p&gt;The first Dockerfile I wrote for a Go service produced a 1.2 GB image. It was based on &lt;code&gt;golang:1.22&lt;/code&gt;, which includes the full Go toolchain, build cache, test infrastructure, and a complete Debian system. The compiled binary was 18 MB. I was shipping 1.2 GB to run 18 MB.&lt;/p&gt;
&lt;p&gt;The second version, after learning about multi-stage builds, produced a 22 MB image. Same binary. No compiler. No package manager. No shell. Just the binary and the absolute minimum needed to run it. The difference matters not just for storage: smaller images pull faster, have smaller attack surfaces, and fail more obviously when a required file is missing.&lt;/p&gt;</description></item><item><title>Lesson 1: Static Binaries — One file, zero dependencies, deploy anywhere</title><link>https://atharva.page/post/go/go-deploy-static-binaries/</link><pubDate>Sun, 30 Jun 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-deploy-static-binaries/</guid><description>&lt;p&gt;The first time I deployed a Go binary to a production server, I was braced for the usual ceremony: SSH in, install the right runtime version, copy over config files, fiddle with symlinks. Instead, I ran &lt;code&gt;scp&lt;/code&gt; and then the binary, and it just worked. No installation. No dependency hell. No &amp;ldquo;but it works on my machine.&amp;rdquo; The binary contained everything it needed.&lt;/p&gt;
&lt;p&gt;This is Go&amp;rsquo;s single greatest operational advantage: the compiler produces a self-contained executable. Understanding exactly how this works — and how it can break — makes the difference between a deploy that takes five minutes and one that takes an afternoon.&lt;/p&gt;</description></item></channel></rss>