Skip to content
cloudhostDocs
Guide

Functions

Code that runs on request: a standard web handler, with environment variables, crons, logs, observability and a preview per branch.

The handler#

A function is a module whose default export has fetch(request, env): it takes a standard Request and returns a Response. env holds the environment variables, the [vars] settings and the bindings (env.DB, env.CACHE, env.BUCKET).

export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    return Response.json({ hello: url.searchParams.get("name") ?? "world", path: url.pathname });
  },
};
  • TypeScript and JavaScript both work; the CLI bundles the entry and its imports into one module.
  • Node built-ins are there under their node: names (node:crypto, node:buffer, …).
  • fetch() reaches the internet; private addresses and the servers' internal addresses are refused.
  • Functions are written in JavaScript and TypeScript.

Deploy, versions and rollback#

Deploy
cloudhost deploy my-api index.ts --var GREETING=salam
# https://my-api.fn.trustpage.ir
cloudhost functions versions my-api
# * v3   2026-11-04T09:30:00.000Z  code 9f86d081884c
#   v2   …
cloudhost functions rollback my-api          # the version before
cloudhost functions rollback my-api --to 2

Each deploy makes an immutable version (code and settings) and makes it live. Rollback rebuilds nothing; it makes an earlier version live again. --date YYYY-MM-DD pins the runtime's behaviour to a date.

In a folder with a cloudhost.toml, plain cloudhost deploy is enough: the cloudhost.toml file.

Limits#

The size of a function's code is limited by plan. The code is measured as it is stored: all its modules together, compressed with gzip; before compression it is at most 64 MiB on every plan. Static files belong to apps: app limits.

LimitHobbyProEnterprise
The function's code, compressed3 MB10 MBBy contract
The function's code before compression64 MB64 MBBy contract

cloudhost deploy checks a deployment against your plan's limits before it uploads anything, and a build on our servers does the same before its output is uploaded; the API checks again. A deployment over a limit is refused with the code limit_exceeded (HTTP 413), and the message names the limit, your deployment's size, your plan's number and Pro's, for example: "Static files in a deployment: this deployment has 25,000 files; the Hobby plan allows 20,000 files, Pro allows 100,000 files." Enterprise limits are set by contract. The same limits are on pricing, and GET /v1/limits returns them for your plan and every plan.

How functions scale#

Every function runs on every server. When it is busy, each server runs more copies of it and spreads requests across them: an extra copy starts when requests queue or latency grows, and stops when it is idle. Each extra copy has a cold start. The number of copies on a server goes up to your plan's limit:

LimitHobbyProEnterprise
Copies per server28By contract

When every copy is busy and you are at the limit, a request waits in a brief queue (about 1 second) for a free copy. If none frees, the answer is HTTP 429 (the function is at its plan's copies limit) or HTTP 503 (the server is full), both with Retry-After: 1. Retry after that time. The Observability page shows copies over time, queue wait, extra copies started and requests turned away.

In the browser: starters and Edit code#

In the dashboard, Create new → Function opens a starter in the code editor: hello world, a JSON API, a webhook receiver, a cron job, with a database (env.DB) or with a cache (env.CACHE). Name it, change the code and press Deploy: the function and its first version are made. For the starters that read a database or a cache, you pick one of the team's; the cron starter sets its schedule too.

Code on a function's page opens the live version's files in the editor. Changes shows how they differ from the live version; Deploy a new version makes a version and makes it live, and Save without deploying only makes the version. The bindings (env.DB and so on) stay those of the live version. Editing is for versions made from plain source (the editor, cloudhost functions new, cloudhost functions source deploy or the MCP tool); a version that was built, or bundled by cloudhost functions deploy, does not hold the code you wrote, so edit it in your own folder or repository.

Terminal
cloudhost functions starters
cloudhost functions new my-api --starter json-api
cloudhost functions new visits --starter database --db DB=shop
cloudhost functions source my-api ./my-api          # the live version's files
cloudhost functions source deploy my-api ./my-api   # deploy them as they are

A function's source is up to 10 files and 256 KB on Hobby, 50 files and 4 MB on Pro. Each file is checked before the deploy, and a syntax error comes back with its line.

Environment variables#

type Env = { GREETING?: string; API_KEY?: string; CLOUDHOST_ENV?: string };

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    return Response.json({ greeting: env.GREETING ?? null, has_key: !!env.API_KEY, environment: env.CLOUDHOST_ENV });
  },
};

Each variable has a key, a value and the environments it applies to: production (the main address), preview (every branch preview) and development (never deployed; for cloudhost env pull and work on your own computer). One key can have a different value in each environment. For preview, a value can target one git branch: that branch's previews get it over the preview value. A plain value can be shown again to owners, admins and developers; a sensitive value can never be read back after saving, not even by its creator, only replaced, and is for production and preview only. A change applies at once: running functions restart with the new value, no new deployment is needed. Values are encrypted with a key of the team's own and never appear in logs or the audit log.

Team shared variables are declared once for the team and linked to chosen projects; a project's own variable with the same key wins over them. With the team policy sensitive by default (owner only), every new production and preview variable is sensitive. [vars] in cloudhost.toml or --var are plain settings and part of the version; an environment variable of the same name wins over them. The old secrets commands still work: secrets put sets a sensitive variable for production and preview.

`[vars]` settingEnvironment variable
Name^[A-Za-z_][A-Za-z0-9_]{0,63}$^[A-Za-z_][A-Za-z0-9_]{0,63}$
How manyup to 64up to 128 per project; up to 256 shared per team
Value sizeup to 5 KiBup to 5 KiB
A value per environmentno: the version'syes, and per preview branch
Changing ita new deploymentat once
System variableValue
CLOUDHOST_ENVproduction or preview
CLOUDHOST_URLthis deployment's address, without https://
CLOUDHOST_GIT_BRANCHthe preview's branch (preview only)
CLOUDHOST_GIT_COMMITthe commit, when it came from a git build

System variables are given to every deployment, unless a variable, setting or binding already has the name.

Crons#

export default {
  async fetch(): Promise<Response> {
    return new Response("I also run on a schedule\n");
  },
  async scheduled(event: { cron: string; scheduledTime: number }): Promise<void> {
    console.log(`tick for ${event.cron} at ${new Date(event.scheduledTime).toISOString()}`);
  },
};

Standard 5-field crons (minute, hour, day of month, month, day of week; with *, lists, ranges, steps and month and day names) call the live version's scheduled(event, env), at most once per tick. Up to 5 per project; the default time zone is Asia/Tehran. crons set replaces the whole list.

Logs, the live log and usage#

Logs
cloudhost functions logs my-api --limit 50
# 09:30:01.204  v3  GET /orders 200 12ms
# 09:30:01.210  v3  [log] handling /orders
cloudhost functions tail my-api          # live, Ctrl-C to stop
cloudhost functions usage my-api --from 2026-11-04T00:00:00Z --to 2026-11-04T06:00:00Z

Every console.log, every request and every cron run is a log line. logs gives the newest lines (1–1000, default 100), tail follows the log live, and usage shows requests, errors, time and traffic per minute (default the last hour, at most 31 days).

Observability#

Requests, 4xx and 5xx errors and p50, p95 and p99 latency over time and per route, over 1 hour, 24 hours or 7 days. Latency is measured from request to response, cold starts included. In the dashboard, with the MCP tool get_observability, or from the API:

API
curl -H "Authorization: Bearer $KEY" "https://api.ibel.ai/v1/functions/my-api/observability?range=24h"

To hear when something breaks, set an alert.

Branch previews#

Preview
cloudhost deploy my-api index.ts --branch feature-x
# deployed function my-api v4 as preview "feature-x"
# https://feature-x--my-api.fn.trustpage.ir
cloudhost functions previews my-api
cloudhost functions previews delete my-api feature-x

Every branch other than main, master and production gets its own address: <branch>--<name>. Production is not touched. A preview bound to a database gets its own database branch (preview-<branch>), so it never writes production data.

Builds on our servers#

build
cloudhost build                     # production
cloudhost build --branch feature-x  # a preview
cloudhost functions builds my-api

cloudhost build sends the project folder (without node_modules, .git and build outputs); dependencies are installed, and the project is built and deployed on our servers, with the live log in your terminal. The build history is in the dashboard, in builds and in the MCP tool get_build_log.

Deploy with git push#

git
git remote add cloudhost https://git.ibel.ai/my-api.git
git push cloudhost main                 # production
git push cloudhost feature-x            # https://feature-x--my-api.fn.trustpage.ir
git push cloudhost --delete feature-x   # removes that preview

Each push is a build: main goes to production and any other branch to its own preview. git asks for a username and password: the username can be anything and the password is an API key (cloudhost keys create git --role developer). The address only receives pushes; there is no clone or browsing.

The project must exist first (a deploy, or cloudhost functions create my-api). A function's repository needs a cloudhost.toml with main.

Move to another team#

An admin or owner of both teams can move a function or app to another team from the dashboard. The address and its environment variables stay (links to the old team's shared variables are removed); its alert rules are deleted. It is refused while a version binds a database, cache or bucket; deploy a version without bindings first.