# Functions and services (/self-hosting/functions)



The user guides are [Functions](/using/functions) and [Services](/using/services). This page is what an operator runs and sets.

## What runs [#what-runs]

| Piece               | Role                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `functions`         | The functions service. Public listener behind the gateway at `/functions/v1` and `/services/v1`; an internal listener in two sides: the runtime's (bundle downloads) and the control side (bundle uploads and the container services' internal routes, image resolving, rollovers and logs, from the orchestrator; invocations from the worker; each caller behind its own bearer). Resolves a branch's function, calls the runtime, proxies services, health-checks them, and delivers [database events](/using/functions/triggers#table-triggers) from the branches' outboxes. |
| `functions-runtime` | workerd with a small supervisor. It loads bundles by hash through its Worker Loader, so a deploy needs no restart. Runs without secrets, as a non-root user, read-only, with every capability dropped and a memory limit, on two networks: `functions` (internal; shared only with the functions service and the gateway alias) and `functions-egress` (the way out for public addresses).                                                                                                                                                                                       |
| Bundle bucket       | The `chelabase-functions` bucket in the object store, written and read through a `functions` S3 identity that can reach that bucket only. Only the functions service and the worker mount its keys.                                                                                                                                                                                                                                                                                                                                                                              |
| Orchestrator        | The [Management API](/reference/management/functions/listFunctions) routes. Sends bundles to the functions service.                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| Worker              | Cron schedules, the table-trigger installs, storage-trigger deliveries, the hourly bundle sweep.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `service-runner`    | **Only with the `services` profile.** Starts [container services](/using/services/containers).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |

The `functions` network is `internal: true`, so the runtime has no route to anything on it but the functions service and the gateway alias `gateway-functions` (the target of `env.CHELABASE`).

Secrets are generated by `init-secrets` like every other: the NATS `functions` password, the orchestrator's and the worker's bearers for the functions service, the functions service's and the worker's bearers for the runner, and the `functions` S3 keys. Pin any of them before the first `up` with the `CHELABASE_SEED_*` lines in `.env`: `CHELABASE_SEED_NATS_FUNCTIONS_PASSWORD`, `CHELABASE_SEED_ORCHESTRATOR_FUNCTIONS_TOKEN`, `CHELABASE_SEED_WORKER_FUNCTIONS_TOKEN`, `CHELABASE_SEED_FUNCTIONS_SERVICE_RUNNER_TOKEN` and `CHELABASE_SEED_WORKER_SERVICE_RUNNER_TOKEN`. Each token is at least 32 characters, and the orchestrator's and the worker's must differ.

## Turn edge functions on [#turn-edge-functions-on]

The functions service and its runtime run in every install: a plain `docker compose up` starts them. [Container services](/using/services/containers) also need the `services` profile (see [below](#the-services-profile)).

## Settings [#settings]

Set these in `.env` (blank means the default). Those marked "compose" are already wired by the compose files; they are listed for installs that run the services another way.

### Functions service [#functions-service]

| Variable                                                                                                            | Default                                                   | What it does                                                                                                                                                            |
| ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CHELABASE_FUNCTIONS_TIMEOUT_MS`                                                                                    | `30000`                                                   | An invocation's wall clock, in milliseconds. Past it: 504 `function_timeout`. A positive integer.                                                                       |
| `CHELABASE_FUNCTIONS_ADDR`                                                                                          | `127.0.0.1:8091`                                          | The public listener (behind the gateway). Comma-separated `host:port`s, names resolved at start. Compose: `functions-public:8091,127.0.0.1:8091`.                       |
| `CHELABASE_FUNCTIONS_INTERNAL_ADDR`                                                                                 | `127.0.0.1:8092`                                          | The internal listener's runtime side (bundle GETs). Compose: `functions-internal:8080`.                                                                                 |
| `CHELABASE_FUNCTIONS_CONTROL_ADDR`                                                                                  | `127.0.0.1:8093`                                          | The internal listener's control side (bundle uploads, invocations, and the container services' image resolving, rollovers and logs). Compose: `functions-control:8080`. |
| `CHELABASE_FUNCTIONS_RUNTIME_URL`                                                                                   | `http://127.0.0.1:8080`                                   | The runtime it calls. Compose: `http://functions-runtime:8080`.                                                                                                         |
| `CHELABASE_FUNCTIONS_URL`                                                                                           | `http://127.0.0.1:8091`                                   | The gateway's upstream for `/functions/v1` and `/services/v1`. Compose: `http://functions-public:8091`.                                                                 |
| `CHELABASE_FUNCTIONS_INTERNAL_URL`                                                                                  | `http://127.0.0.1:8093`                                   | Where the orchestrator and the worker reach the control side. Compose: `http://functions-control:8080`.                                                                 |
| `CHELABASE_ORCHESTRATOR_FUNCTIONS_TOKEN` (or `_FILE`)                                                               | required                                                  | The orchestrator's bearer. At least 32 characters; read by the orchestrator and the functions service.                                                                  |
| `CHELABASE_WORKER_FUNCTIONS_TOKEN` (or `_FILE`)                                                                     | required                                                  | The worker's bearer. At least 32 characters; must differ from the orchestrator's.                                                                                       |
| `CHELABASE_FUNCTIONS_SERVICE_RUNNER_TOKEN` (or `_FILE`)                                                             | none                                                      | The functions service's bearer for the runner.                                                                                                                          |
| `CHELABASE_INTERNAL_URL`                                                                                            | `http://127.0.0.1:8083`                                   | The orchestrator's internal listener, asked for the event dispatcher's database login.                                                                                  |
| `CHELABASE_COMPUTE_CA_FILE`                                                                                         | none                                                      | The CA every compute is verified against. Unset: database events aren't dispatched.                                                                                     |
| `CHELABASE_FUNCTIONS_S3_BUCKET`, `CHELABASE_FUNCTIONS_S3_ACCESS_KEY_FILE`, `CHELABASE_FUNCTIONS_S3_SECRET_KEY_FILE` | compose sets `chelabase-functions` and the generated pair | The worker's bundle-sweep identity. (The functions service uses the `CHELABASE_STORAGE_BACKEND` and `CHELABASE_STORAGE_S3_*` settings with the same bucket and pair.)   |
| `CHELABASE_FUNCTIONS_RUNTIME_MEMORY`                                                                                | `1g`                                                      | The runtime container's memory limit (a compose size); every function shares it. Settable in `.env`.                                                                    |
| `CHELABASE_FUNCTIONS_RUNTIME_TAG`                                                                                   | `latest`                                                  | The runtime image tag a workbench builds and runs. Development only.                                                                                                    |
| `CHELABASE_PUBLIC_SCHEME`, `CHELABASE_API_HOST`                                                                     | as set                                                    | What `env.CHELABASE_URL` holds, and the host `env.CHELABASE` always reaches. See [Hosts](/self-hosting/hosts).                                                          |

### External services [#external-services]

| Variable                         | Default | What it does                                                                                                                                                                        |
| -------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CHELABASE_SERVICES_ALLOW_HOSTS` | none    | Host names or IP literals, **no port**, that external services may use even if they resolve to non-public addresses. Set it on **both** the orchestrator and the functions service. |
| `CHELABASE_SERVICES_ALLOW_HTTP`  | `false` | `true` accepts `http` service URLs. Same two services.                                                                                                                              |
| `CHELABASE_INTERNAL_SUBNETS`     | none    | Comma-separated CIDRs added to the hard deny, beside every network the functions container is attached to. The compose files don't set it.                                          |

These are independent of `CHELABASE_WEBHOOKS_ALLOW_HOSTS` and `_ALLOW_HTTP`. The compose files pass `CHELABASE_SERVICES_ALLOW_HOSTS` and `CHELABASE_SERVICES_ALLOW_HTTP` through to the orchestrator and the functions service, so set them in `.env`. Internal hosts (the stack's own service names and networks) are always denied, allow-list or not.

### Service runner [#service-runner]

| Variable                                                                                         | Default          | What it does                                                                                                                                |
| ------------------------------------------------------------------------------------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `CHELABASE_SERVICE_RUNNER_MEMORY_CAP_MB`                                                         | `512`            | The most memory a container gets (MiB, at least 16). A spec may ask for less.                                                               |
| `CHELABASE_SERVICE_RUNNER_CPU_CAP`                                                               | `1`              | The most CPU a container gets (0.01 to 256).                                                                                                |
| `CHELABASE_SERVICE_RUNNER_RECONCILE_SECS`                                                        | `30`             | How often it reconciles desired and actual containers, in seconds. Settable in `.env`.                                                      |
| `CHELABASE_SERVICE_RUNNER_URL`                                                                   | none             | Where the callers reach the runner. Compose: `http://service-runner-control:8094`.                                                          |
| `CHELABASE_SERVICE_RUNNER_ADDR`                                                                  | `127.0.0.1:8094` | Its API listener. Compose: `service-runner-control:8094,127.0.0.1:8094`.                                                                    |
| `CHELABASE_COMPOSE_PROJECT`                                                                      | required         | The compose project whose `functions` container the runner attaches to each service's network. Compose sets it from `COMPOSE_PROJECT_NAME`. |
| `CHELABASE_STACK`                                                                                | `main`           | The only stack whose containers it touches.                                                                                                 |
| `CHELABASE_FUNCTIONS_SERVICE_RUNNER_TOKEN`, `CHELABASE_WORKER_SERVICE_RUNNER_TOKEN` (or `_FILE`) | required         | The bearers of its two callers, the functions service and the worker.                                                                       |

### Development only [#development-only]

| Variable                                                     | What it does                                                                                                                                     |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `CHELABASE_STUB_SERVICE_URL`                                 | Where the end-to-end test for external services finds its stub service (`infra/compose/fixtures/stub-service.yml`). Unset, that test is skipped. |
| `CHELABASE_STUB_SERVICE_BIND`, `CHELABASE_STUB_SERVICE_PORT` | The address (`172.17.0.1`) and port (`18099`) the stub overlay publishes.                                                                        |

## The `services` profile [#the-services-profile]

The runner and container services are off by default. Enable them by setting the profile:

```bash
# in .env
COMPOSE_PROFILES=services
# or
docker compose --profile services up -d
```

Without it there is no `service-runner`: container deploys, rollovers and logs answer 503 `runner_unavailable`, and [external services](/using/services/external) work as usual. The runner works with the functions service, which runs by default.

The runner is the **third holder of the Docker socket**, after the orchestrator and the worker (they start computes). It acts only on containers labelled with its stack and a service, creates them hardened (non-root, read-only, no capabilities, resource limits, no host mounts), and gives each its own private network that only the functions container joins. It does **not** filter egress. See [What the runner doesn't isolate](/using/services/containers#what-the-runner-doesnt-isolate): a container can reach the host's private and link-local addresses, and an image registry that resolves to a non-public address is refused, which narrows but can't close pull-side SSRF. Give container deploys only to tenant admins you trust with the Docker host's network.

## Security notes for operators [#security-notes-for-operators]

* **Functions share one runtime thread.** A function in a busy loop stalls every function until the watchdog restarts the runtime, about 6 to 9 seconds, and it can be repeated. Only people you trust should have Developer access to a database with functions. See [Auth modes and limits](/using/functions/auth-and-limits#a-busy-loop-stalls-every-function).
* **`env.CHELABASE` bypasses a reverse proxy** in front of the API host: it reaches the gateway directly. Filters, allow-lists and rate limits at your proxy don't see function traffic, and all of it shares one per-IP bucket at the gateway. If you list the runtime's network in `CHELABASE_GATEWAY_TRUSTED_PROXIES`, the binding already strips client-address headers, but don't trust ranges that cover the runtime.
* **A function can call itself** through the binding with no hop limit.
* **Outbound from a function** is public addresses only, enforced by the runtime; **outbound from a service** is the allow-list above plus the hard deny.
* **Database triggers keep a compute awake** while installed and enabled. The database's owner-level roles can read (and write) the outbox, and every row is vetted before delivery.
* **Platform mode:** functions and services are unavailable (403 `not_available_in_platform_mode`, and `/v1/me` reports `functions_available: false` so Studio hides the tab). A Cloudflare deployer is a later epic.
