# Lifecycle

> Source: `src/content/docs/actors/lifecycle.mdx`
> Canonical URL: https://rivet.dev/docs/actors/lifecycle
> Description: Learn about actor lifecycle hooks for initialization, state management, and cleanup.

---
# Lifecycle

Actors follow a well-defined lifecycle with hooks at each stage. Understanding these hooks is essential for proper initialization, state management, and cleanup.

## Lifecycle

Actors transition through several states during their lifetime. Each transition triggers specific hooks that let you initialize resources, manage connections, and clean up state.

```
Loading ──Start──▶ Ready ──spawn driver──▶ Started
                                              │
     ┌────────────────────────────────────────┤
     │                                        │
     │ idle timer + can_sleep                 │ Destroy command
     ▼                                        ▼
  SleepGrace ─── grace window closes ──▶ Destroying
     │                                        │
     ▼                                        │
  SleepFinalize ──── stop sequence ───────────┤
                                              ▼
                                          Terminated
```

**On Create** (runs once per actor)

1. `onMigrate`
2. `createState`
3. `onCreate`
4. `createVars`
5. `onWake`
6. `run` (background, does not block)

**On Destroy**

1. `onDestroy`

**On Wake** (after sleep, restart, or crash)

1. `onMigrate`
2. `createVars`
3. `onWake`
4. `run` (background, does not block)

**On Sleep** (after idle period)

1. Wait for `run` to complete (with timeout)
2. `onSleep`

**On Connect** (per client)

1. `onBeforeConnect`
2. `createConnState`
3. `onConnect`

**On Disconnect** (per client)

1. `onDisconnect`

**On Inbound Action Invoke** (per action call)

1. Action handler executes

**On Inbound Queue Publish** (per `handle.send(...)`)

1. `queues.<name>.canPublish` (if defined)
2. Queue message is enqueued

**On Event Subscription Request** (per subscribe request)

1. `events.<name>.canSubscribe` (if defined)
2. Subscription is applied

## Lifecycle Hooks

Actor lifecycle hooks are defined as functions in the actor configuration.

### `state`

The `state` constant defines the initial state of the actor. See [state documentation](/docs/actors/state) for more information.

### `onMigrate`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

The `onMigrate` hook runs on every actor start, before `createState`, `onCreate`, `createVars`, and `onWake`. Can be async. It runs early so that database migrations are applied before any other lifecycle hook accesses the database. The second parameter is `true` when the actor is being created for the first time.

### `createState`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

The `createState` function dynamically initializes state based on input. Called only once when the actor is first created. Can be async. See [state documentation](/docs/actors/state) for more information.

### `vars`

The `vars` constant defines ephemeral variables for the actor. These variables are not persisted and are useful for storing runtime-only data. The value for `vars` must be clonable via `structuredClone`. See [ephemeral variables documentation](/docs/actors/state#ephemeral-variables) for more information.

### `createVars`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

The `createVars` function dynamically initializes ephemeral variables. Can be async. Use this when you need to initialize values at runtime. See [ephemeral variables documentation](/docs/actors/state#ephemeral-variables) for more information.

### `onCreate`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

The `onCreate` hook is called when the actor is first created. Can be async. Use this hook for initialization logic that doesn't affect the initial state.

### `onDestroy`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

The `onDestroy` hook is called when the actor is being permanently destroyed. Can be async. Use this for final cleanup operations like closing external connections, releasing resources, or performing any last-minute state persistence.

The actor is still fully functional when `onDestroy` runs. You can access the database, broadcast events, call `waitUntil`, send queue messages, and use `schedule.after`. State mutations made during `onDestroy` are persisted before the actor is torn down.

### `onWake`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

This hook is called any time the actor is started (e.g. after restarting, upgrading code, or crashing). Can be async.

This is called after the actor has been initialized but before any connections are accepted.

Use this hook to set up any resources or start any background tasks, such as `setInterval`.

### `onSleep`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

This hook is called when the actor is going to sleep. Can be async. Use this to clean up resources, close connections, or perform any shutdown operations.

The actor is still fully functional when `onSleep` runs. You can access the database, broadcast events, call `waitUntil`, send queue messages, and use `schedule.after`. State mutations made during `onSleep` are persisted before the actor finishes sleeping.

This hook may not always be called in situations like crashes or forced terminations. Don't rely on it for critical cleanup operations.

Not supported on Cloudflare Workers.

### `run`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

The `run` hook is called after the actor starts and runs in the background without blocking actor startup. This is ideal for long-running background tasks like:

- Reading from message queues in a loop
- Tick loops for periodic work
- Custom workflow logic
- Background processing

The handler exposes `c.aborted` for loop checks and `c.abortSignal` for canceling operations when the actor is stopping. You should always check or listen for shutdown to exit gracefully.

**Important behavior:**
- The actor may go to sleep at any time during the `run` handler. Wrap work that must keep the actor awake with `c.keepAwake(promise)` to block idle sleep until the promise settles.
- If the `run` handler exits (returns), the actor follows its normal idle sleep timeout once it becomes idle
- If the `run` handler throws an error, the actor logs the error and then follows its normal idle sleep timeout once it becomes idle
- On shutdown, `c.abortSignal` fires so the `run` handler can exit within the graceful shutdown window.

Finite `run` handlers leave the actor alive after they finish. If you want a one shot task to clean itself up when it is done, call `c.destroy()` before returning.

### `onStateChange`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

Called whenever the actor's state changes. Cannot be async. This is often used to broadcast state updates.

Do not mutate `c.state` inside `onStateChange`; re-entrant state mutation is rejected.

### `createConnState` and `connState`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

There are two ways to define the initial state for connections:
1. `connState`: Define a constant object that will be used as the initial state for all connections
2. `createConnState`: A function that dynamically creates initial connection state based on connection parameters. Can be async.

### `onBeforeConnect`

[API Reference](/typedoc/interfaces/rivetkit.mod.BeforeConnectContext.html)

The `onBeforeConnect` hook is called whenever a new client connects to the actor. Can be async. Clients can pass parameters when connecting, accessible via `params`. This hook is used for connection validation and can throw errors to reject connections.

The `onBeforeConnect` hook does NOT return connection state - it's used solely for validation.

Connections cannot interact with the actor until this method completes successfully. Throwing an error will abort the connection. This can be used for authentication - see [Authentication](/docs/actors/authentication) for details.

### `onConnect`

[API Reference](/typedoc/interfaces/rivetkit.mod.ConnectContext.html)

Executed after the client has successfully connected. Can be async. Receives the connection object as a second parameter.

Messages will not be processed for this actor until this hook succeeds. Errors thrown from this hook will cause the client to disconnect.

### `canPublish` and `canSubscribe`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

Use schema-level hooks to authorize queue publishes and event subscriptions. Both hooks can be async and must return booleans:

- `queues.<name>.canPublish` runs before inbound queue publishes.
- `events.<name>.canSubscribe` runs before inbound event subscription requests.

For actions, enforce authorization directly inside each action handler.

Use deny-by-default rules for each hook and return `false` unless explicitly allowed. See [Access Control](/docs/actors/access-control) for full guidance.

### `onDisconnect`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

Called when a client disconnects from the actor. Can be async. Receives the connection object as a second parameter. Use this to clean up any connection-specific resources.

### `onRequest`

[API Reference](/typedoc/interfaces/rivetkit.mod.RequestContext.html)

The `onRequest` hook handles HTTP requests sent to your actor at `/actors/{actorName}/http/*` endpoints. Can be async. It receives the request context and a standard `Request` object, and should return a `Response` object.

See [Request Handler](/docs/actors/request-handler) for more details.

### `onWebSocket`

[API Reference](/typedoc/interfaces/rivetkit.mod.WebSocketContext.html)

The `onWebSocket` hook handles WebSocket connections to your actor. Can be async. It receives the actor context and a `WebSocket` object. Use this to set up WebSocket event listeners and handle real-time communication.

See [WebSocket Handler](/docs/actors/websocket-handler) for more details.

### `onBeforeActionResponse`

[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html)

The `onBeforeActionResponse` hook is called before sending an action response to the client. Can be async. Use this hook to modify or transform the output of an action before it's sent to the client. This is useful for formatting responses, adding metadata, or applying transformations to the output.

## Sleeping

Actors automatically sleep after a period of inactivity to free up resources. When a request arrives for a sleeping actor, it wakes up, restores its state, and handles the request.

### When Actors Sleep

#### Idle Timeout

An actor is considered idle and eligible to sleep when **all** of the following are true:

- No active HTTP requests
- No active connections (unless they are hibernatable WebSockets)
- No active `run` handler (unless it is waiting on a queue)
- No outstanding `c.keepAwake(promise)` promises
- No pending disconnect callbacks
- No async `onWebSocket` event handlers (eg `open`, `message`, `close`) still running

Once the actor becomes idle, the sleep timer starts. After `sleepTimeout` (default 30 seconds) of continuous inactivity, the actor begins the sleep process. Any activity resets the timer.

Outbound requests (e.g. `fetch` calls) do not count as activity and will not keep the actor awake. Wrap them with `c.waitUntil()` if they must complete before the actor sleeps.

#### Upgrades & Eviction

The platform may force an actor to migrate to a new machine during version upgrades or when a serverless request is about to timeout. The same [shutdown sequence](#shutdown-sequence) runs, then the actor is rescheduled on a new machine and wakes up with its persisted state.

Use `onSleep`, `waitUntil`, or `keepAwake` to control the length of the grace period before the actor moves to another machine.

### Manual Lifecycle Controls

You can also trigger lifecycle transitions from the Rivet Cloud API. These endpoints are useful for operational workflows, debugging, and forcing an actor to move through the same sleep or reschedule path that the platform would normally trigger.

```bash
curl -X POST \
  "https://cloud-api.rivet.dev/actors/$ACTOR_ID/sleep?namespace=$NAMESPACE" \
  -H "Authorization: Bearer $RIVET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
```

```bash
curl -X POST \
  "https://cloud-api.rivet.dev/actors/$ACTOR_ID/reschedule?namespace=$NAMESPACE" \
  -H "Authorization: Bearer $RIVET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
```

`/sleep` asks the actor to enter the normal sleep shutdown sequence. `/reschedule` asks the platform to allocate the actor again, which is useful after crashes or when you need to force a fresh placement. Both endpoints require the actor ID and namespace.

### Skip Ready Wait

The gateway normally holds requests until the actor is ready. The actor is not ready during startup (before `onWake` finishes) or during the sleep grace period (while `onSleep` and `waitUntil` are running). Probes and readiness checks can opt out with `skipReadyWait` to reach the actor's `onRequest` or `onWebSocket` handler in either window.

See [Skip Ready Wait](/docs/clients/javascript#skip-ready-wait) on the JavaScript client page for usage.

### Keeping the Actor Awake

RivetKit gives you two primitives for holding the actor awake across background work. Both take a `Promise` and differ in how they interact with idle sleep and the grace period.

| Method | Accepts | Blocks idle sleep | Blocks grace finalize | Use case |
| --- | --- | --- | --- | --- |
| `c.keepAwake(promise)` | `Promise` (returns same promise) | Yes | Yes | Critical work that must keep the actor running end to end (for example a turn in a game, an ongoing tool call). |
| `c.waitUntil(promise)` | `Promise<unknown>` (returns void) | No | Yes | Best-effort finalization work that may complete during the grace window (for example analytics flushes, cleanup writes). |

`c.keepAwake(promise)` is the preferred primitive for long-running work the actor should not sleep through. It holds a keep-awake counter until the promise settles, which blocks both idle sleep and the grace finalize step. The promise is returned unchanged, so you can `await` it if you need the value.

`setPreventSleep(enabled)` is deprecated and now a no-op. Wrap the work you want to keep alive with `c.keepAwake(promise)` instead.

### On Sleep Hook

The [`onSleep`](#onsleep) hook runs during shutdown for cleanup like clearing intervals or closing connections. It is best-effort and will not run if the actor crashes.

### Wait Before Sleep

`c.waitUntil(promise)` registers a background promise that must resolve before the actor finishes sleeping. Use this to flush data or finish in-flight work during shutdown without blocking the main execution flow.

The actor waits up to `sleepGracePeriod` for graceful sleep work during the [shutdown sequence](#shutdown-sequence). That single budget covers `onSleep`, `waitUntil`, `keepAwake`, async raw WebSocket handlers such as `message` and `close`. By default, this graceful sleep window is 15 seconds total. If the timeout is exceeded, the actor proceeds with sleep anyway.

### Sleep Timeouts

| Option | Default | Description |
|--------|---------|-------------|
| `sleepTimeout` | 30 seconds | Time of inactivity before the actor begins sleeping. |
| `sleepGracePeriod` | 15 seconds | Total graceful shutdown window for hooks, `waitUntil`, `keepAwake`, async raw WebSocket handlers, and disconnects. |

Rivet enforces a hard limit of **30 minutes** for the entire stop process. These can be configured in the [options](#options).

### WebSocket Hibernation

WebSocket connections are preserved across sleep cycles by default and transparently migrated to the new actor instance. Client stays connected and sees no interruption. Actor migration is very fast, realtime workloads are not interrupted.

### Shutdown Sequence

When an actor sleeps or is destroyed, it enters the graceful shutdown window:

1. `c.abortSignal` fires and `c.aborted` becomes `true`. New connections and dispatch are rejected. Alarm timeouts are cancelled. On sleep, scheduled events are persisted and will be re-armed when the actor wakes.
2. `onSleep` or `onDestroy` and `onDisconnect` for each closing connection run during the same window. User `waitUntil` promises and async raw WebSocket handlers are drained. Hibernatable WebSocket connections are preserved on sleep and closed on destroy.
3. Once graceful work has completed, state is saved and final cleanup runs.

The entire window is bounded by `sleepGracePeriod` on both sleep and destroy. Defaults to 15 seconds. If the window is exceeded, the actor proceeds to state save anyway.

#### Graceful shutdown window

During steps 1 through 6, the actor is still fully functional. Database access, `broadcast`, `waitUntil`, `queue.send`, and `schedule.after` all work. State mutations are persisted at step 7. Actions invoked by pre-existing connections or lifecycle hooks continue to execute normally.

New connections and raw WebSocket upgrades are rejected as soon as the shutdown sequence begins. New requests that arrive during shutdown are held until the actor wakes up again. The caller does not need to retry.

If `schedule.after` is called during shutdown, the event is persisted so it survives the sleep/wake cycle, but no local timeout is scheduled. The event will fire after the actor wakes.

In-flight actions are **not** waited on during shutdown. If an action must complete before the actor stops, wrap the critical work with `c.waitUntil()`.

## Options

The `options` object allows you to configure various timeouts and behaviors for your actor.

| Option | Default | Description |
|--------|---------|-------------|
| `createVarsTimeout` | 5000ms | Timeout for `createVars` function |
| `createConnStateTimeout` | 5000ms | Timeout for `createConnState` function |
| `onConnectTimeout` | 5000ms | Timeout for `onConnect` hook |
| `sleepGracePeriod` | 15000ms | Total graceful shutdown window for both sleep and destroy |
| `stateSaveInterval` | 1000ms | Interval for persisting state |
| `actionTimeout` | 60000ms | Timeout for action execution |
| `connectionLivenessTimeout` | 2500ms | Timeout for connection liveness check |
| `connectionLivenessInterval` | 5000ms | Interval for connection liveness check |
| `sleepTimeout` | 30000ms | Time before actor sleeps due to inactivity |
| `canHibernateWebSocket` | false | Whether WebSockets can hibernate (experimental) |

## Advanced

### Actor Shutdown Abort Signal

The `c.abortSignal` provides an `AbortSignal` that fires when the actor is stopping, and `c.aborted` is the shorthand boolean for loop checks. Use these to cancel ongoing operations when the actor sleeps or is destroyed.

The abort signal fires at the very start of the [shutdown sequence](#shutdown-sequence), before `onSleep` or `onDestroy` runs. This means `c.aborted` is already `true` inside those lifecycle hooks. The signal fires early so that the `run` handler can exit promptly, but the actor remains fully functional throughout the graceful shutdown window.

See [Canceling Long-Running Actions](/docs/actors/actions#canceling-long-running-actions) for manually canceling operations on-demand.

### Using `ActorContext` Type Externally

When extracting logic from lifecycle hooks or actions into external functions, you'll often need to define the type of the context parameter. Rivet provides helper types that make it easy to extract and pass these context types to external functions.

See [Types](/docs/actors/types) for more details on using `ActorContextOf`.

## Full Example

_Source doc path: /docs/actors/lifecycle_
