A small HTTP server wrapper for Go with Kubernetes-native health probes and
graceful shutdown. It wraps net/http.Server, adds /livez and /readyz
endpoints, and drains connections cleanly on SIGINT/SIGTERM.
The server is written in Lisette (in src/)
and compiled to Go. The generated Go package lives at the repo root, so other
Go projects use it like any normal library.
Note: this library does not handle routing. Bring your own router (
chi,gorilla/mux,http.ServeMux, …) and pass it viaWithHandler.
go get github.com/banan-tech/httpserverpackage main
import (
"net/http"
"github.com/banan-tech/httpserver"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World"))
})
srv := httpserver.New([]httpserver.ServerOption{
httpserver.WithHandler(mux),
httpserver.WithAddr(":8080"),
})
// Run blocks until SIGINT/SIGTERM, then shuts down gracefully.
if err := srv.Run(); err != nil {
panic(err)
}
}Registered automatically unless WithoutDefaultProbes() is used. The probe
paths default to /livez and /readyz and are configurable via
WithLivenessPath / WithReadinessPath. Probes are mounted directly on the
internal mux, so they never pass through (and are never logged by) the handler
given to WithHandler:
| Path | Purpose |
|---|---|
/livez |
Liveness — static 200 while the process can serve. No dependency checks (restart only). |
/readyz |
Readiness — 200 when ready and all checks pass; 503 while shutting down or on failure. |
/_metrics |
Only if WithMetricsHandler is set (e.g. Prometheus). |
/ |
Your handler, if provided via WithHandler. |
A failing readiness check returns 503 with a JSON body naming the check:
{ "failed_check": "db", "error": "connection refused" }Pass any of these to New:
| Option | Default | Description |
|---|---|---|
WithAddr(addr) |
:8080 |
Listen address. |
WithPortFromEnv() |
— | Use :$PORT if PORT is set (applied before options). |
WithHandler(h) |
— | Root handler mounted at /. |
WithMetricsHandler(h) |
— | Handler mounted at /_metrics. |
WithReadinessCheck(name, fn) |
— | Register a named dependency check for /readyz (call once per dependency). |
WithoutDefaultProbes() |
off | Disable the built-in liveness and readiness probes. |
WithLivenessPath(path) |
/livez |
Path the liveness probe is mounted at. |
WithReadinessPath(path) |
/readyz |
Path the readiness probe is mounted at. |
WithLogger(l) |
JSON | Structured *slog.Logger. |
WithReadHeaderTimeout(d) |
5s |
Header read deadline (Slowloris protection). |
WithReadTimeout(d) |
15s |
Full request read deadline. |
WithWriteTimeout(d) |
70s |
Response write deadline. |
WithIdleTimeout(d) |
90s |
Keep-alive idle timeout. |
WithShutdownTimeout(d) |
15s |
Graceful drain deadline. |
WithShutdownHook(fn) |
— | Run during shutdown, after connections drain. |
srv := httpserver.New([]httpserver.ServerOption{
httpserver.WithHandler(mux),
httpserver.WithReadinessCheck("db", func(ctx context.Context) error {
return db.PingContext(ctx)
}),
})On SIGINT/SIGTERM, Run flips the readiness probe to 503 (so Kubernetes
stops routing new traffic), then drains in-flight requests within
ShutdownTimeout before running any shutdown hooks and exiting.
Keep
ShutdownTimeoutbelow the pod'sterminationGracePeriodSeconds, orSIGKILLfires before the drain finishes. Default:15s < 30s(the Kubernetes default).
| Method | Description |
|---|---|
New(opts) |
Build a server from options. |
Run() |
Serve, blocking until a signal, then shut down gracefully. |
Start() |
Serve, blocking (no signal handling). A clean stop returns nil. |
Shutdown(ctx) |
Drain connections within the shutdown timeout, then run hooks. |
Handler() |
The root http.Handler, handy for httptest. |
The source of truth is the Lisette code in src/. The root .go files are
generated — do not edit them by hand.
lis run # run the example (src/main.lis)
lis check # type-check
make emit # compile src/ to Go and emit the package into the repo root