Runtime
The part that actually runs your modules: a small state machine, an ordered start, and a shutdown that waits for work to finish.
Overview
The runtime is the object that owns your application while it is alive. It loads modules, runs their hooks in order, listens for termination signals, and unwinds everything when it is time to stop.
You rarely build one by hand. createApplication from @zudojs/core creates a runtime for you and hands you an Application wrapped around it.
TWO RUNTIMES, TWO PACKAGES
Zudo ships two runtimes and they are not the same object. @zudojs/core has the one createApplication uses. @zudojs/runtime is a separate, standalone package with its own state names and extra readiness and health features. Everything up to the "Standalone Runtime" section below describes the @zudojs/core one.
For everything in the next four sections:
These docs follow the framework source. If an export shown here is missing from the version you installed, update to the latest @zudojs release.
Runtime States
A state machine is a rule about which step may follow which. The runtime has six states, and moving to a state that is not allowed from the current one throws InvalidRuntimeTransitionError instead of quietly doing something strange.
created │ start() ▼ bootstrapping ──── error ────▶ failed │ │ ▼ │ ready │ │ stop() │ stop() ▼ ▼ stopping ─────────────────────▶ stopped
| State | What it means |
|---|---|
created | Built, nothing has run. This is the only state you can start from. |
bootstrapping | Loading modules and running onInitialize then onReady. |
ready | Every module is up. The application is serving. |
stopping | Running onShutdown then onDestroy, in reverse order. |
stopped | Finished. Terminal — this runtime cannot start again. |
failed | Something threw. Terminal, but you can still call stop() to clean up. |
The names come from the RuntimeState enum, which is exported so you can compare against it rather than typing strings.
APPLICATION STATE IS SEPARATE
app.state is not the same value. It is one of created, initializing, initialized, starting, running, stopping, stopped, failed. The application wraps the runtime, so running on the outside corresponds to ready on the inside.
Startup
Startup is four steps: work out the module order, build each module from its factory, run every onInitialize, then run every onReady. Only after the last onReady does the runtime become ready.
A complete program that starts, reports, and stops:
What you should see:
Notice that api was listed first but database ran first. Declaration order does not matter; the dependencies field does.
Startup behaviour is tunable through runtime.startup:
| Option | What it does | Default |
|---|---|---|
autoLoadModules | Build module instances from their factories | true |
autoInitializeModules | Run onInitialize during bootstrap | true |
autoStartModules | Run onReady during bootstrap | true |
continueOnInitializeError | Keep going when a module fails to initialize | false |
continueOnStartError | Keep going when a module fails in onReady | false |
timeoutMs | Give up on bootstrap after this long; 0 means never | 0 |
WATCH OUT
With continueOnInitializeError: true the runtime still reaches ready, but the bootstrap result reports success: false and lists the failures. A half-started application that looks healthy is worse than one that refuses to start, so leave this off unless you check the result.
Graceful Shutdown
Graceful means the application stops accepting new work, finishes what it already accepted, and only then lets go of its resources. The opposite is being killed mid-request.
Shutdown walks the modules in the exact reverse of startup order. Every module gets onShutdown first; once they are all done, every module gets onDestroy.
| Option | What it does | Default |
|---|---|---|
autoStopModules | Run onShutdown | true |
autoDestroyModules | Run onDestroy | true |
continueOnStopError | Keep stopping the rest when one module throws | true |
continueOnDestroyError | Keep destroying the rest when one module throws | true |
timeoutMs | Abandon shutdown after this long; 0 means never | 30000 |
The two continueOn defaults are true on purpose. During shutdown, one broken module must not strand every other module's cleanup.
Shortening the shutdown budget to five seconds:
app.stop() stops the runtime but leaves the application able to start again. app.shutdown() stops it and releases the lifecycle for good — after that, a restart is not possible.
Signal Handling
A signal is how an operating system asks a process to stop. Pressing Ctrl-C sends SIGINT. Docker and Kubernetes send SIGTERM before they eventually force a kill.
The runtime installs handlers when it starts and removes them when it stops, so a signal turns into a graceful shutdown instead of an abrupt exit.
| Option | What it does | Default |
|---|---|---|
handleSigint | Stop gracefully on Ctrl-C | true |
handleSigterm | Stop gracefully on SIGTERM | true |
handleSighup | Stop gracefully on SIGHUP | false |
handleUncaughtException | Mark failed and stop on an uncaught error | true |
handleUnhandledRejection | Mark failed and stop on an unhandled rejection | true |
forceExitOnSecondSignal | Exit immediately if a second signal arrives mid-stop | false |
forceExitCode | Exit code used by the force exit above | 1 |
A container-friendly setup — impatient operators get a hard exit on the second Ctrl-C:
DANGER
The runtime never calls process.exit() unless forceExitOnSecondSignal is switched on. That is deliberate: exiting the process is the host application's decision, not the framework's. If your process lingers after a signal, something else is still holding an open handle.
The Standalone Runtime
@zudojs/runtime is a different package with a different runtime. It exists for hosts that assemble the pieces themselves rather than going through createApplication.
Its createRuntime takes explicit dependencies — a map of modules, a logger, a container and an event bus — and returns a Runtime.
Two things it adds over the core runtime:
- Readiness checks.
registerReadinessCheck(name, check)adds a named check;runReadinessChecks()re-runs them all;runtime.readinessreports each result. A newly registered check starts out failing until it is first evaluated, so registering one on a live runtime moves it to degraded until it passes. - Health.
runtime.healthandruntime.statusgive a structured report suitable for a health endpoint.
DIFFERENT STATE NAMES
This runtime's states are created, initializing, initialized, starting, running, stopping, stopped and failed — plain strings, not the core RuntimeState enum. Do not compare values from one runtime against the other.
The testing helper testRuntime is deliberately not exported from the package root. Import it from @zudojs/runtime/testing.