Before the engine wakes, the omens must be read.
Before the machine sleeps, its spirit must be honored.
To kill a process without rite is to invite the wrath of the Machine God.— Tech-Priest Dominus Ferrox, Adeptus Mechanicus
The go-app-ignition package provides a simple lifecycle for applications.
When developing an application, two related tasks come up repeatedly:
- checking dependency readiness (readiness probes) before startup
- shutting down cleanly (graceful shutdown) when a stop signal is received.
The go-app-ignition package provides an implementation of this lifecycle:
- Runs a configured set of readiness probes (
ReadinessHandlers) before starting the applications. - Exposes the current readiness status via the
AppIgnition.Ready()method, which can be used in a health-check endpoint. - Starts the applications (
App) and waits for a termination signal: a readiness-probe error, an application error, an OS signal (SIGINT/SIGTERM), or an explicit request viaExitChan. - Performs a graceful shutdown of all applications, joining any resulting errors via
errors.Join.
A working example, verified by go test, is available in app_ignition_example_test.go.
A detailed description of the components is provided in the Architecture section.
go get -u github.com/AlTheOne/go-app-ignitionThe package consists of two files at the module root:
readiness.gohandles readiness-probe orchestrationapp_ignition.gohandles the application lifecycle.
The package's central struct, managing the lifecycle. Created via NewAppIgnition and exposes two methods:
Run(ctx context.Context, shutdownRelease context.CancelFunc) error— runs the readiness probes, starts the given applications, blocks waiting for a termination signal, and performs a graceful shutdown; returns the combined error ornil.Ready() bool— thread-safely returns the current readiness status; can be used, for example, in a health-check endpoint.
The []ReadinessHandler type — a set of readiness probes run concurrently before starting the applications, via errgroup. If any probe returns an error, the context of the remaining probes is cancelled, and AppIgnition.Run returns that error. A function that doesn't watch for context cancellation on its own can be adapted via ReadinessHandlerWrapper.
The interface every managed application must implement:
type App interface {
Run() error // blocking run
Shutdown() error // graceful shutdown
}Run() must block until the application either terminates on its own (in which case AppIgnition initiates shutdown of the other applications) or Shutdown() is called.
Besides the readiness probes and applications, NewAppIgnition accepts two channels through which the calling code controls shutdown:
| Parameter | Type | Purpose |
|---|---|---|
errExitChan |
chan error |
Reports a fatal error (e.g. from the application itself); the error is propagated as the result of Run. |
exitChan |
chan struct{} |
Requests a clean shutdown with no error, e.g. from an admin endpoint. |
SIGINT/SIGTERM signals are intercepted automatically inside Run and don't require a separate channel from the calling code.
sequenceDiagram
actor Caller
participant AI as AppIgnition
participant RH as ReadinessHandle
participant Apps as App(s)
participant OS as OS (syscalls)
Caller->>AI: Run(ctx, shutdownRelease)
alt the apps list is empty
AI-->>Caller: return nil
else the apps list is not empty
AI->>RH: ReadinessHandle(ctx, logger, handlers...)
RH-->>AI: err
alt err != nil
AI-->>Caller: return err
else readiness OK
AI->>AI: ready.Store(true)
loop for every App
AI->>Apps: go App.Run()
end
AI->>OS: signal.Notify(SIGINT, SIGTERM)
AI->>AI: runSignalListener(): select
par possible wake-up sources
OS-->>AI: SIGINT / SIGTERM
and
Apps-->>AI: Run() returned an error → errExitChan
and
Caller-->>AI: ctx.Done() (external cancellation)
and
Caller-->>AI: close(ExitChan)
end
AI->>AI: ready.Store(false)
loop for every App
AI->>Apps: Shutdown()
Apps-->>AI: err
end
AI->>OS: signal.Stop() (unsubscribe)
AI->>Caller: shutdownRelease()
AI-->>Caller: return err (errors.Join)
end
end
If you need extended functionality or a different approach, consider the packages below:
- skovtunenko/graterm - provides primitives to perform ordered GRAceful TERmination for Golang applications.
- appleboy/graceful - graceful shutdown package when a service is turned off by software function.
- meshapi/go-shutdown - lightweight Go package to handle process terminations and shutdowns gracefully.
- maxbolgarin/contem - zero-dependency drop-in context.Context replacement for graceful shutdown.