Go cookbook · Advanced
Profile a running service with pprof
CPU or memory usage climbs in production and you need to see where.
Approach
Register the pprof handlers on a separate, internal-only server (not the public mux), then capture CPU, heap and
goroutine profiles with go tool pprof.
Solution
package main
import (
"log"
"net/http"
"net/http/pprof"
"time"
)
func startDebugServer() {
mux := http.NewServeMux()
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
srv := &http.Server{Addr: "127.0.0.1:6060", Handler: mux, ReadHeaderTimeout: 5 * time.Second}
go func() { log.Println(srv.ListenAndServe()) }()
}
func main() {
startDebugServer()
// ... start the public server on its own mux ...
select {}
}
# 30-second CPU profile, opened in a browser UI
go tool pprof -http=:8081 http://127.0.0.1:6060/debug/pprof/profile?seconds=30
# Heap in use, and goroutine dump to spot leaks
go tool pprof -http=:8082 http://127.0.0.1:6060/debug/pprof/heap
curl -s 'http://127.0.0.1:6060/debug/pprof/goroutine?debug=2' | head -50
Pitfalls
- Importing
_ "net/http/pprof", which registers handlers onhttp.DefaultServeMux, then serving that mux publicly. - Profiling for a few seconds during idle periods; capture under real load.
- Ignoring goroutine counts; steadily growing counts usually mean a leak.
Variations
- Continuous profiling services (Pyroscope, Grafana) collect profiles over time.
- Use the execution tracer (
/debug/pprof/trace) for latency and scheduling issues.
Further reading: net/http/pprof
Reference: https://pkg.go.dev/net/http/pprof
More Go recipes
- Benchmark code with testing.B.Loop
- Build a small, secure container image for a Go service
- Decode and validate JSON request bodies safely
- Embed static files and templates in the binary
- Load and validate configuration from environment variables
- Log structured JSON with slog and redact secrets
- Process jobs concurrently with a bounded worker pool
- Rate limit requests per client