Who it is for
You write Go, and your service runs on a single Linux box under systemd. Maybe it is an API with a background worker and SQLite, maybe a webhook receiver, maybe an internal tool the whole company leans on. It works. What you want to know is what happens at the edges: a deploy while requests are in flight, a client that never finishes sending headers, a container with too little memory, a crash at 3 a.m.
This is not a Go tutorial. It assumes you know the language and have shipped something. If you are looking for an introduction to goroutines, the well-known Go books are a better start.
What is inside
A book of 16 chapters, about 2,000 lines, and a small reference service that every Go listing in the book comes from. A script checks that each of the 60 code blocks matches the tested source, so nothing in the text is a snippet nobody ran.
| # | Chapter |
|---|---|
| 1 | Project layout for a small service |
| 2 | Configuration from the environment: validate everything, fail fast |
| 3 | Structured logging with log/slog, without leaking secrets |
| 4 | HTTP server timeouts |
| 5 | Graceful shutdown |
| 6 | Health versus readiness |
| 7 | Context, cancellation and goroutine-leak checks |
| 8 | Database access with SQLite: WAL, one writer, migrations |
| 9 | Errors and panics |
| 10 | Metrics and pprof, safely |
| 11 | Testing |
| 12 | Building: static, reproducible binaries |
| 13 | systemd: unit, hardening, restart, stop |
| 14 | Memory: what a Go process does under pressure |
| 15 | Deploying with release folders |
| 16 | Common production bugs, each with a check that catches it |
You also get the reference service (standard library plus one SQLite driver), a load generator, a small memory experiment, the systemd unit and a Dockerfile, three checklists, and a Docker harness that re-runs every check from a clean container. The output of our last full run is included so you can compare.
What the tests show
The chapters on shutdown and systemd are backed by tests that start the real binary and send real signals.
| What we did | What happened |
|---|---|
| SIGTERM with a 1.5 s job in flight | New connections were refused after 11 ms, the in-flight request finished after 1.399 s, the process exited with code 0 after 1.669 s |
| SIGTERM to an idle service | Exit in 2 ms |
| SIGTERM with a 9 s job and a 500 ms shutdown limit | Exit after 515 ms with code 1, job put back in the queue |
| Client that sends headers slowly, 300 ms header timeout | Connection cut after 295 ms |
systemctl stop with a 3 s request in flight | Request returned 200, stop took 3.19 s |
kill -9 under systemd | Healthy again 2.10 s later |
| Two builds from different directories | Byte-identical binaries (and different ones when -trimpath is left out, as a control) |
The systemd unit ships with hardening options, and we read the result back from the running process rather than trusting the file: no capabilities, a seccomp filter, read-only /usr and /etc, a memory limit. systemd-analyze security gave the unit an exposure score of 1.4.
Throughput and memory
Setup: Docker Desktop on an Apple Silicon laptop, 8 CPUs, the service, the load generator and the database in one container, 16 clients, three runs of 8 seconds per mode.
| Mode | Requests per second (3 runs) |
|---|---|
| Writes | 9,190 to 11,189 (p99 8.4 to 11.7 ms) |
| Reads | 13,541 to 18,421 |
| Health endpoint | 23,032 to 26,757 |
Resident memory was 13 MiB at idle and 23 MiB after the load run, with a peak of 25 MiB.
GOMEMLIMIT, measured
Chapter 14 does not tell you to set GOMEMLIMIT and move on. It runs a small program with a 40 MiB live heap inside a container capped at 80 MiB. Without a limit it was OOM-killed in 3 of 3 runs. With GOMEMLIMIT=56MiB it survived 3 of 3, at the price of roughly four times as many garbage collections. The book also explains why the limit is soft and what that means for sizing.
Requirements and format
Go 1.24 or newer. Docker only if you want to re-run the harness. Everything is Markdown, Go source, shell and config files: no PDF lock, no account, read it anywhere.
FAQ
Is this a book for beginners?
No. It assumes you already write Go. It is about the production side: what the process does when something goes wrong.
Does it cover Postgres?
The reference service and its tests use SQLite. The database chapter also gives guidance for Postgres.
How is it different from the Go Webhook Receiver Starter?
The starter is a small product you run. The handbook is the reasoning and the tests behind a well-behaved Go service, using a different reference service.
Can I use the code in my own projects?
Yes, in your own projects and for your own or your employer's servers. Please do not resell or redistribute the book or the kit.
Changelog
- 0.1.0: first release.
Read more
- Graceful shutdown for a Go HTTP server under systemd
- GOMEMLIMIT in an 80 MB container, with the numbers
- Go SQLite reads returning 404 for rows that exist
- log/slog setup for a small Go service on journald
- Go HTTP server timeouts that survive slow and stuck clients
Get it
Payment, tax and invoices are handled by Polar as merchant of record. You receive a download link after checkout.