Go: Your New Go-To DevOps Language

ARC DevOps Hour

Milan Malfait

2025-12-11

Introduction

  • My Go journey
    • Fiddling around with it and used it for Advent of Code 2024
    • Joined the ARC TRE team January 2025
  • What we’ll cover today:
    • Go’s unique characteristics
    • Why it’s ideal for DevOps
    • Real-world examples

What Makes Go Different

  • Created at Google (2009) as a modern simple alternative to C
  • Core philosophy: simplicity, readability, and pragmatism
  • The “boring is good” principle - stability over novelty
  • Key features overview: compiled, statically typed, garbage collected, built-in concurrency
  • Compilation speed: Go compiles so fast it feels interpreted

Why Go?

  • Designed for modern software engineering challenges
    • Performance
    • Scalability
    • Security
    • Reliability
  • Lean syntax
  • Strong standard library
  • Built-in tools

Go’s Minimal Syntax

  • Only 25 keywords (Python: 35, Java: 68, C++23: 92)
  • Strongly idiomatic - one way to do things
  • No classes, no inheritance - favour composition
  • Define behaviour through interfaces
  • Explicit error handling - no exceptions

A complete web server in 15 lines

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

All You Need Is STD

Go’s standard library includes everything you need1:

  • os, io, fmt
  • Logging: log, log/slog
  • Networking: net/http
  • Databases: database/sql
  • Data: encoding/json, encoding/xml, encoding/csv
  • … and much more

Error Handling: Explicit is Better

func thingThatMightMessUp() (string, error)

result, err := thingThatMightMessUp()
if err != nil {
    return fmt.Errorf("failed to do the thing: %w", err)
}
  • No hidden control flow (no exceptions)
  • Errors are values - error is just another type
  • Forces you to think about failure cases

The joy of having built-in tools

# Formatting
go fmt

# Testing
go test

# Compile & execute
go run

# Dependency management
go get
go mod

# Docs generator
go doc

The joy of having built-in tools

What About Performance?

Go has excellent performance right out of the box. By design, there are no knobs or levers that you can use to squeeze more performance out of Go

Steve Francia (Go Team)

  • Compiled
  • Garbage collected → memory safe but pays some performance cost

What About Performance?

Go has excellent performance right out of the box. By design, there are no knobs or levers that you can use to squeeze more performance out of Go

Steve Francia (Go Team)

Speed comparison of programming languages

Go’s DevOps Superpowers

  • Fast build times & cross-compilation
  • Small static binaries
  • Low memory footprint and garbage collection
  • Efficient concurrency
  • Lends itself well to microservices architecture

Size Matters

Typical image sizes of an API written in Python (with Flask) or Go:

Image Base Final Size Reduction
python-api python:3.14-slim ~160MB baseline
go-api golang:1.25 ~950MB +9x larger (includes runtime)
go-api-multistage scratch ~8MB 20x smaller

Concurrency with Goroutines

Concurrency with Goroutines

  • Goroutines: lightweight threads managed by Go runtime
  • Simple syntax: just add go before a function call
  • Channels: safe communication between goroutines
  • Perfect for DevOps tasks:
    • Parallel API calls to multiple services
    • Concurrent log processing
    • Non-blocking I/O operations

Goroutines in Action

A non-concurrent example

func checkHealth(url string, results *[]string)

func main() {
    services := []string{"http://api1.com", "http://api2.com", "http://api3.com"}
    results := make([]string, 0, len(services))

    for _, service := range services {
        checkHealth(service, &results) // Run health checks sequentially
    }

    for _, res := range results {
        fmt.Println(res)
    }
}

Goroutines in Action

Making it concurrent

func checkHealth(url string, results chan<- string)

func main() {
    services := []string{"http://api1.com", "http://api2.com", "http://api3.com"}
    results := make(chan string, len(services))

    for _, service := range services {
        go checkHealth(service, results) // Run health checks concurrently
    }

    for range services {
        fmt.Println(<-results) // Collect results
    }
}

Goroutines in Action

Making it concurrent

func checkHealth(url string, results chan<- string)

func main() {
    services := []string{"http://api1.com", "http://api2.com", "http://api3.com"}
    results := make(chan string, len(services))

    for _, service := range services {
        go checkHealth(service, results)
    }

    for range services {
        fmt.Println(<-results)
    }
}

Every Go Program is a Goroutine


main.go
func main() {
    panic("AAAAHHH!")
}

Every Go Program is a Goroutine


main.go
func main() {
    panic("AAAAHHH!")
}


$ go run ./main.go
panic: AAAAHHH!

goroutine 1 [running]:
main.main()
    /Users/milan/talks/2025-12-11-DevOpsHour-Go/examples/panic/main.go:4 +0x2c
exit status 2

Go in the Wild

  • Container orchestration: Docker, Kubernetes, containerd
  • Infrastructure: Terraform, Packer, Consul, Vault
  • Monitoring: Prometheus, Grafana, Telegraf
  • CLI tools: GitHub CLI, kubectl, helm
  • Cloud: Many AWS/GCP/Azure tools
  • The ARC TRE!

Why Did ARC TRE Choose Go?

  • Simple concurrency
  • Small binaries → small containers
  • Great Standard Library
  • Good Tooling
  • Fast Compilation → enables hot reloading
  • Safety: recoverable panics and memory safety
  • Good external libraries: AWS SDK, Kubernetes client
  • Tom Y was learning it and liked it

How To Get Going

Books:

More code examples available at https://github.com/milanmlft/talks/tree/main/2025-12-11-DevOpsHour-Go/examples