> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ohmyho.st/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> For account actions, read https://ohmyho.st/skills/ohmyhost-get-started/SKILL.md and use the authenticated ohmyho.st CLI or local product MCP. Mintlify search only reads documentation. Preserve the customer’s selected project, environment and authentication provider.

# Functions and cron

> Run a Worker module with HTTP and scheduled handlers on the managed runtime.

A project can run server code without a web framework. Set `runtime.mode: functions` in `ohmyhost.yaml`, keep `build.install` and export a Worker module from `src/ohmyhost/worker.ts`:

```ts theme={null}
export default {
  async fetch(request, env, ctx) {
    return new Response("ok");
  },
  async scheduled(controller, env, ctx) {
    // controller.cron, controller.scheduledTime, controller.noRetry()
  },
};
```

`fetch` answers every request of the environment. The module receives the same bindings as a framework app: the application database, files, mail, [runtime secrets](/secrets) and the declared egress origins. There is no `build.command` and no `build.output`; `ohmyhost init --dry-run --json` reports a missing module as a blocker.

## Declare schedules

```yaml theme={null}
functions:
  crons:
    - "0 3 * * *"
    - "*/15 * * * *"
```

Between one and eight unique five-field UTC crons; the shortest interval is five minutes. Declared crons work on every edge runtime. The functions runtime, Next.js and TanStack Start keep `scheduled` in the default export of `src/ohmyhost/worker.ts`; a Vite app keeps it in the default export of its companion `src/ohmyhost/companion.ts`. Named exports are never invoked. Init blocks a module without a default export (`worker_module_default_export_required`) and a declaration without the handler (`scheduled_handler_required`).

The platform bundles `src/ohmyhost/worker.ts` on its own, outside the framework build. tsconfig `paths` resolve; Vite-only aliases, framework virtual modules and modules that declare TanStack Start server functions or Next.js route handlers do not. Import plain application modules and the generated runtime clients, and keep framework entry code out of the Worker module. A script the runtime cannot start fails the deployment terminally; `operation get` reports `error.code: runtime_candidate_rejected`.

## How a run executes

Every minute the platform starts one run per declared cron that is due. The handler receives `controller.cron`, `controller.scheduledTime` and `controller.noRetry()` together with the environment bindings. A run may take up to 120 seconds. A thrown error or a platform failure is retried in the following minutes up to three attempts; calling `controller.noRetry()` before throwing stops the retries. Promotion and rollback switch the crons together with the deployment, and deleting the project removes them.

## Observe runs

Scheduled runs are not deployment log events. Read them per environment, newest first:

```sh theme={null}
ohmyhost function runs --project "$PROJECT_ID" --environment "$ENVIRONMENT_ID" --limit 20 --json
```

The MCP tool `function_runs_list` and `GET /v1/projects/{project_id}/environments/{environment_id}/function-runs` return the same list. Each run carries its cron, the scheduled UTC minute, `state` (`due`, `running`, `retry`, `succeeded`, `failed`), the attempt, the status the handler returned (`204` for a completed handler, `422` when it called `controller.noRetry()` and threw, `500` for a thrown error) and its start and finish times. A declared cron with no run for a past due minute means the environment had no active deployment with that cron at the time.

## Cost

A run is metered like any request: one request plus the CPU milliseconds it uses, from the same credit balance and at the same rates as the application. There is no separate function plan.

[Environments](/environments) · [Usage](/usage) · [Troubleshooting](/troubleshooting)
