# Functions

Functions are code that runs on demand. You write the code, deploy it to Volcano, and invoke it via HTTP.

Functions are code that runs on demand. You write the code, deploy it to Volcano, and invoke it via HTTP. Volcano handles provisioning, scaling, and execution.

## How functions work

```text
┌─────────────────────────────────────────────────────────────┐
│ 1. Write your code (Node.js, Python, or Ruby)               │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. Package a source ZIP or tar.gz and upload via API          │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 3. Volcano builds and deploys the function                   │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 4. Invoke via DNS endpoint or API endpoint                   │
└─────────────────────────────────────────────────────────────┘
```

Functions:

- Scale automatically based on demand
- Run only when invoked (no idle costs)
- Can access databases with user context
- Receive the user's identity when a signed-in user invokes them, once their [visibility](https://docs.volcano.dev/platform/functions/creating-functions.md#choose-who-can-invoke-it) admits signed-in users
- Can be invoked automatically on a cron schedule with [scheduled invocations](https://docs.volcano.dev/platform/functions/scheduled-invocations.md)

Work that runs longer than one invocation allows, or that has to wait on something, belongs in a [durable function](https://docs.volcano.dev/platform/functions/durable-functions.md) instead. Those checkpoint their progress and resume from the last completed step.

## CLI folder layout

When using the Volcano CLI, place functions under:

```text
volcano/functions/
```

Supported layouts:

- single-file functions such as `volcano/functions/hello.js`
- directory-based functions such as `volcano/functions/api/index.js`

The CLI derives the function name from:

- the filename for single-file functions
- the directory name for directory-based functions

The CLI packages cloud function sources as `tar.gz` archives and also automatically packages:

- shared files/directories whose names start with `_`
- dependency manifests such as `package.json`, `requirements.txt`, and `Gemfile`

Cloud deploys install dependencies during the function compile build. Do not upload `node_modules`, `python_deps`, or `vendor` to the cloud API. Local mode installs them too, from the same manifests, so you deploy the same sources either way.

Function and frontend source archives are limited to 256 MB, which the API enforces. The CLI does not apply its own source archive size limit. The final image each build produces, dependencies included, is limited to 4096 MB and checked in the cloud build.

See [Creating functions](https://docs.volcano.dev/platform/functions/creating-functions.md) for concrete layout examples.

## Deployment regions

Functions deploy according to the owning project's region policy:

- Project `all_regions=true`: function deploys to all configured regions.
- Project `all_regions=false`: function deploys only to `selected_regions`.

Function API responses include `deployed_regions` so you can see where the function is active.

## Deployment lifecycle

Deploying a new function or updating an existing function's code starts an
asynchronous workflow. Function reads return `status: "provisioning"` while the
workflow is running, then transition to `active` when deployment succeeds or
`failed` if deployment fails. Deleting a function returns `status: "deleting"`
while cleanup runs, then the function disappears from `get` and `list` results.

## Supported runtimes

| Language | Runtimes                                                   | Default handler |
| -------- | ---------------------------------------------------------- | --------------- |
| Node.js  | nodejs24.x, nodejs22.x                                     | `index.handler` |
| Python   | python3.14, python3.13, python3.12, python3.11, python3.10 | `main.handler`  |
| Ruby     | ruby4.0, ruby3.4, ruby3.3                                  | `main.handler`  |

`python3.10` is deprecated: Python 3.10 reached end of life in October 2026. It still deploys, and Volcano will remove it before 2027-08-31. Move functions to a newer Python runtime.

The handler is the entry point Volcano calls when your code runs. For Node.js, `index.handler` means the `handler` export in `index.js`. You can customize this when deploying.

## Quick example

### 1. Write your function

```javascript
// index.js
exports.handler = async (event) => {
  const name = event.payload?.name || 'World';

  return {
    statusCode: 200,
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      message: `Hello, ${name}!`,
    }),
  };
};
```

### 2. Package and deploy

```bash
# Create source ZIP or tar.gz file
zip function.zip index.js

# Deploy to Volcano. The response carries the function's id and invoke_url.
curl -X POST "https://api.volcano.dev/projects/$PROJECT_ID/functions" \
  -H "Authorization: Bearer $PLATFORM_TOKEN" \
  -F "name=hello" \
  -F "runtime=nodejs24.x" \
  -F "handler=handler" \
  -F "code=@function.zip" > function.json

FUNCTION_ID=$(jq -r '.id' function.json)
INVOKE_URL=$(jq -r '.invoke_url' function.json)
```

### 3. Invoke

Invoke `invoke_url` as returned. Functions answer on their own domain, so an
endpoint built from the API URL will not reach them.

```bash
# Option A (recommended): the returned endpoint, which is geo-routed
curl -X POST "$INVOKE_URL" \
  -H "Authorization: Bearer $SERVICE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"payload": {"name": "Volcano"}}'
```

```bash
# Option B: Invoke endpoint on API host (direct invocation)
curl -X POST "https://api.volcano.dev/functions/$FUNCTION_ID/invoke" \
  -H "Authorization: Bearer $SERVICE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"payload": {"name": "Volcano"}}'
```

Response:

```json
{
  "message": "Hello, Volcano!"
}
```

The invocation HTTP status code and headers come directly from your function response, and Volcano adds `X-Volcano-Version` (`production`: `VERSION`, non-production: `ENV-VERSION`), `X-Volcano-Region`, `X-Volcano-Proxy-Ms` (milliseconds spent preparing the call, from your request arriving until your function ran), `X-Volcano-Proxy-Handler-Ms` (the part of that spent in the invoke endpoint itself), and `X-Volcano-Compute-Ms` (milliseconds spent running your function, or until a streamed response started). See [Invoking functions](https://docs.volcano.dev/platform/functions/invoking-functions.md#response-format) for the headers Volcano owns and does not forward.

## Environment variables

Volcano doesn't set any environment variables automatically — add them through the API, including `DATABASE_URL` for your project's database connection string. See [Environment variables](https://docs.volcano.dev/platform/functions/environment-variables.md).

```javascript
// Access environment variables in your function
const apiKey = process.env.MY_API_KEY;
const dbUrl = process.env.DATABASE_URL;
```

## User context

When a function is invoked with a user's access token, it receives the user's identity in `event.__volcano_auth`. The function's [visibility](https://docs.volcano.dev/platform/functions/creating-functions.md#choose-who-can-invoke-it) must be `authenticated` or `public`; new functions are `private` and answer user tokens with `404`.

```javascript
exports.handler = async (event) => {
  // Check if user is authenticated
  if (!event.__volcano_auth) {
    return {
      statusCode: 401,
      body: JSON.stringify({ error: 'Unauthorized' }),
    };
  }

  const { user_id, email, role, project_id } = event.__volcano_auth;

  return {
    statusCode: 200,
    body: JSON.stringify({
      message: `Hello, ${email}!`,
      userId: user_id,
      role: role, // 'authenticated' or 'anonymous'
    }),
  };
};
```

> **Note:** When invoked with a service key instead of a user token, `event.__volcano_auth` is not present. Service key invocations are for admin operations that don't have a user context.

See [User context](https://docs.volcano.dev/platform/functions/user-context.md) for details.

## Response time on the first invocation

A function that has not run recently has nowhere to run, so the next invocation
waits for a runtime to start and load your code before your handler is reached.
That startup is what makes an occasional invocation slower than the ones after
it.

Volcano keeps your runtimes started for you, in every region the function is
deployed to:

- **For two days after each deploy**, whether or not anything calls it. You
  deploy, then call it yourself — that first call is the one worth being fast,
  and it is fast without any traffic having warmed it up.
- **For a day after the most recent invocation**, for as long as the function
  keeps being used. A function called every few hours stays continuously ready.

A function that goes a full day without an invocation stops being kept ready, and
its next caller waits for a runtime to start. That invocation also puts the
function back on the list, within a couple of minutes, so a function coming back
into use is slow once rather than slow repeatedly. Redeploying restores the
two-day window immediately.

Nothing here is a setting, and it is the same on both plans. Keeping runtimes
ready is not billed as invocations or bandwidth, and it does not appear in your
[usage](https://docs.volcano.dev/platform/guides/plans-and-limits.md).

[Durable functions](https://docs.volcano.dev/platform/functions/durable-functions.md) are not kept ready. They are started
rather than called, and an execution that runs for hours pays a runtime start
once at its beginning and once after each wait, so readiness is not what decides
how long one takes.

### What runs, and what does not

Your handler is never called to keep a runtime ready. Volcano starts the runtime
and loads your module, then stops before reaching your handler, so nothing you
do per invocation happens: no request is processed and no response is produced.

Loading your module does run the code at the top level of the file, outside your
handler, the same way it runs on a real cold start. That is what makes the
readiness worth having: a connection pool or client created there is already
built when a real caller arrives. Anything that code logs shows up in your
[logs](https://docs.volcano.dev/platform/functions/logs.md) as an ordinary startup, so a module that logs on load will do so
while its runtimes are being kept ready.

```javascript
// Runs when the runtime starts, including when Volcano is keeping it ready.
const pool = new Pool({ connectionString: process.env.DATABASE_URL });

exports.handler = async (event) => {
  // Only runs for a real invocation.
  const { rows } = await pool.query('SELECT now()');
  return { statusCode: 200, body: JSON.stringify(rows[0]) };
};
```

Top-level code that is not safe to run without a caller — sending a message,
writing a row, charging a card — belongs inside the handler. That was already
true, because a cold start runs it too; keeping runtimes ready just makes it
happen more often.

Keeping a runtime ready without calling your handler needs Volcano to know which
file your handler lives in, which it takes from the handler name: `index.handler`
means the `handler` export in `index.js`. A handler that resolves some other way —
through a directory index, or a package export — leaves Volcano unable to find
that file, and a function it cannot find the entry point for is not kept ready.
Point the handler at the file directly if you want the behavior above.

## Resource limits

Your plan sets each function's resource limits:

| Resource                  | HOBBY plan | SUPERAGENT plan |
| ------------------------- | ---------- | --------------- |
| Timeout                   | 30 seconds | 180 seconds     |
| Memory                    | 128 MB     | 256 MB          |
| Disk                      | 512 MB     | 1024 MB         |
| Rate limit (per function) | 10 RPS     | Unlimited       |
| Rate limit (project-wide) | 60 RPS     | Unlimited       |

A step in a [durable function](https://docs.volcano.dev/platform/functions/durable-functions.md) gets its own, longer
timeout. See [Plans and limits](https://docs.volcano.dev/platform/guides/plans-and-limits.md#durable-functions).

## What's next

| Guide                                             | Description                                    |
| ------------------------------------------------- | ---------------------------------------------- |
| [Creating functions](https://docs.volcano.dev/platform/functions/creating-functions.md)       | Detailed deployment guide with examples        |
| [Durable functions](https://docs.volcano.dev/platform/functions/durable-functions.md)         | Long-running work that checkpoints and resumes |
| [Invoking functions](https://docs.volcano.dev/platform/functions/invoking-functions.md)       | Call functions from your app                   |
| [User context](https://docs.volcano.dev/platform/functions/user-context.md)                   | Access authenticated user data                 |
| [Environment variables](https://docs.volcano.dev/platform/functions/environment-variables.md) | Configure secrets and settings                 |
| [Logs](https://docs.volcano.dev/platform/functions/logs.md)                                   | View and filter function logs                  |
| [Deployment guide](https://docs.volcano.dev/platform/functions/deployment-guide.md)           | Optimize package size and dependencies         |
