Canary Deployments in Go: Safe Deployment Without Fear

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Canary Deployments with Go: How to Release Updates Without Fear and Risk



Imagine: you've spent years building the architecture, writing tests, setting up monitoring. And then — the release. One click, and the new version goes to production. Your heart is pounding, your fingers are shaking. Sound familiar? What if I told you that you can deploy a new version to just 5% of users, observe the metrics, and only then roll it out to everyone? Without downtime, without panic, with full control.



This is called a canary deployment. The name comes from miners who took canaries with them — the bird would die from gas before the humans, warning of danger. In the DevOps world, these "canaries" are the first 5-10% of users who receive the new version. If something goes wrong — we roll back without anyone noticing.



In this article, we'll break down how to implement canary deployment in Go with real code examples, a load balancer, and metrics. Let's go!



Why Go for Canary Deployment?



Go is a language built for microservices. It compiles to a static binary, requires no dependencies on the server, and scales easily. But most importantly, Go has built-in concurrency support and excellent libraries for HTTP servers, making it an ideal candidate for implementing smart traffic routing.



  • Performance — A Go server handles thousands of requests per second on a single core.
  • Simplicity of deployment — One binary, no virtual machines or interpreters.
  • Rich ecosystem — Packages for metrics (Prometheus), tracing (OpenTelemetry), and load balancing.


If you're already writing in Go and want to automate deployment — the canary strategy will become your best friend.



Canary Deployment Architecture in Go



Let's imagine we have a microservice in Go that processes user requests. Usually, we deploy a new version to all instances at once. With the canary approach, we run the new version in parallel with the old one, but only direct a portion of traffic to it.



Here's a simplified diagram:



  1. Stable version (v1) — runs on 90% of instances.
  2. Canary version (v2) — runs on 10% of instances.
  3. Load balancer — distributes traffic proportionally.
  4. Monitoring — compares metrics (latency, errors, throughput).
  5. Manual or automatic decision — roll out v2 to 100% or roll back.


This can be implemented in various ways: via Kubernetes (Service Mesh), via Nginx with custom weights, or by writing your own load balancer in Go. We'll go with the third approach — it gives full control and is great for learning.



Writing a Canary Traffic Load Balancer in Go



Let's write a simple reverse-proxy that will route requests to two backends: stable and canary. We'll set the canary version's weight via a config or environment variable.



package main

import ( "log" "math/rand" "net/http" "net/http/httputil" "net/url" "os" "strconv")

type CanaryProxy struct { stable *httputil.ReverseProxy canary *httputil.ReverseProxy canaryWeight int // percentage of traffic to canary (0-100)}

func NewCanaryProxy(stableURL, canaryURL string, weight int) *CanaryProxy { stableTarget, _ := url.Parse(stableURL) canaryTarget, _ := url.Parse(canaryURL)

return &CanaryProxy{ stable: httputil.NewSingleHostReverseProxy(stableTarget), canary: httputil.NewSingleHostReverseProxy(canaryTarget), canaryWeight: weight, }}

func (p *CanaryProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Decision based on a random number if rand.Intn(100) <

Blogs

Book Recommendations