Build And Deploy

Control Plane

Expose liveness, readiness, metrics, and Go diagnostics outside application routes.

By Guillermo Alvarez - Published Updated

Enable probes in app config

Add an empty control-plane config when the app should expose framework-owned operational endpoints on the same handler as the app:

return lazyapp.New(lazyapp.Config{
    Name:         "sample_app",
    Drawer:       Draw,
    Public:       app.Public,
    Views:        app.Views,
    Context:      Context,
    ControlPlane: lazycontrolplane.Config{},
})

The empty config exposes:

GET /livez
GET /readyz

Control-plane routes are checked before application routes. If the app also draws /livez, the control-plane route wins.

Use a separate address

ListenAndServe activates the default control plane when CONTROL_PLANE_ADDR is set:

ADDR=0.0.0.0:8080 CONTROL_PLANE_ADDR=127.0.0.1:9090 ./app

When CONTROL_PLANE_ADDR differs from the app address, ListenAndServe starts a second HTTP server for the control plane. The public app server then serves only application traffic.

If CONTROL_PLANE_ADDR matches ADDR, PORT, or the default :3000, GoLazy mounts the control plane into the app server instead of trying to bind the same port twice:

CONTROL_PLANE_ADDR=3000 ./app

Add readiness checks

Readiness checks receive the request context and return an error when the app is not ready:

ControlPlane: lazycontrolplane.Config{
    Readiness: []lazycontrolplane.ReadinessCheck{{
        Name: "database",
        Check: func(ctx context.Context) error {
            return db.PingContext(ctx)
        },
    }},
},

/readyz returns 503 Service Unavailable when a check fails.

Attach metrics and pprof

Metrics are opt-in:

ControlPlane: lazycontrolplane.Config{
    Metrics: metricsHandler,
},

That registers GET /metrics. GoLazy does not add a Prometheus dependency for you; pass a handler from the exporter you choose.

Go diagnostics are also opt-in:

ControlPlane: lazycontrolplane.Config{
    Pprof: true,
},

That registers /debug/pprof/ and the standard pprof subpaths. Prefer a separate CONTROL_PLANE_ADDR before enabling diagnostics in production.

Serve manually

If you need a custom server lifecycle, instantiate the control plane yourself:

plane := lazycontrolplane.New(lazycontrolplane.Config{})
server := &http.Server{
    Addr:    "127.0.0.1:9090",
    Handler: plane,
}

When you manage the server manually, lazyapp.ListenAndServe is not responsible for starting that control-plane server.