Skip to content

Deployment

The framework repo ships reference material under deploy/: a multi-stage distroless Dockerfile (proto regenerated inside the image with pinned tools) and a docker-compose with otelcol, Prometheus, Grafana and PgBouncer configuration. The three items below are deliberately left to the operator — the framework does not do them for you.

A new scaffold carries local development configuration. Before the first git add, Docker build, or CI handoff, verify that every secret is externalized. Do not leave a usable JWT signing secret in version control or an image layer.

  • Supply real secrets through a secret manager, mounted file, or runtime environment using GORTEXA_AUTH__JWT_SECRET.
  • Keep only an unusable placeholder or a secret-free example configuration in the repository; ignore local overrides.
  • Make sure both the Docker build context and runtime image exclude local secret configuration, and verify the image starts only with runtime injection.
  • Plan rotation, issuer, and audience; services sharing a secret should use different audiences.

This is a deployment gate, not optional later work. Likewise, the scaffold’s in-memory resource is only a demo; establish real storage, cache, and MQ lifecycle before treating readiness as meaningful.

The service port is cleartext h2c; the server does no TLS itself. Bearer tokens crossing this port are unencrypted. Put the service behind a reverse proxy, service mesh or load balancer that terminates TLS. Do not expose the h2c port to the public internet.

/readyz only reflects that the process is alive until you register real checks with app.Health().Register(...):

app.Health().Register("postgres", func(ctx context.Context) health.State {
if err := pool.Ping(ctx); err != nil {
return health.Unhealthy
}
return health.Healthy
})

Health probes can target a private ops port, separate from the public one: kernel.WithAdminListener(addr) serves only /healthz and /readyz on a separate plain-HTTP port, so an orchestrator can probe health without reaching the public h2c port (app.AdminAddr() reports the bound address). To expose pprof or a custom ops handler on a separate port, use kernel.WithExtraListener(lis, h). Both are opt-in; the main h2c port’s single-port multiplexing is unchanged.

Each check runs under a 5-second timeout, so a single slow dependency cannot stall the query.

The framework does authentication only: it verifies the JWT signature and puts the claims into the context. Who may do what (roles, permission policy) is decided by the application, in handlers or its own interceptor:

claims, ok := auth.ClaimsFrom(ctx)
if !ok || !slices.Contains(claims.Roles, "admin") {
// reject
}

On SIGTERM the server drains in-flight requests within server.shutdown_timeout (default 20s) before exiting. Interruption under authenticated load is verified by example 05.

  • Keep server.reflection at false: do not expose the service structure.
  • observ.otlp_insecure must be false for any remote collector.
  • Real secrets use runtime injection and must not exist in Git or an image layer — see Configuration.