Go on One VPS: Production Handbook

How to write a Go service you can leave running on one server: shutdown, timeouts, memory and systemd, with a working reference service behind every listing.

Buy on Polar: $39 One-time purchase · v0.1.0 · instant download via Polar

Go on One VPS: Production Handbook cover
Output from a real test run, not a mock-up.

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
1Project layout for a small service
2Configuration from the environment: validate everything, fail fast
3Structured logging with log/slog, without leaking secrets
4HTTP server timeouts
5Graceful shutdown
6Health versus readiness
7Context, cancellation and goroutine-leak checks
8Database access with SQLite: WAL, one writer, migrations
9Errors and panics
10Metrics and pprof, safely
11Testing
12Building: static, reproducible binaries
13systemd: unit, hardening, restart, stop
14Memory: what a Go process does under pressure
15Deploying with release folders
16Common 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 didWhat happened
SIGTERM with a 1.5 s job in flightNew 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 serviceExit in 2 ms
SIGTERM with a 9 s job and a 500 ms shutdown limitExit after 515 ms with code 1, job put back in the queue
Client that sends headers slowly, 300 ms header timeoutConnection cut after 295 ms
systemctl stop with a 3 s request in flightRequest returned 200, stop took 3.19 s
kill -9 under systemdHealthy again 2.10 s later
Two builds from different directoriesByte-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.

ModeRequests per second (3 runs)
Writes9,190 to 11,189 (p99 8.4 to 11.7 ms)
Reads13,541 to 18,421
Health endpoint23,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

Get it

Buy on Polar: $39

Payment, tax and invoices are handled by Polar as merchant of record. You receive a download link after checkout.