---
title: "Run CPU work on workers"
description: "Move CPU-heavy work off the request thread with workers.run, a declared module, a time limit and the tenant carried in."
section: "guides"
status: "Building for 0.4"
spec: "parallelism"
canonical: "https://mainsdk.dev/docs/guides/run-cpu-work-on-workers"
updated: "2026-10-28"
---

# Run CPU work on workers

> Move CPU-heavy work off the request thread with workers.run, a declared module, a time limit and the tenant carried in.

Status: Building for 0.4 · Canonical: https://mainsdk.dev/docs/guides/run-cpu-work-on-workers · Updated: 2026-10-28

You will move a CPU-heavy step out of a request handler onto a worker thread, declare the module, set a time limit and test it. It takes about 10 minutes and needs no account.

## You need

- A MainSDK app with a `main.yaml`.
- A function that burns CPU for more than a few milliseconds and needs no network, database or secret. A worker may not call other MainSDK interfaces.

## 1. Declare the module

```yaml title="main.yaml"
workers:
  size: auto
  timeout: 10s
  modules: ["src/workers/*.worker.ts"]
```

## 2. Write the worker module

```ts title="src/workers/score.worker.ts"
import { worker } from "mainsdk/workers";

export default worker((rows: number[][], ctx) => {
  ctx.log.info("scoring", { rows: rows.length });
  return rows.map((r) => r.reduce((a, b) => a + b, 0));
});
```

## 3. Call it

```ts title="lib/score.ts"
import { workers } from "mainsdk";

export function score(rows: number[][], signal?: AbortSignal) {
  return workers.run<number[][], number[]>("./src/workers/score.worker.ts", rows, { signal });
}
```

Pass plain data in and out. Never pass a secret: it fails with `MAIN-WORKER-0004`.

## 4. Test it

`createTestApp()` runs the task inline. Add `workers: "threads"` to prove it on a real thread.

```ts title="test/score.test.ts"
import { createTestApp } from "@mainsdk/testing";

const app = createTestApp({ yaml: "schema: 1\nproject: t\nworkers:\n  modules: [src/workers/*.worker.ts]\n", workers: "threads" });
const out = await app.workers.run("./src/workers/score.worker.ts", [[1, 2], [3]]);
```

## Check it worked

- `npx mainsdk check` has no `MAIN-WORKER-` finding, and `npx mainsdk discover` lists the module.
- `workers.snapshot()` shows `completed` rising and `threads` back to 0 after the idle time.
- A task that loops past the timeout fails with `MAIN-WORKER-0005` and the next task still runs.
