Reference

Environment Variables

The environment contract of a self-managed ObjectOS deployment, Community or Enterprise — what each variable decides, which edition reads it, which combinations fail at startup, and which names are retired.

Environment variables carry deployment-level decisions: which image is running, where the data lives, and what this deployment is entitled to be. Anything a tenant or an administrator should be able to change while the deployment is running belongs in system settings instead.

This page is the contract — what each variable decides, and what happens when two of them disagree. The Edition column says which image reads it:

  • Both — the open Community image, ghcr.io/objectstack-ai/objectstack, and the licensed Enterprise image alike.
  • Enterprise — only the Enterprise image, or the deploy bundle that ships with it.

On Community, Docker shows the values to set. On Enterprise, the values you copy live in the deploy bundle that ships with your release: an annotated template you copy to .env and edit. That template is pinned to your release; this page is not. Where the two differ, the template shipped with your image wins.

Naming. ObjectOS-owned variables use the OS_ prefix. A few pre-1.0 unprefixed names still work and emit a one-shot deprecation warning — see Legacy names. Two other groups are worth knowing before you reach for a name you remember: retired names, which are read by nothing or refuse the boot outright, and legacy names that still resolve.

The decisions every deployment makes

VariableEditionDecides
OS_EE_IMAGEEnterpriseWhich image runs. Read by the bundled Compose stack, not by the runtime. Pin the digest, never a tag — see Docker.
OS_AUTH_SECRETBothThe base secret sessions are signed from. Unset, the sign-in endpoints are skipped and the server runs with no authentication. Must be identical on every replica. Rotating it invalidates every existing session.
OS_DATABASE_URLBothThe database the deployment runs on. Unset, the runtime writes a SQLite file under its home directory, which a container loses with its filesystem. In production, point this at your managed PostgreSQL — and on Enterprise, remove the bundled database service.
OS_SECRET_KEYBothThe key every stored secret is encrypted under: encrypted settings, secret fields, datasource credentials. Unset, the runtime mints one and keeps it on its own filesystem, which a container loses on restart. Set it to a 32-byte value (openssl rand -hex 32), keep it in your secret manager, and back it up with the database. On several replicas it is required and must be the same value on every one — see Multi-node.
OS_DB_USER, OS_DB_PASSWORD, OS_DB_NAMEEnterpriseProvision the bundled database service in the shipped Compose stack. The credentials inside OS_DATABASE_URL must match them. Irrelevant once you use a managed database.
AI_GATEWAY_API_KEYEnterpriseThe AI provider credential behind the "ask your data" agent.

License and cloud posture

These two are a pair. Setting one without reading the other is the most expensive mistake on this page, because unsupported pairings are refused at startup rather than degraded.

VariableEditionDecides
OS_LICENSE_KEYEnterpriseThe deployment's entitlement. A license also has a mode — ordinary licenses are validated online against a control plane; air-gap licenses are verified locally with no network traffic at all. The mode is a property of the license you were issued, not something you configure here.
OS_CLOUD_URLBothWhether this deployment has a control plane, and which one. off (and its aliases none, local, disabled) means no control plane: no marketplace, and no license validation either.

On Community there is no license, so OS_CLOUD_URL decides only whether the deployment connects to a control plane. On Enterprise the two variables are a pair, and two consequences follow — both surprise people:

  • Unset is not "off" — on either edition. An unset OS_CLOUD_URL resolves to the public control plane. It was never a neutral value — it selects the connected behaviour. A deployment meant to talk to nobody must say off out loud.
  • An ordinary license with off is refused at startup, naming both variables. It is not a degraded state but an impossible one: an online license is validated by an exchange that needs a control plane, so with none configured the exchange is never made — not on this boot and not on a later one — and the grace window never starts, because grace runs from the last successful validation.

Air-gapped Deployment carries the full matrix of supported combinations and the reasoning behind the refusal.

Multi-organization deployments

A walled tenancy posture puts up the per-organization isolation wall. It is a licensed capability, and it makes three further decisions mandatory: the runtime refuses to start until each is declared. Declaring any available value is accepted; not deciding is not.

VariableEditionDecides
OS_TENANCY_POSTUREBoth; walled values EnterpriseWhether organizations are isolated from one another. Unset, or single, is one organization. The walled values are isolated and group. A walled posture requires a license: without one the runtime exits at startup rather than serve traffic while pretending to be isolated.
OS_AUTH_MEMBERSHIP_POLICYBothWhat a fresh sign-up joins. auto binds every new user to the deployment's default organization — right for a single-organization box, wrong where a membership is the tenant boundary. invite-only grants membership only by an explicit act: creating a workspace, accepting an invitation, an admin, or SSO provisioning. Also settable in Setup → Authentication → Membership; setting it here pins it and makes the Setup field read-only.
OS_AI_STUDIO_AGENTSEnterpriseWhich AI agents are mounted, as a comma-separated subset of ask and build. Unset mounts both. ask reads; build authors metadata — and metadata is scoped to the deployment, not to an organization, so on a shared database one customer's build turn rewrites the schema every other customer runs on. The AI seat does not cover this: it is one flag for both agents. A misspelled value fails the boot rather than quietly falling back to mounting both.
OS_PLATFORM_OWNER_EMAILBothWho the deployment's platform administrators are: one email address, or a comma-separated list. Under a walled posture it is the only source of platform-administrator standing — since ObjectStack 17.5 an admin_full_access grant no longer confers it there — and unset or blank, the runtime refuses to start. A listed account holds the standing only once its address is verified, so the deployment needs a way to verify it: an email transport, or a trusted federated sign-in. One unparseable entry voids the whole list. A change takes effect on restart.

A single-organization deployment sees none of these checks. It may leave the last three unset: the first account to register becomes the platform administrator, unless OS_PLATFORM_OWNER_EMAIL names one, in which case that verified account does.

Multi-node

Multi-node is an Enterprise capability. A Community deployment runs one replica, and declaring more — OS_CLUSTER_REPLICAS above 1 — refuses the boot rather than run replicas on per-process locks. Scaling past one replica changes what is mandatory: a cluster driver, and the same OS_SECRET_KEY and OS_AUTH_SECRET on every replica.

VariableEditionDecides
OS_CLUSTER_DRIVEREnterpriseTurns on the shared coordinator. Unset means one replica on the built-in in-memory coordinator.
OS_REDIS_URLEnterpriseWhere that coordinator lives.
OS_CLUSTER_REPLICASEnterpriseHow many application replicas the bundled stack starts. The runtime reads it too, as the topology you declared: above 1 without a multi-node license, the boot is refused.

OS_SECRET_KEY becomes required once a cluster driver is set. With no cluster driver each replica mints and persists its own key; across replicas those keys would silently diverge, so the runtime refuses to boot instead of serving with inconsistent crypto.

Running a published app

The runtime image and the app it serves are independent release axes: one variable names a published artifact, and upgrading the app is a change to that variable plus a restart — no image rebuild.

VariableEditionDecides
OS_ARTIFACT_URLBothThe one variable that selects the app. An absolute https://, http:// or file:// URL naming a built objectstack.json, with an optional SRI-style integrity pin in the URL fragment (#sha256= followed by 64 hex characters). A pinned artifact whose bytes do not match refuses the boot, naming the expected and the actual digest. Overrides the artifact the Community image looks for at /srv/app/objectstack.json. On the Enterprise image it is resolved before any configuration is read, so the image's own enterprise wiring is not loaded on this path.
OS_COMPOSED_ARTIFACT_URLEnterpriseSelects the app for the composed shape — a published app booted together with the image's enterprise plugins, several organizations sharing one database. That shape has its own template in the deploy bundle, is air-gap-licensed only, and requires OS_CLOUD_URL=off; both are enforced at startup. Setting both this and OS_ARTIFACT_URL refuses the boot.

There is deliberately no companion variable for the integrity hash. The pin lives in the URL fragment, so there is one value to copy and one to rotate, and "URL updated, hash not" is not a state you can spell.

Publish to immutable, version-named objects, give the artifact host's write credential to your publishing pipeline only, and pin the digest in production. Those three rules are what make rollback — the same operation as upgrade, with the older URL — actually work.

Runtime services

VariableEditionDecides
OS_PORTBothThe HTTP port the runtime listens on. The Community image sets 8080. os start never moves to another port: a busy one fails the boot. Legacy alias: PORT.
OS_AUTH_URLBothThe public base URL of the sign-in server: callbacks and the cookie origin. Required behind a proxy and in production.
OS_CORS_ORIGINBothOrigins admitted to cross-origin requests, comma-separated. Unset, none is admitted — and none needs to be while the Console and the API share the public origin. * is refused at boot while credentials are on.
OS_ENVIRONMENT_IDBothThe environment id a single-environment runtime reports as its own. A completed cloud binding persists this, after which a self-hosted runtime does not need it set.
OS_TRUSTED_ORIGINSBothAdditional trusted origins, comma-separated.
OS_ROOT_DOMAINBothRoot domain used to trust subdomains in platform-SSO deployments.
OS_DATABASE_DRIVERBothOverrides the driver otherwise inferred from the OS_DATABASE_URL scheme. Set it only when the scheme cannot be inferred; an unsupported value fails rather than falling back. Since ObjectStack 17.7 the in-memory engine is refused as a boot store, whether it is named here (memory) or by a memory:// URL.
OS_DATABASE_AUTH_TOKENBothAuth token for managed drivers that use one.
OS_SKIP_SCHEMA_SYNCBothSet to 1 to skip the boot-time schema sync. Use when schema is managed out of band — the Enterprise deploy bundle's stack already runs migrations as a separate one-shot step before any replica starts.
OS_TELEMETRY_DBBothA dedicated datasource for lifecycle-classed system data. Set to 0 to opt out, or to a path or URL to opt in.
OS_STORAGE_LOCAL_ROOTBothWhere the local file-storage adapter keeps uploads, relative to the working directory (default ./.objectstack/data/uploads). Back it up with the database. Setting it pins Setup → Settings → File Storage → Root directory.
OS_LIFECYCLE_DISABLEDBothSet to 1 to disable the lifecycle service (retention reaper, table rotation, audit archiver). Retention windows themselves are tuned through the lifecycle.retention_overrides setting, not through environment variables.
OS_AUTOMATION_SCHEDULED_WORK_ENABLEDBothWhether the deployment runs scheduled work that apps and packages declare: time-triggered flows (a schedule, or a time-relative sweep) and packaged defineJob cron jobs. Only true, 1, on or yes turn it on; unset is off, the ObjectStack runtime's default since 17.5, in every tenancy posture. While it is off, nothing on a clock arms, and each such flow is reported as disabled by deployment policy. The platform's own scheduled work (approval escalation, retention, message dispatch) is not gated. Under isolated, a scheduled flow or job must also declare the organization it runs as. ⚠️ Whether your ObjectOS image or the deploy-bundle template sets this for you is not something this page can promise: set it explicitly if your app depends on scheduled flows, and read the effective value with os doctor.
OS_MCP_SERVER_ENABLEDBothGoverns the Model Context Protocol server over HTTP at /api/v1/mcp, and only that surface. On by default: set an explicit falsy value (false, 0, off, no) to disable it. An authenticated principal is always required.
OS_MCP_STDIO_ENABLEDBothSet to a truthy value to auto-start the long-lived stdio MCP transport. Off by default, and independent of the HTTP surface above.
OS_MCP_STDIO_API_KEYBothThe API key that stdio transport is principal-bound to, resolved through the same authorization chain as HTTP MCP, so record- and field-level security still apply. Fails closed: with stdio enabled and no resolvable key, the transport does not start.

Observability

Export is opt-in. The exporter defaults to noop, so a deployment emits nothing until you select one — setting an endpoint alone does nothing.

VariableEditionDefaultDecides
OS_OBS_EXPORTERBothnoopnoop, console, json or otlp. Use console or json for local debugging and otlp for a collector.
OS_OTLP_ENDPOINTBoth—OTLP/HTTP root URL. Required when the exporter is otlp; if it is empty the runtime warns and falls back to noop.
OS_OTLP_HEADERSBoth—Extra OTLP headers, as comma-separated key=value pairs.
OS_OBS_SERVICE_NAMEBoth—The service.name resource attribute on emitted metrics.
OS_OBS_DEPLOYMENT_ENVBothproductionThe deployment.environment resource attribute.
OS_OTLP_FLUSH_MSBoth—Flush interval for the OTLP exporter, in milliseconds.

Data-contract escape hatches

ObjectStack 17.0 made the platform refuse values that break their declared contract, where it used to warn and accept them. These variables re-open the old behaviour temporarily, for an integration you cannot fix today. Each takes exactly 1; anything else leaves the strict behaviour on. The violation is still logged, so the drift stays visible.

VariableEditionRe-opens
OS_ALLOW_LAX_ACTION_PARAMSBothAn action call whose params miss a required parameter, break its options / multiple / reference declaration, or carry an undeclared key. Without it, such a call answers 400 VALIDATION_FAILED.
OS_ALLOW_LAX_MEDIA_VALUESBothA malformed file / image / avatar / video / audio value, where those values are checked strictly.
OS_ALLOW_LAX_VALUE_SHAPESBothA malformed reference (lookup, master_detail, user, tree) or structured JSON value (location, address, …), where those values are checked strictly.

Media and value shapes are checked strictly on a database the 17.x runtime created. A database upgraded from an earlier major is checked strictly only once the matching migration has recorded its flag — see CLI → Data migrations. Do not reach for OS_DATA_VALUE_SHAPE_STRICT_ENABLED=1 to get there sooner: it turns strict checking on for every value class at once, whatever this deployment's data actually holds.

Pinning a system setting

System settings — the tenant- and administrator-editable ai, email, feature_flags and similar namespaces — can be pinned at the deployment level with a variable named OS_ plus the namespace plus the key, uppercased, with . and - replaced by _. For example ai.openai_base_url becomes OS_AI_OPENAI_BASE_URL, and feature_flags.ai_enabled becomes OS_FEATURE_FLAGS_AI_ENABLED. A pinned setting is read-only in Setup.

Google sign-in, configurable in Setup → Authentication, also reads GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET at the deployment level.

Retired names

These names appeared in earlier ObjectOS documentation. They are listed here so that a configuration file you inherited can be read and corrected — not so they can be used. Each is retired on both editions, except where its row says otherwise.

Retired nameWhat to use instead
OS_ARTIFACT_FILEOS_ARTIFACT_URL. Nothing in the shipped runtime reads OS_ARTIFACT_FILE; a deployment that sets it gets the boot it would have got with nothing set, and no error naming the mistake. A local file is file:// on the replacement, and the replacement carries the integrity pin.
OS_ARTIFACT_PATHOS_ARTIFACT_URL. The Enterprise image refuses a boot that carries a non-default OS_ARTIFACT_PATH, naming the replacement. Two spellings for "which artifact do I boot" is one dialect too many, and the retired one never carried an integrity pin. The Community image still sets it to /srv/app/objectstack.json, the path it looks for: leave it alone, mount the artifact there, and name any other artifact with OS_ARTIFACT_URL.
OS_BUSINESS_DB_URLOS_DATABASE_URL, or a datasource declared in the artifact. Nothing reads OS_BUSINESS_DB_URL; a deployment that sets it and nothing else has no database configured at all.
OS_LOCALIZATION_DATE_FORMAT, OS_LOCALIZATION_TIME_FORMAT, OS_LOCALIZATION_NUMBER_FORMAT, OS_LOCALIZATION_FIRST_DAY_OF_WEEKNothing: ObjectStack 17.7 retired the settings they pinned, and dates, times, numbers and the week start now follow the language. See Localization.

OS_CLOUD_API_KEY is not a variable a self-hosted deployment sets. It is the service credential the hosted cloud injects into runtimes it operates itself, and — separately — the bearer token os package publish uses to authenticate against a package catalog from CI (see Packages). A self-hosted deployment that connects to a control plane presents a runtime token minted when the deployment was bound to that control plane, not a key you paste into a file.

Legacy names

These still resolve on both editions, but the canonical name is preferred and the legacy one may be removed in a future major version.

CanonicalLegacyNote
OS_PORTPORTEmits a one-shot deprecation warning.
OS_AUTH_SECRETAUTH_SECRETEmits a one-shot deprecation warning.
OS_TENANCY_POSTUREOS_MULTI_ORG_ENABLEDThe posture derives from the legacy boolean only when OS_TENANCY_POSTURE is unset: true there means isolated. Declare the posture directly — it is the variable the license and startup checks are written against.
OS_MULTI_ORG_ENABLEDOS_MULTI_TENANT