Skip to main content
Functions run as real containers, so configuration reaches your code as ordinary process environment variables — process.env, os.environ, os.Getenv, System.getenv. There is no separate configuration API to learn. Stateful Actors read theirs the same way, through process.env, using the scoped tables in telnyx.toml.

What’s in the environment

Declaring variables

Define non-sensitive configuration under [env_vars] in func.toml (or telnyx.toml; for actors, see Scoped variables for actors):
Three behavioral contracts:
  • All values are strings. Parse numbers and booleans in your code.
  • Changes take effect on the next telnyx-edge ship — there is no live update.
  • Names share the env namespace with bindings. If an [env_vars] entry has the same name as a declared binding — or is named SECRETS while a [[secrets]] block is declared — ship warns that one shadows the other on env and still proceeds; rename one. (A binding named SECRETS, or a duplicate [[secrets]] handle, is a hard error.)
[env_vars] values are plaintext in the manifest and end up in version control. Put credentials in secrets instead.

Reading variables

Scoped variables for actors

In a telnyx.toml project, [env_vars] also takes scope sub-tables, and each [[actors]] block can carry its own env_vars. This is how a Stateful Actor gets configuration — keys written directly under [env_vars] reach the function only. A func.toml project accepts the scoped tables too; with no actors declared, only the function-reaching scopes have any effect.
Each workload’s environment is resolved separately, layer by layer, and on a key collision the more specific layer wins:
  • Function = [env_vars.shared] → [env_vars] → [env_vars.function]
  • Actor type = [env_vars.shared] → [env_vars.actor] → that type’s [[actors]] env_vars
So in the example the function sees LOG_LEVEL=info, REGION=us-east; Call sees LOG_LEVEL=debug, MAX_TURNS=20, REGION=us-east; Queue sees LOG_LEVEL=warn, REGION=us-east. Nothing in [env_vars.function] or a flat key ever reaches an actor, and nothing actor-scoped reaches the function. Actor code reads the result from process.env, exactly like the function:
Things to know:
  • One actor type has one environment. Several [[actors]] blocks may bind the same type, but they must declare identical env_vars (one block setting them and another omitting them counts as a conflict) — otherwise ship fails. To give every handle the same values, put them on each block or use [env_vars.actor].
  • Actor env applies from the function that defines the actor class. A reference binding to a type another function owns does not deploy that type’s runtime, so any env_vars on that block, [env_vars.actor], and the actor half of [env_vars.shared] are not applied — ship warns and proceeds. Declare actor env in the owning function’s manifest.
  • Changes take effect on the next ship, like any other [env_vars] change. A variable you delete is removed from the actor on that deploy.
  • Unknown scope names are rejected — [env_vars.actors] (plural) is an error, not silently ignored. Every value must be a string.

Testing locally

telnyx-edge dev (CLI v0.5.12 and later) applies the same resolution: it runs one actor runtime per actor type, each with only its own resolved environment, and gives the function its function-scope set. It validates the manifest with the same rules as ship, so a manifest ship would reject fails locally first. On start it reports what it passed:
PATH, PORT and DAPR_HTTP_PORT in an actor scope (and PORT, DAPR_HTTP_PORT, DAPR_HOST, TELNYX_CONFIG_PATH and FUNCTION_ENTRY for the function) are set by the local stack itself, so dev ignores your value with a warning.

Names, values, and limits

ship rejects a manifest that breaks any of these, naming each offending key:
  • Names use letters, digits, and underscores, start with a letter or underscore, and are at most 255 characters.
  • Values are strings of at most 10,000 bytes, and may not begin with {{ — that syntax is a deploy-time template, not a literal. To reference a secret, use secrets instead.
  • At most 50 variables per workload, counted after resolution: the function’s resolved set and each actor type’s resolved set are capped independently.
  • Reserved everywhere: TELNYX_RUNTIME_BINDINGS, TELNYX_ACCOUNT_ID, TELNYX_BINDINGS_JSON, EC_ACTOR_ROUTER_URL, EC_ACTOR_WS_DIAL.
  • Reserved in any scope that reaches an actor ([env_vars.shared], [env_vars.actor], [[actors]] env_vars) — the actor runtime sets these itself: ACTOR_ENTRY, BUNDLE_REF, POD_IP, SQLITE_SIDECAR_PORT, TELNYX_ACTOR_DATA_DIR, TELNYX_ACTOR_METHOD_TIMEOUT_MS, TELNYX_ACTOR_STATE_STORE, TELNYX_API_BASE_URL, TELNYX_BUILTIN_ACTOR_TYPE, TELNYX_CONFIG_PATH, TELNYX_OBJECTSTORE_BUCKET, TELNYX_OBJECTSTORE_HOST, TELNYX_OBJECTSTORE_REGION, TELNYX_SECRETS_BINDING, TELNYX_SQL_ENGINE, TELNYX_WS_DIAL_AUTH, TELNYX_WS_DIAL_AUTH_KEY, TELNYX_WS_DIAL_AUTH_KEY_PREVIOUS.

Environment variables vs secrets

Next Steps

  • Secrets — the server-side counterpart for sensitive values, including the typed env.SECRETS surface for TypeScript
  • Bindings — typed, pre-authenticated handles instead of raw variables
  • Configuration — the full manifest reference: every func.toml and telnyx.toml key