Start and stop an application
Start the service, prepare required state, run process work, and stop cleanly.
app.start() is the boundary between an application's declarations and its
running process. It connects NATS, prepares shared runtime state, runs
application lifecycle hooks, verifies declared Sync resources, then registers
the live service and returns the Bun-compatible server definition.
The returned server handles /_cloud/ready before the application router.
Because Bun does not start serving until the awaited definition is returned,
the endpoint becomes reachable only after setup, start, Sync readiness,
and registration have completed. Use it for direct container or pod readiness checks.
Pass the Hono fetch handler:
export default await app.start({
fetch: router.fetch,
});Set the start options
The complete shape is:
export default await app.start({
fetch: router.fetch,
openapi: apiRoutes,
lifecycle: {
setup,
start,
stop,
},
capabilities: inventoryCapabilities,
help: inventoryHelp,
port: 3000,
skipSetup: false,
});| Option | Required | Default | Meaning |
|---|---|---|---|
fetch |
Yes | — | Application request handler |
openapi |
No | — | Bare router used to generate OpenAPI |
lifecycle |
No | — | setup, start, and stop hooks |
capabilities |
No | — | Versioned Types, Queries, and Actions |
help |
No | — | App-owned product Help registered with the live service |
port |
No | 3000 |
Internal Bun server port |
skipSetup |
No | false |
Skip the setup hook |
See App capabilities for the executable contract and In-product Help for the Help definition.
OpenAPI also needs the document path declared in defineApp(). See
Typed HTTP APIs.
Lifecycle hooks
| Hook | Use |
|---|---|
setup |
Prepare required state before the server definition is returned |
start |
Start workers, schedulers, and subscriptions |
stop |
Release process resources |
export default await app.start({
fetch: router.fetch,
lifecycle: {
setup: async () => {
await migrate();
},
start: async () => {
await stockWorker.start();
},
stop: async () => {
await stockWorker.stop();
},
},
});Keep HTTP middleware and route mounting outside the lifecycle.
Prepare state in setup
setup runs on every normal start. Database migrations belong here:
setup: async () => {
await migrate();
},Make setup work safe to run more than once. See Migrations and transactions.
skipSetup: true prevents the hook from running. Use it only when another
controlled process already prepared the required state. It must not hide a
failing migration.
Clean up a failed start
Cloud calls the application's stop hook if setup, start, resource
readiness, or registration fails. It then releases notification registration,
watchers, registry entries, and its NATS connection. The application is not
advertised before its hooks and declared Sync resources are ready. Database
writes and external effects are not rolled back.
Make stop safe after partial startup. When a hook has several steps, it can
also release completed steps locally:
start: async () => {
await importWorker.start();
try {
await reconciliationWorker.start();
} catch (error) {
await importWorker.stop();
throw error;
}
},See Lifecycle background work.
Stop in reverse order
Cloud calls stop for SIGTERM and SIGINT.
Stop resources in the reverse order from startup:
stop: async () => {
await reconciliationWorker.stop();
await importWorker.stop();
},After the hook, Cloud removes notification registration. It then stops the runtime watcher and removes the application registry entry. Sync workers drain before the NATS connection closes. Stop and drain application workers before releasing the database clients or other dependencies they use.
See Scaling and shutdown for deployment behavior and shutdown deadlines.
Lifecycle context
Each hook receives:
setup: async (cloud) => {
const log = cloud.logger("inventory");
log.info("Preparing inventory");
const timezone = await cloud.settings.get<string>("app.timezone");
const applications = cloud.runtime.apps;
},The context contains:
logger(source)for structured application logs;- asynchronous
settings.get()andsettings.set(); - a snapshot of registered applications;
sync, the process-owned Sync instance for distributed primitives.
Request handlers should use request middleware instead. See Settings and Request middleware.
Startup order
Cloud starts the application in this order:
- require the shared
APP_SECRETand connect the process-owned NATS instance; - declare registry resources and start the runtime watcher;
- run
setup, unless skipped; - register notification definitions;
- load the settings cache;
- run
start; - await readiness of all Sync resources declared during startup;
- publish Help and capability records, then advertise the application;
- return the server definition.
Every application container needs the same non-empty APP_SECRET. Startup
fails before registration when it is missing.
The returned object contains port, development, and fetch. Applications
that expose Bun WebSockets add their handlers to that result:
const result = await app.start({ fetch: router.fetch });
export default {
...result,
websocket,
};