Run background processes·续

src/index.tsts

   export class MyContainer extends DurableObject<Env> {
    // ...

async alarm(): Promise<void> {
        let running = false;

for (const [key, argv] of await this.ctx.storage.list<string[]>({
            prefix: "process:",
        })) {
            const id = key.slice("process:".length);
            const status = await this.status(id);

if (status?.state === "running" || status?.state === "starting") {
                running = true;
            } else {
                console.log({ event: "process.ended", id, argv, status });
                await this.ctx.storage.delete(key);
            }
        }

if (running) {
            await this.ctx.storage.setAlarm(Date.now() + 60_000);
        }
    }
   }
   ```

A running process does not keep the container running. The alarm checks every process each minute, and each alarm keeps the container running. A Durable Object that restarted between alarms sets the 10-minute inactivity timeout again in its constructor. The container stops 10 minutes after the alarm finds that the last process has ended. For more information, refer to [Sandbox lifetime](https://developers.cloudflare.com/sandbox/concepts/lifetime/#activity-keeps-an-instance-running).

A Durable Object has one alarm. If your class already has an `alarm()` handler, merge the process checks into it, and set the alarm to the earliest time that any check needs. For more information, refer to [Alarms](https://developers.cloudflare.com/durable-objects/api/alarms/).

When a process ends, the alarm writes a `process.ended` log to [Workers Logs](https://developers.cloudflare.com/workers/observability/logs/workers-logs/) within a minute. Replace the `console.log()` call with what your application does next, such as sending a notification.
7. Add routes to your Worker that call these methods:

*src/index.jsjs*

```js
   const command = [
    "sh",
    "-c",
    "echo ready; for i in $(seq 1 300); do echo tick $i; sleep 1; done",
   ];

export default {
    async fetch(request, env) {
        const url = new URL(request.url);
        const match = /^\/processes\/([a-z0-9-]{1,63})(\/logs)?$/.exec(
            url.pathname,
        );

if (!match) {
            return new Response("Not found", { status: 404 });
        }

const [, id, logs] = match;
        const sandbox = env.MY_CONTAINER.getByName("sandbox");

if (logs) {
            const stream =
                url.searchParams.get("stream") === "stderr" ? "stderr" : "stdout";
            const output = await sandbox.logs(id, stream);
            return output === undefined
                ? new Response("Not found", { status: 404 })
                : new Response(output);
        }

if (request.method === "POST") {
            return (await sandbox.startProcess(id, command))
                ? new Response(null, { status: 201 })
                : new Response("The process ID is in use", { status: 409 });
        }

if (request.method === "DELETE") {
            return (await sandbox.stop(id))
                ? new Response(null, { status: 202 })
                : new Response("The process is not running", { status: 409 });
        }

const status = await sandbox.status(id);
        return status
            ? Response.json(status)
            : new Response("Not found", { status: 404 });
    },
   };
   ```

*src/index.tsts*

```ts
   const command = [
    "sh",
    "-c",
    "echo ready; for i in $(seq 1 300); do echo tick $i; sleep 1; done",
   ];

export default {
    async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        const match = /^\/processes\/([a-z0-9-]{1,63})(\/logs)?$/.exec(
            url.pathname,
        );

if (!match) {
            return new Response("Not found", { status: 404 });
        }

const [, id, logs] = match;
        const sandbox = env.MY_CONTAINER.getByName("sandbox");

if (logs) {
            const stream =
                url.searchParams.get("stream") === "stderr" ? "stderr" : "stdout";
            const output = await sandbox.logs(id, stream);
            return output === undefined
                ? new Response("Not found", { status: 404 })
                : new Response(output);
        }

if (request.method === "POST") {
            return (await sandbox.startProcess(id, command))
                ? new Response(null, { status: 201 })
                : new Response("The process ID is in use", { status: 409 });
        }

if (request.method === "DELETE") {
            return (await sandbox.stop(id))
                ? new Response(null, { status: 202 })
                : new Response("The process is not running", { status: 409 });
        }

const status = await sandbox.status(id);
        return status
            ? Response.json(status)
            : new Response("Not found", { status: 404 });
    },
   } satisfies ExportedHandler<Env>;
   ```

This example command prints `ready`, then prints a line each second for five minutes. Replace it with your own command. The route accepts only process IDs that are safe in a path, because each ID becomes a directory name.
8. Deploy your Worker:npmyarnpnpm

npx wrangler deploy ```

   yarn wrangler deploy
   ```

pnpm wrangler deploy ```

  1. Start a process named ticker, then check it. Replace the example hostname with the workers.dev URL that Wrangler prints:
   curl https://<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev/processes/ticker --request POST
   curl https://<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev/processes/ticker
   ```

The first request responds with `201`, and a second `POST` with the same ID responds with `409`. The status includes the process ID:

```json
   { "state": "running", "pid": 354 }
   ```

10. Read the output so far:

```sh
    curl https://<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev/processes/ticker/logs
    ```

The request prints the lines so far and ends:

```txt
    ready
    tick 1
    tick 2
    tick 3
    ```

Add `stream=stderr` to the query to read standard error.
11. Stop the process, then check it again:

```sh
    curl https://<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev/processes/ticker --request DELETE
    curl https://<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev/processes/ticker
    ```

The `DELETE` request responds with `202`. The status shows the exit code:

```json
    { "state": "exited", "exitCode": 143 }
    ```

## Related resources

- [Follow a background process](https://developers.cloudflare.com/sandbox/commands/follow-a-background-process/): stream the output of a process while it runs, or wait until it prints a line.
- [Stream command output](https://developers.cloudflare.com/sandbox/commands/stream-command-output/): commands that finish while the request is open.
- [Run a server in the background](https://developers.cloudflare.com/sandbox/commands/run-a-server-in-the-background/): processes that run until the container stops.
- [Preview a web application](https://developers.cloudflare.com/sandbox/previews/): a web server as the main process.
- [Process workspace example ↗︎](https://github.com/cloudflare/sandbox-sdk/tree/main/examples/process-workspace): a deployable Worker that runs processes with the same approach, plus routes that list processes and wait for one to exit. Its scripts do not record the boot ID.
- [Sandbox lifetime](https://developers.cloudflare.com/sandbox/concepts/lifetime/)
- [Durable Object alarms](https://developers.cloudflare.com/durable-objects/api/alarms/)

Was this helpful?

YesNo

## On this page

[![](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NiIgaGVpZ2h0PSIzMCIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDY2IDMwIj48ZyBjbGlwLXBhdGg9InVybCgjY2xpcDBfNzQ2XzIpIj48cGF0aCBmaWxsPSIjZmY5OTEwIiBkPSJNNTMuMTIgMTMuMTI1YzYuOTk3IDAgMTIuNjY4IDUuNzA4IDEyLjY2OCAxMi43NSAwIDEuMjU1LS4xOCAyLjQ2OC0uNTE2IDMuNjE0YS43Mi43MiAwIDAgMS0uNjk0LjUxMUg0Ni45NTNhLjM3NS4zNzUgMCAwIDEtLjM0OC0uNTFsLjMzNy0uODgyYzEuMDc2LTIuODMzIDQuMTQ2LTUuMTU0IDYuOTg3LTUuMjlsNS4zODQtLjI3N2EuNTQzLjU0MyAwIDAgMCAuNTE0LS41NDQuNTQzLjU0MyAwIDAgMC0uNTEtLjU0NGwtNS4wNTYtLjI5OWMtMi43MTgtLjE0LTQuMDQ2LTIuNTYtMy4zMTUtNS4wODFsLjg0Mi0yLjkwMmEuNzIuNzIgMCAwIDEgLjY0Ni0uNTI3cS4zNC0uMDIuNjg2LS4wMTkiLz48cGF0aCBmaWxsPSIjZmY1ZjA4IiBkPSJNMzUuNjA5IDBjNy4yOTUgMCAxMy40MjQgNS4wMjUgMTUuMTU3IDExLjgyNC4wNzMuMjg1LjA1LjU4Ni0uMDU0Ljg2MmwtMS4zODggMy42NzdjLTEuMDc2IDIuODMzLTMuODQyIDUuMjktNi45NyA1LjI5bC0yNC4xMDUuMjc4YS41MzMuNTMzIDAgMCAwLS41MjUuNTM1YzAgLjI5My4yMzQuNTMxLjUyNC41MzVsMjMuNzY1LjMxNmMyLjcyNCAwIDQuMDQ2IDIuNTYgMy4zMTYgNS4wODFsLS4zMDcgMS4wNjFhLjc1Ljc1IDAgMCAxLS43MTYuNTQxSC45NjRhLjcxLjcxIDAgMCAxLS43MTgtLjY0NiAxMSAxMSAwIDAgMS0uMDM0LS44NTRjMC01LjQ3NiA0LjE2Ni05Ljk3MyA5LjQ4Mi0xMC40NTZhNy4yIDcuMiAwIDAgMS0uMTY3LTEuNTQ0YzAtMy45MzUgMy4xNy03LjEyNSA3LjA4LTcuMTI1IDEuNTU1IDAgMi45OTQuNTA2IDQuMTYyIDEuMzYyQzIyLjg0OSA0LjQ5NyAyOC43MDcgMCAzNS42MDkgMCIvPjxwYXRoIGZpbGw9IiNmZjk5MTAiIGQ9Ik01My4xMiAxMy4xMjVjNi45OTcgMCAxMi42NjggNS43MDggMTIuNjY4IDEyLjc1IDAgMS4yNTUtLjE4IDIuNDY4LS41MTYgMy42MTRhLjcyLjcyIDAgMCAxLS42OTQuNTExSDQ2Ljk1M2EuMzc1LjM3NSAwIDAgMS0uMzQ4LS41MWwuMzM3LS44ODJjMS4wNzYtMi44MzMgNC4xNDYtNS4xNTQgNi45ODctNS4yOWw1LjM4NC0uMjc3YS41NDMuNTQzIDAgMCAwIC41MTQtLjU0NC41NDMuNTQzIDAgMCAwLS41MS0uNTQ0bC01LjA1Ni0uMjk5Yy0yLjcxOC0uMTQtNC4wNDYtMi41Ni0zLjMxNS01LjA4MWwuODQyLTIuOTAyYS43Mi43MiAwIDAgMSAuNjQ2LS41MjdxLjM0LS4wMi42ODYtLjAxOSIvPjxwYXRoIGZpbGw9IiNmZjVmMDgiIGQ9Ik0zNS42MDkgMGM3LjI5NSAwIDEzLjQyNCA1LjAyNSAxNS4xNTcgMTEuODI0LjA3My4yODUuMDUuNTg2LS4wNTQuODYybC0xLjM4OCAzLjY3N2MtMS4wNzYgMi44MzMtMy44NDIgNS4yOS02Ljk3IDUuMjlsLTI0LjEwNS4yNzhhLjUzMy41MzMgMCAwIDAtLjUyNS41MzVjMCAuMjkzLjIzNC41MzEuNTI0LjUzNWwyMy43NjUuMzE2YzIuNzI0IDAgNC4wNDYgMi41NiAzLjMxNiA1LjA4MWwtLjMwNyAxLjA2MWEuNzUuNzUgMCAwIDEtLjcxNi41NDFILjk2NGEuNzEuNzEgMCAwIDEtLjcxOC0uNjQ2IDExIDExIDAgMCAxLS4wMzQtLjg1NGMwLTUuNDc2IDQuMTY2LTkuOTczIDkuNDgyLTEwLjQ1NmE3LjIgNy4yIDAgMCAxLS4xNjctMS41NDRjMC0zLjkzNSAzLjE3LTcuMTI1IDcuMDgtNy4xMjUgMS41NTUgMCAyLjk5NC41MDYgNC4xNjIgMS4zNjJDMjIuODQ5IDQuNDk3IDI4LjcwNyAwIDM1LjYwOSAwIi8+PC9nPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDBfNzQ2XzIiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0wIDBoNjZ2MzBIMHoiLz48L2NsaXBQYXRoPjwvZGVmcz48L3N2Zz4=)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/sandbox/commands/run-background-processes/#page","headline":"Run background processes","description":"Start a background process in a Linux sandbox, then check it, read its output, and stop it from later requests.","url":"https://developers.cloudflare.com/sandbox/commands/run-background-processes/","inLanguage":"en","image":"https://developers.cloudflare.com/sandbox/commands/run-background-processes/og.png?v=e231bc0f3fb59dce","dateModified":"2026-10-23","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}


description: Run Python code in a Linux sandbox with the Python version and packages you choose, and return its output. title: Run Python code image: https://developers.cloudflare.com/sandbox/commands/run-python-code/og.png?v=bfa2c3a9669d3792


Skip to content

Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/sandbox/llms.txt
Use this file to discover all available pages before exploring further.