TabulariaDocs

DocumentationInstall & operate

Configuration reference

Every setting of Tabularia is an environment variable. This page lists all of them: the ones the gateway and the engine read, the frontend's, and the few that only the Docker Compose files and the Helm chart use. It is generated from the code by .github/scripts/config_reference.py, and a check fails when a variable is added, removed or changed without the page following: edit the code or config_reference.toml, never this page.

Nested names. The settings are grouped in sections, and a variable names the section and the field with a double underscore: STORAGE__BUCKET is the bucket field of the storage section, CLICKHOUSE_EXTERNAL__S3_ENDPOINT the s3_endpoint field of clickhouse_external. Write them in capitals. Booleans are true or false, lists are JSON (APP__CORS_ORIGINS=["https://app.example.com"]), and a variable left out keeps the default shown below. Keep comments on their own line: not every tool strips a trailing # comment from a value.

Where to set them.

  • Docker Compose: in infrastructure/.env, a copy of infrastructure/.env.example. The gateway and engine containers read the whole file, so one value reaches every service that needs it. Compose is the way to try Tabularia; production runs on Kubernetes with the Helm chart (Kubernetes guide).
  • Helm: the Helm value column gives the key of values.yaml the chart renders the variable from. A variable without one goes in extraEnv, by its name (it lands in the shared ConfigMap; the chart refuses a name it already sets), or, for a secret, in the Secret you name in existingSecret. See Deploying on Kubernetes.

Who reads what. The gateway is the control plane (API, sign-in, permissions, schedules, flow orchestration), in one process or split into api and orchestrator copies (APP__ROLE). The engine is one image with several roles: the internal engine API, the run and preview workers, and the beat scheduler; they all read the same settings. The frontend reads a few public values at startup. A variable only matters to the service it is listed under; giving it to the others is harmless.

Secrets are never printed here. required means the process refuses to start without the variable; development value (hidden) means a placeholder that works on a laptop and must be replaced before anyone else can reach the installation (the gateway refuses some of them when APP__ENV_NAME=production).

Not in the environment. Some things are set in the administration panel instead: the engine parameters marked Also adjustable in Administration → Settings (the variable is the starting value, see engine settings), the processes per worker (Administration → Workers), the AI models people may use, the engines allowed, the forwarding of the audit log to a SIEM (Splunk), webhook receivers and service accounts (API), and the banners. Connection credentials (databases, SFTP, S3, APIs, SharePoint) belong to each connection, encrypted with SECURITY__FERNET_KEY.

Before production

The defaults are made for a laptop. Before an installation is reachable by anyone else, set at least these (the release checklist lists what changed between versions):

  • STORAGE__ACCESS_KEY — use credentials of your own (minioadmin is the development MinIO's).
  • STORAGE__SECRET_KEY — use credentials of your own.
  • SECURITY__FERNET_KEY — generate a key of your own and keep it with your backups: without it the saved connections cannot be decrypted.
  • DB__PASSWORD — replace the development password (the gateway refuses it in production unless DB__URL is set).
  • JWT__SECRET — generate a secret of at least 32 characters (the gateway refuses the default and shorter ones in production).
  • AUTH__ADMIN_PASSWORD — set a password of your own before the first start (at least 12 characters; the gateway refuses admin and other published passwords).
  • APP__ENV_NAME — set it to production, so the gateway refuses the development secrets.
  • SECURITY__KEYS_DIR — on Kubernetes leave it empty and set SECURITY__FERNET_KEY; with Compose, back the keys volume up with the database.
  • AI__MAX_COST_PER_DAY_USD — set it, and AI__MAX_COST_PER_DAY_TOTAL_USD, wherever people you do not know can sign in (every question is paid with your key).
  • AUTH__FIRST_RUN_SETUP — leave it off: the administrator comes from AUTH__ADMIN_PASSWORD.
  • NOTEBOOKS__ORIGIN — with notebooks, set it to a host name of their own next to the interface's, routed to the gateway: without it their apps and editors do not open.
  • GRAFANA_ADMIN_PASSWORD — change it wherever Grafana is reachable by others.

Shared by the gateway and the engine

Read by the gateway and by the engine. Give both the same value: the Compose file and the chart do, since every container reads the same .env or ConfigMap.

VariableTypeDefaultHelm valueDescription
APP__ENV_NAMEstringdevelopmentapp.envNameProduction: set it to production, so the gateway refuses the development secrets. production turns on the gateway's production guard: it refuses to start with the development secrets (JWT__SECRET, DB__PASSWORD), an empty or published AUTH__ADMIN_PASSWORD, or a JWT__SECRET shorter than 32 characters. The engine accepts it and nothing there depends on it.
APP__CORS_ORIGINSJSON list of strings; * and null refused["http://localhost:3000", "http://127.0.0.1:3000"]app.corsOriginsOrigins allowed to call the API from a browser, with the session cookie. Needed only when the interface is served from another host than the gateway (then set AUTH__COOKIE_DOMAIN too). The engine API applies the same list to its own CORS.
STORAGE__ENDPOINTstringhttp://localhost:9002externalServices.storage.endpointURL of the S3-compatible object storage, as the workers reach it (http://minio:9000 in Compose). The gateway reads it only to name the storage in OpenLineage events (gs:// for Google Cloud Storage).
CELERY__TASK_TIME_LIMITinteger3600engine.taskTimeLimitSecondsSeconds after which the worker kills a task: the real ceiling on a run, an import or a preview. The gateway reads it too, to derive ENGINE__RUN_STALE_TIMEOUT_SECONDS. Also adjustable in Administration → Settings.
SECURITY__FERNET_KEYstringemptysecrets.fernetKeyProduction: generate a key of your own and keep it with your backups: without it the saved connections cannot be decrypted. Encrypts the credentials of database and file connections, at rest in the gateway and on their way to the engine, which decrypts them only inside the worker. The same value everywhere; never change it once a connection is saved. Empty with SECURITY__KEYS_DIR set (the Compose stack): the key created there at the first start. Empty without it: the services refuse to start. Generate one with openssl rand -base64 32 | tr '+/' '-_'.
SECURITY__KEYS_DIRstringemptyextraEnvProduction: on Kubernetes leave it empty and set SECURITY__FERNET_KEY; with Compose, back the keys volume up with the database. A directory every service shares on one machine (the Compose stack mounts the keys volume there): the secrets left empty are created in it at the first start and read afterwards, fernet.key for SECURITY__FERNET_KEY and, with AUTH__FIRST_RUN_SETUP, bootstrap.token, which the sample-data container exchanges for a session once the administrator exists. Back it up with the data. Not for Kubernetes, where the pods share no volume: the chart sets the key.
UPLOAD__MAX_BYTESinteger, ≥ 01073741824 (1 GiB)extraEnvThe largest file a person can upload, in bytes; 0 = no ceiling. The gateway answers 413 before calling the engine when the size is declared, and cuts an undeclared (chunked) body at the ceiling; the engine API refuses it too. The file is spooled on the engine's disk and converted there, so this is also a disk bound: keep the engine's temporary space above it. Imports from SFTP, S3 and APIs have their own caps (SFTP__*, S3_SOURCE__*, API__*). Also adjustable in Administration → Settings.

Gateway

The control plane: in Compose the gateway service; on Kubernetes the gateway (api) and orchestrator workloads.

Process and HTTP

Read by both services, so listed under Shared by the gateway and the engine: APP__CORS_ORIGINS, APP__ENV_NAME.

VariableTypeDefaultHelm valueDescription
APP__NAMEstringTabulariaextraEnvProduct name shown by the API. Product identity, not a deployment setting: leave it.
APP__VERSIONstring1.3.0extraEnvThe release, shown by /system/info and in the interface's menu. Set by the code; leave it.
APP__CODENAMEstringCatoneextraEnvThe release's name. Set by the code; leave it.
APP__ROLEone of all, api, orchestratorallset per workload: api for the gateway, orchestrator for the orchestratorWhat this gateway process does: all answers requests and does the background work (schedules, flow executions, clean-up); api only answers; orchestrator only does the background work. Split them to run several copies: they share the work through the database.
APP__ROOT_PATHstring; a path such as /apiemptyingress.apiPrefixPath prefix a reverse proxy publishes the gateway under, stripping it before forwarding (/api when one host serves interface and API). It fixes the paths the gateway writes for the browser: the SSO cookie, /docs, redirects. Empty = published at the root.
APP__GATEWAY_THREADSinteger, ≥ 440gateway.threadsThreads serving the synchronous routes. More do not serve more (Python runs one at a time); the database pool follows this number. A different name from the engine's APP__ENGINE_API_THREADS because both read the same file and want opposite values. Also adjustable in Administration → Settings, applied after a restart of the gateway and orchestrator.
APP__TIMEZONEstring; an IANA name, e.g. Europe/RomeUTCapp.timezoneTime zone in which cron schedules are read (wall-clock time, daylight saving included); they are stored in UTC. Timestamps are still shown in each viewer's own time zone. Also adjustable in Administration → Settings.

Metadata database (Postgres)

Users, groups, permissions, flows, connections, runs, schedules and the audit log. The schema is migrated with Alembic: see database migrations.

VariableTypeDefaultHelm valueDescription
DB__HOSTstringlocalhostexternalServices.postgres.hostPostgres host.
DB__PORTinteger5432externalServices.postgres.portPostgres port.
DB__USERstringtabulariaexternalServices.postgres.userPostgres user. The development Compose file also creates the database with it.
DB__PASSWORDstringdevelopment value (hidden)secrets.dbPasswordProduction: replace the development password (the gateway refuses it in production unless DB__URL is set). Postgres password.
DB__NAMEstringtabulariaexternalServices.postgres.databasePostgres database.
DB__URLstringemptyexistingSecretA full DSN (postgresql://user:password@host:5432/dbname) for a managed Postgres that does not fit the pieces above; when set it wins over them. It holds the password: treat it as a secret.
DB__POOL_SIZEinteger, ≥ 1emptyextraEnvConnections kept open to Postgres. Empty = one per thread (APP__GATEWAY_THREADS), which is what the gateway needs. Pool plus overflow, summed over every gateway process, must stay under Postgres' max_connections. Also adjustable in Administration → Settings, applied after a restart of the gateway and orchestrator.
DB__MAX_OVERFLOWinteger, ≥ 010extraEnvExtra connections opened at a peak and closed when returned. Also adjustable in Administration → Settings, applied after a restart of the gateway and orchestrator.
DB__AUTO_MIGRATEbooleanfalseextraEnvtrue: the gateway migrates the schema itself at startup (the development Compose file sets it). false: migrations are a step of their own (python -m app.db.migra, the chart's init container) and the gateway only refuses a database behind its code.

Sign-in, sessions and API tokens

Local sign-in and the session cookie, the first administrator, and the API tokens of scripts and service accounts (API).

VariableTypeDefaultHelm valueDescription
JWT__SECRETstringdevelopment value (hidden)secrets.jwtSecretProduction: generate a secret of at least 32 characters (the gateway refuses the default and shorter ones in production). Signs every session and API token (HS256). Generate one with openssl rand -hex 32; changing it signs everybody out.
JWT__ALGORITHMstringHS256extraEnvSignature algorithm of the session tokens. Leave it.
JWT__ACCESS_TTL_MINUTESinteger720auth.jwtAccessTtlMinutesThe longest a session lasts, active or not. Sessions also end at sign-out, from «Your sessions», by an administrator, at a password change or when the account is disabled. Also adjustable in Administration → Settings.
AUTH__ADMIN_EMAILstringadmin@tabularia.localauth.adminEmailE-mail of the administrator created at the first start. Existing users are never touched.
AUTH__ADMIN_PASSWORDstringemptysecrets.adminPasswordProduction: set a password of your own before the first start (at least 12 characters; the gateway refuses admin and other published passwords). Password of that administrator, used when it is created. No default: the gateway refuses to create the administrator with a published password (admin, the guides' examples) or one under 12 characters, in every environment. Empty with AUTH__FIRST_RUN_SETUP (the Compose stack): no administrator at the start, the sign-in page creates it. Empty otherwise: the gateway refuses to start. An administrator still holding admin from an older release gets this password at the next start, and its sessions close.
AUTH__ADMIN_NAMEstringAdministratorauth.adminNameDisplay name of that administrator.
AUTH__ALLOW_KNOWN_ADMIN_PASSWORDbooleanfalseextraEnvDevelopment only: true lets a local stack whose scripts sign in with a fixed password (admin) create or keep it. Ignored with APP__ENV_NAME=production.
AUTH__FIRST_RUN_SETUPbooleanfalseextraEnvProduction: leave it off: the administrator comes from AUTH__ADMIN_PASSWORD. While no account exists, the sign-in page asks for the first administrator (email and password, the same password rule), signs it in, and from then on is the ordinary sign-in. On with docker-compose.yml, which answers on 127.0.0.1 only; AUTH__ADMIN_PASSWORD, when set, creates the administrator instead. Ignored with APP__ENV_NAME=production.
AUTH__SESSION_IDLE_MINUTESinteger, ≥ 00auth.sessionIdleMinutesMinutes without activity after which a session closes: the gateway refuses it and the open page signs out by itself after a one-minute warning. 0 = never. Also adjustable in Administration → Settings.
AUTH__COOKIE_DOMAINstringemptyauth.cookieDomainDomain of the session cookie. Empty = the gateway's host, right when interface and API share a host. With the interface on another host of the same domain (app.example.com and api.example.com) set the common domain, example.com; two unrelated domains do not work.
AUTH__PERSONAL_TOKENSbooleantrueauth.personalTokensWhether people may create API tokens for their own scripts, with their own permissions. false: only service accounts, created by an administrator, have tokens. Also adjustable in Administration → Settings.
AUTH__API_TOKEN_MAX_DAYSinteger, 1–3650365auth.apiTokenMaxDaysThe furthest expiry of an API token: every token has one. Also adjustable in Administration → Settings.

Single sign-on (OIDC)

Optional: Keycloak, Microsoft Entra ID, Auth0, Okta… Off while OIDC__ISSUER is empty; when it is set, the gateway configures itself from the issuer's discovery document and refuses to start if the client secret, redirect URI or post-login URL is missing. Runnable example: docs/examples/keycloak/; how groups are mapped: SSO group mapping.

VariableTypeDefaultHelm valueDescription
OIDC__ISSUERstringemptyoidc.issuerIssuer URL, e.g. https://keycloak.example.com/realms/tabularia or, for Entra ID, https://login.microsoftonline.com/<tenant-id>/v2.0. Empty = SSO off.
OIDC__CLIENT_IDstringemptyoidc.clientIdClient id registered on the identity provider.
OIDC__CLIENT_SECRETstringemptysecrets.oidcClientSecretSecret of that (confidential) client. Required when SSO is on.
OIDC__REDIRECT_URIstringemptyoidc.redirectUriThe gateway's callback, e.g. https://tabularia.example.com/api/auth/sso/callback; it must match the one registered on the identity provider exactly. Required when SSO is on.
OIDC__SCOPESstringopenid profile emailoidc.scopesScopes asked for, separated by spaces; must include openid.
OIDC__GROUPS_CLAIMstringgroupsoidc.groupsClaimWhere the groups are: a claim (groups for Keycloak's group mapper, roles for Entra ID app roles) or a path into the token, as it appears in the JSON: realm_access.roles and resource_access.<client id>.roles for Keycloak's realm and client roles (a client id with dots works). Several, comma-separated, are joined: groups,realm_access.roles. A path the id_token does not carry is looked up in the access token (Keycloak puts the roles there by default), then in the provider's userinfo. Also adjustable in Administration → Settings.
OIDC__GROUP_ALLOWLISTstringemptyoidc.groupAllowlistIdentity-provider groups mapped onto Tabularia groups, separated by commas. Empty = all of them. Also adjustable in Administration → Settings.
OIDC__AUTO_CREATE_GROUPSbooleanfalseoidc.autoCreateGroupstrue: create the Tabularia groups missing at sign-in. false: map only onto groups an administrator already made. Also adjustable in Administration → Settings.
OIDC__AUTHORITATIVEbooleantrueoidc.authoritativetrue: the identity provider owns group membership (groups absent from the claim are removed). false: SSO only adds. A token without any of the group claims counts as no groups (identity providers leave the claim out for a person in no group): check the claim name, the audit of each sign-in names a missing one. Also adjustable in Administration → Settings.
OIDC__SUPERUSER_GROUPstringemptyoidc.superuserGroupMembers of this group are administrators. Empty = nobody becomes one through SSO.
OIDC__ALLOWED_EMAIL_DOMAINSstringemptyoidc.allowedEmailDomainsOnly people with an e-mail in these domains may sign in, separated by commas. Empty = anyone the identity provider authenticates, which may be a whole company directory. Also adjustable in Administration → Settings.
OIDC__REQUIRE_ALLOWLISTED_GROUPbooleanfalseoidc.requireAllowlistedGrouptrue: signing in requires one of the groups of OIDC__GROUP_ALLOWLIST (or the administrators' group). Also adjustable in Administration → Settings.
OIDC__POST_LOGIN_URLstringhttp://localhost:3000/auth/callbackoidc.postLoginUrlThe interface page that receives the session after SSO, e.g. https://tabularia.example.com/auth/callback.
OIDC__BUTTON_LABELstringSingle sign-onoidc.buttonLabelLabel of the SSO button on the sign-in page. Also adjustable in Administration → Settings.
OIDC__DISCOVERY_TTL_SECONDSinteger3600extraEnvHow long the identity provider's discovery document is trusted before it is fetched again. Also adjustable in Administration → Settings.
OIDC__TIMEOUT_SECONDSnumber10extraEnvTimeout of the calls to the identity provider. Also adjustable in Administration → Settings.
OIDC__CA_FILEstringemptyoidc.caCert / oidc.caConfigMapA PEM file of the certificate authorities to trust for the identity provider, besides the public ones: a company Keycloak whose certificate the company's own authority signed (without it every sign-in fails with idp_untrusted). The chart mounts it from oidc.caCert or oidc.caConfigMap. The gateway refuses to start when the file is not one.
OIDC__VERIFY_TLSbooleantrueoidc.verifyTlsWhether the identity provider's certificate is checked. false accepts it even when no trusted authority signed it (a company Keycloak whose authority you cannot give in OIDC__CA_FILE): whoever can stand between the gateway and the provider can then sign in as anyone, administrators included. The gateway logs a warning at every start. See Single sign-on with Keycloak.
OIDC__LOGIN_TX_TTL_SECONDSinteger600extraEnvHow long a sign-in may stay on the identity provider's page (the state, nonce and PKCE cookie). Also adjustable in Administration → Settings.
VariableTypeDefaultHelm valueDescription
ENGINE__BASE_URLstringhttp://localhost:8000set by the chart (the engine Service)Internal URL of the engine API (http://backend:8000 in Compose). The gateway is its only client.
ENGINE__BUCKETstringdata-prepexternalServices.storage.bucketThe gateway's copy of the bucket name, compared with the bucket of every request (a mismatch is refused with 403). It must equal STORAGE__BUCKET: the Compose file and the chart derive it from that value, so set it by hand only if you know why.
ENGINE__RUN_STALE_TIMEOUT_SECONDSintegerCELERY__TASK_TIME_LIMIT + 300engine.runStaleTimeoutSecondsA run that has not reached a final state after this long is declared lost and marked failed. It must stay above CELERY__TASK_TIME_LIMIT, so the gateway gives up only after the worker has killed the task. Also adjustable in Administration → Settings.
ENGINE__SETTINGS_PANELbooleantrueengine.settingsPanelWhether Administration → Settings may change some engine parameters at runtime (engine settings). false: the environment is the only source, the panel shows the values without changing them, and values saved before stop applying.
ENGINE__DBT_EXPORT_TIMEOUT_SECONDSnumber, > 0 and ≤ 3600100extraEnvSeconds the gateway waits for the engine to build one dbt export; past it the download stops with a clear 504 instead of hanging until the ingress cuts it. Keep it, plus AI__DBT_EXPORT_TIMEOUT_SECONDS, under the ingress' read timeout. Also adjustable in Administration → Settings.

AI assistant

Optional: a chat over the catalog and an assistant in the flow editor. Off until an administrator adds a provider in Administration → AI models (Anthropic, or any OpenAI-compatible endpoint), or AI__BASE_URL and AI__SECRET_KEY are set, and no model is usable until an administrator enables it. On the external ClickHouse the assistant can run as a read-only user: see the AI user. The dbt export uses it too (dbt export).

VariableTypeDefaultHelm valueDescription
AI__BASE_URLstringemptyai.baseUrlOpenAI-compatible endpoint, e.g. https://api.scaleway.ai/v1: a provider that shows in the panel as read-only. Empty = no such provider.
AI__ACCESS_KEYstringemptyai.accessKeyIdentifier of the key (Scaleway calls it access key); the protocol authenticates with the secret alone, this only tells which key is in use.
AI__SECRET_KEYstringemptysecrets.aiSecretKeyIts API key. Empty = no such provider.
AI__DEFAULT_MODELstringemptyai.defaultModelModel proposed first in the picker, if an administrator enabled it, on any provider: its name, e.g. claude-opus-5-5. Also adjustable in Administration → Settings.
AI__METADATA_ONLYbooleanfalseextraEnvOn, no value of a row of data reaches the model configured here: the chat, the flow and script assistants and the dbt export get names, types, descriptions, row counts and structure, with no query, preview, sample or error text, and the AI comments on dashboards and reports are off. Values people typed in filters, SQL and descriptions still go. See AI in Tabularia. Also adjustable in Administration → Settings.
AI__MAX_RESULT_ROWSinteger, 1–2000200ai.maxResultRowsRows a query of the assistant may return, to the model and to the chat. Also adjustable in Administration → Settings.
AI__MAX_REQUESTSinteger, 1–5012ai.maxRequestsModel round-trips per message of the data chat: the cap on its tool-calling loop. Also adjustable in Administration → Settings.
AI__MAX_FLOW_REQUESTSinteger, 1–8030ai.maxFlowRequestsThe same for the assistants that build — the flow editor's, and the Assistant page's for whoever may make changes — which take a round-trip for every step they build and check. Also adjustable in Administration → Settings.
AI__MAX_HISTORY_MESSAGESinteger, 0–40060extraEnvPast messages of a conversation replayed to the model: higher remembers more and costs more per turn. Also adjustable in Administration → Settings.
AI__MAX_COST_PER_TURN_USDnumber, ≥ 00.5ai.maxCostPerTurnUsdSpending ceiling of one turn, in dollars. The round-trip cap does not limit tokens. A model with no known price counts at $4 in and $20 out per million tokens. Also adjustable in Administration → Settings.
AI__MAX_COST_PER_DAY_USDnumber, ≥ 00ai.maxCostPerDayUsdProduction: set it, and AI__MAX_COST_PER_DAY_TOTAL_USD, wherever people you do not know can sign in (every question is paid with your key). Daily ceiling per user, in dollars. 0 = none. Also adjustable in Administration → Settings.
AI__MAX_COST_PER_DAY_TOTAL_USDnumber, ≥ 00ai.maxCostPerDayTotalUsdDaily ceiling of the whole installation, in dollars: where many people share one account (a public demo) the per-user ceiling protects nothing. Once reached, the assistant is unavailable until the next day. 0 = none. Also adjustable in Administration → Settings.
AI__MAX_INPUT_TOKENS_PER_TURNinteger, ≥ 1000400000extraEnvInput tokens one turn may send to the model, without those read from the provider's cache: the spending ceilings stand for those. Also adjustable in Administration → Settings.
AI__DBT_EXPORT_TIMEOUT_SECONDSnumber, > 0 and ≤ 3600180extraEnvSeconds for all the AI calls of one dbt export (descriptions and translations). Keep it, plus ENGINE__DBT_EXPORT_TIMEOUT_SECONDS, under the ingress' read timeout (300 s in the chart). Also adjustable in Administration → Settings.
AI__MAX_CHATS_LISTEDinteger, 1–500100extraEnvConversations listed per person; older ones are not deleted, only left out of the list. Also adjustable in Administration → Settings.
AI__COMMENT_KEEP_SECONDSinteger, ≥ 6086400ai.commentKeepSecondsHow long an AI comment on a dashboard's or a report's numbers stays for whoever reads the same numbers: within it, those numbers cost no new call. Also adjustable in Administration → Settings.
AI__COMMENT_MIN_SECONDSinteger, ≥ 603600ai.commentMinSecondsLeast time between two calls for one element when a dashboard refreshes by itself with new numbers: until then the old text stays, saying the time of its numbers. A wall screen must not make a call at every refresh. Opening a page, a refresh by hand and a document are not held by it. Also adjustable in Administration → Settings.
AI__COMMENT_WAIT_SECONDSinteger, 1–24030ai.commentWaitSecondsLongest a report or a document waits for the AI comment. Past it, it goes out with a line saying the comment is not available, and the text is kept for the next one. Keep it under the ingress' read timeout (300 s in the chart). Also adjustable in Administration → Settings.
AI__COMMENT_MODELstringemptyai.commentModelThe model of the AI comments, as AI__DEFAULT_MODEL names one. Empty: the default model. One that cannot be used means no comment: another model is never used in its place, since the numbers go where the administrator said. Also adjustable in Administration → Settings.

The agents' instructions

Administration → AI → Agent instructions shows the system prompt of every AI agent: the data assistant, the flow and script assistants, the conversation titles, and the dbt export's descriptions and SQL translations. An administrator can rewrite an agent's text or restore the default; observers read it. A change applies from the next request, on every gateway replica, and goes to the audit log (ai.prompt_update, ai.prompt_reset).

What the administrators write is only the agent's text. At every request Tabularia appends a context that cannot be edited: today's date, the engine's SQL dialect and the row cap, the flow or the script being edited, the dialects of a translation, and, last, the language of the user's interface (the answer is written in it whatever language the question or the data are in). The panel shows that context with example values. Texts the AI already wrote for a version of a flow in a dbt export stay: a new text applies to later versions.

Agents over MCP

The MCP server at /mcp (on the gateway, under APP__ROOT_PATH when set): agents outside Tabularia — Claude Desktop, an IDE, another company agent — use the same tools as the assistant, signed in with an API token. A read-only token only reads; a write token builds flows in drafts and proposes plans, and a plan changes nothing until it is confirmed, by the agent's human (confirm_plan) or by the person on the plan's page in Tabularia. Every applied action is in the audit log with via: mcp and the token's name. It needs no AI model: the model is the client's. Stateless (JSON answers, no sessions), so any gateway replica serves any call.

VariableTypeDefaultHelm valueDescription
MCP__ENABLEDbooleantrueextraEnvTurns the MCP server on. Off, /mcp answers 404 and the API tokens keep working on the rest of the API. Also adjustable in Administration → Settings.
MCP__CONFIRM_IN_TABULARIA_ONLYbooleanfalseextraEnvOn, an agent cannot apply its own plans (confirm_plan refuses with the plan's link): a person signed in to Tabularia confirms each plan on its page. Also adjustable in Administration → Settings.
MCP__METADATA_ONLYbooleanfalseextraEnvOn, the MCP tools that read values are neither offered nor callable, and the others leave out rows and error text. It covers /mcp only: an API token can still read data on the rest of the API. See AI in Tabularia. Also adjustable in Administration → Settings.

Development sample

VariableTypeDefaultHelm valueDescription
PREVIEW__DEFAULT_SAMPLE_ROWSinteger, ≥ 00app.previewDefaultSampleRowsAbove 0, every source node without a sample of its own is read only for its first N rows in editor previews and trial runs. People switch it off per node; scheduled runs and «Run in production» always read everything. Useful when every read costs (remote storage, a pay-per-use warehouse). 0 = no automatic sample. Also adjustable in Administration → Settings.

Orchestration and scheduling

How flows are executed: queued in the database, picked up by an orchestrator process (APP__ROLE all or orchestrator), which waits for each step to complete before the next.

VariableTypeDefaultHelm valueDescription
ORCHESTRATOR__REFRESH_WAIT_SECONDSinteger600orchestrator.refreshWaitSecondsLongest a flow waits for a Refresh node to complete before aborting rather than reading stale data. Raise it for very long refreshes. An Output node has no cap of its own: it is bounded by ENGINE__RUN_STALE_TIMEOUT_SECONDS. Also adjustable in Administration → Settings.
ORCHESTRATOR__LOG_RETENTION_DAYSinteger, ≥ 030orchestrator.logRetentionDaysAfter how many days a run's logs are emptied: what a Script or Container node printed, and the engine's full traceback of a failure. The runs stay, with their outcome, short error, timings and exit code. 0 keeps them for ever. With the three settings below, the orchestrators trim this history every hour and then vacuum the tables they touched. Also adjustable in Administration → Settings.
ORCHESTRATOR__CONTRACT_RESULTS_KEPTinteger, ≥ 0100orchestrator.contractResultsKeptThe latest data contract reports kept per datasource; older ones are deleted every hour, never one whose notice is still to be sent. 0 keeps them all. Also adjustable in Administration → Settings.
ORCHESTRATOR__VERSIONS_KEPTinteger, ≥ 050orchestrator.versionsKeptThe latest versions kept per flow and per saved script; the one in use is always kept. 0 keeps them all. Also adjustable in Administration → Settings.
ORCHESTRATOR__AI_CHAT_RETENTION_DAYSinteger, ≥ 090orchestrator.aiChatRetentionDaysThe assistant's conversations untouched for this many days are deleted, with their turns; the spending ledger stays. 0 keeps them. Also adjustable in Administration → Settings.
ORCHESTRATOR__POLL_INTERVAL_SECONDSnumber3orchestrator.pollIntervalSecondsThe longest wait, in seconds, between two checks of a step the flow is waiting for: the first checks come sooner (a quarter of a second, then every second for half a minute). Also adjustable in Administration → Settings.
ORCHESTRATOR__QUEUE_POLL_SECONDSnumber, > 01extraEnvSeconds between two looks at the queue of flow executions. Also adjustable in Administration → Settings.
ORCHESTRATOR__MAX_CONCURRENTinteger, ≥ 120orchestrator.maxConcurrentFlow executions (and cells of flow groups) one orchestrator process carries at once; the others wait in the queue. Each uses a database connection while it asks the engine about a step: keep it below the pool (DB__POOL_SIZE). Also adjustable in Administration → Settings.
ORCHESTRATOR__QUEUE_TIMEOUT_SECONDSnumber, > 0900orchestrator.queueTimeoutSecondsAn execution still queued after this long (no orchestrator running, or all of them full) is not started any more: it is closed as failed. Also adjustable in Administration → Settings.
ORCHESTRATOR__HEARTBEAT_SECONDSnumber, > 010extraEnvHow often the process executing a flow says it is still there. Also adjustable in Administration → Settings.
ORCHESTRATOR__DEAD_AFTER_SECONDSnumber, > 060orchestrator.deadAfterSecondsAn execution silent for longer belongs to a process that died: another orchestrator closes it as interrupted. The machines' clocks must agree within this margin. Also adjustable in Administration → Settings.
SCHEDULING__WORKER_CAPACITYinteger2extraEnvFlow runs one worker is assumed to carry at once: used only to highlight the crowded minutes in the schedule load heatmap. Match it to the run workers' CELERY__WORKER_CONCURRENCY. Also adjustable in Administration → Settings.

Database backups

Kubernetes only (on Docker Compose Administration → Backups is greyed out). The automatic backup of the metadata database: an orchestrator runs pg_dump on the schedule and streams it to the engine, which stores it in the object storage and removes the oldest beyond the number to keep. BACKUP__ENABLED, BACKUP__SCHEDULE and BACKUP__KEEP are the installation's defaults, which Administration → Backups can change without a restart; where the files go is read by the engine. By default Tabularia's bucket under backups/postgres/: in production give the backups a bucket of their own. Restoring one: Upgrading.

VariableTypeDefaultHelm valueDescription
BACKUP__ENABLEDbooleantruebackup.enabledWhether the database is backed up on the schedule. Administration → Backups can turn it off or on; «Back up now» works either way.
BACKUP__SCHEDULEstring0 3 * * *backup.scheduleWhen: a five-field cron (minute hour day month weekday) in APP__TIMEZONE. The process refuses to start with an invalid one.
BACKUP__KEEPinteger, 1–100014backup.keepHow many backups stay; after each backup the oldest beyond this number are removed.
BACKUP__PG_DUMPstringpg_dumpextraEnvThe pg_dump used when the image has none of the server's own version. The gateway image has those of PostgreSQL 14 to 18 and uses the server's, so that the server's own pg_restore reads the backup; this one (18's) also reads older servers.
BACKUP__TIMEOUT_SECONDSinteger, ≥ 603600backup.timeoutSecondsThe longest a backup (the dump and its upload) may take before it is stopped and reported as failed. Also adjustable in Administration → Settings.

OpenLineage

Optional: what every run read and wrote, sent to a catalog in the OpenLineage standard (Marquez, DataHub, Astro…). Off when neither OPENLINEAGE__URL nor OPENLINEAGE__FILE is set. See OpenLineage.

VariableTypeDefaultHelm valueDescription
OPENLINEAGE__URLstringemptyopenlineage.urlHTTP endpoint of the collector, e.g. http://marquez:5000.
OPENLINEAGE__ENDPOINTstringapi/v1/lineageopenlineage.endpointPath of the lineage endpoint on the collector: api/v1/lineage for Marquez and Astro, openapi/openlineage/api/v1/lineage for DataHub.
OPENLINEAGE__API_KEYstringemptysecrets.openlineageApiKeyBearer token, if the collector asks for one.
OPENLINEAGE__FILEstringemptyopenlineage.fileAlso, or instead, write the events as JSON Lines to this file.
OPENLINEAGE__NAMESPACEstringtabulariaopenlineage.namespaceNamespace of this installation's jobs.
OPENLINEAGE__TIMEOUT_SECONDSnumber, > 010openlineage.timeoutSecondsHow long the collector may take to answer. Events are sent after the run is closed, from a background thread: a collector that is down never fails a run. Also adjustable in Administration → Settings.
OPENLINEAGE_URLstringemptyextraEnvThe OpenLineage library's own variable: switches lineage on like OPENLINEAGE__URL, configured the library's way.
OPENLINEAGE_CONFIGstringemptyextraEnvThe OpenLineage library's own configuration file (openlineage.yml), as an alternative to the variables above.

Webhooks and audit log

Webhook receivers are added in Administration → Webhooks (API); forwarding the audit log to a SIEM is set up in Administration → SIEM (Splunk). Neither has an environment variable beyond these.

VariableTypeDefaultHelm valueDescription
WEBHOOKS__RETENTION_DAYSinteger, ≥ 130webhooks.retentionDaysDays the history of webhook calls is kept. Also adjustable in Administration → Settings.
WEBHOOKS__TIMEOUT_SECONDSnumber, > 0 and ≤ 6010webhooks.timeoutSecondsHow long a webhook receiver may take to answer. Also adjustable in Administration → Settings.
AUDIT__RETENTION_MINUTESinteger, ≥ 00app.auditRetentionMinutesAudit entries older than this many minutes are deleted, once a minute. 0 = kept forever. Minutes because on a public demo the useful window is an hour (43200 = thirty days). The privacy notice can quote it.
AUDIT__IP_RETENTION_DAYSinteger, ≥ 090extraEnvClient addresses and browser strings are personal data, kept for this many days: past them they are emptied from audit entries, API tokens and sessions, once an hour, and the entry itself stays (who did what, and when). 0 = kept forever. Deleting an account empties its addresses at once, failed sign-ins with its email included. Independent of AUDIT__RETENTION_MINUTES: when that one is shorter, entries go before their addresses would.

Monitoring

VariableTypeDefaultHelm valueDescription
MONITORING__NODE_EXPORTER_URLstringhttp://node-exporter:9100/metricsmonitoring.nodeExporterUrlnode-exporter the RAM badge and the Monitoring panel read the host's memory from, live. Empty = badge off (the chart deploys no node-exporter).
MONITORING__TIMEOUT_SECONDSnumber3extraEnvTimeout of that read. Also adjustable in Administration → Settings.
MONITORING__CACHE_SECONDSnumber3extraEnvSeconds one read is shared by every open page. Also adjustable in Administration → Settings.

Worker autoscaling from the panel (Kubernetes)

Lets an administrator change the minimum and maximum number of worker pods from Administration → Workers. Off unless KUBERNETES__SCALING is true; the chart sets the whole block, and a service account allowed to read the two worker Deployments and change their autoscalers, with adminScaling.enabled (Deploying on Kubernetes).

VariableTypeDefaultHelm valueDescription
KUBERNETES__SCALINGbooleanfalseadminScaling.enabledTurns the panel's autoscaling bounds on.
KUBERNETES__CLUSTER_VIEWbooleanfalseclusterView.enabled (on by default)Turns Administration → Cluster on: the pods of Tabularia and of its users' code, their state, CPU and memory (with metrics-server) and the warning events, read-only, for administrators and observers.
KUBERNETES__API_URLstringhttps://kubernetes.default.svcextraEnvKubernetes API server, from inside the cluster.
KUBERNETES__NAMESPACEstringemptyset by the chart (the pod's namespace)Namespace of the release's pods and Deployments. Empty = the pod's own, read from its service account.
KUBERNETES__TOKEN_FILEstring/var/run/secrets/kubernetes.io/serviceaccount/tokenextraEnvService-account token the gateway authenticates with.
KUBERNETES__CA_FILEstring/var/run/secrets/kubernetes.io/serviceaccount/ca.crtextraEnvCertificate authority of the API server.
KUBERNETES__NAMESPACE_FILEstring/var/run/secrets/kubernetes.io/serviceaccount/namespaceextraEnvWhere the pod's namespace is read when KUBERNETES__NAMESPACE is empty.
KUBERNETES__RUN_WORKER_DEPLOYMENTstringemptyset by the chart with adminScaling.enabledDeployment of the run workers.
KUBERNETES__RUN_WORKER_AUTOSCALERstringemptyset by the chart with adminScaling.enabledTheir autoscaler, hpa/<name> or scaledobject/<name> (KEDA). Empty = none.
KUBERNETES__RUN_WORKER_MAX_REPLICASinteger, ≥ 110worker.autoscaling.panelMaxReplicas (or maxReplicas)The highest maximum of run-worker pods the panel may set.
KUBERNETES__PREVIEW_WORKER_DEPLOYMENTstringemptyset by the chart with adminScaling.enabledDeployment of the preview workers.
KUBERNETES__PREVIEW_WORKER_AUTOSCALERstringemptyset by the chart with adminScaling.enabledTheir autoscaler, as for the run workers.
KUBERNETES__PREVIEW_WORKER_MAX_REPLICASinteger, ≥ 18previewWorker.autoscaling.panelMaxReplicas (or maxReplicas)The highest maximum of preview-worker pods the panel may set.
KUBERNETES__TIMEOUT_SECONDSnumber, > 010extraEnvTimeout of the calls to the Kubernetes API. Also adjustable in Administration → Settings.
KUBERNETES__ROLLOUTbooleanfalsesettingsRollout.enabled (on by default)Administration → Settings restarts the pods that read a changed setting only when they start, one at a time, by eviction (docs/engine-settings.md). Off, it names the components to restart by hand.
KUBERNETES__RELEASEstringemptyset by the chartThe Helm release: Administration → Settings selects its pods by app.kubernetes.io/instance to restart them, and names it in the command it shows when it cannot.
KUBERNETES__APP_NAMEstringemptyset by the chartThe app.kubernetes.io/name of the release's pods, so that the rollout of Administration → Settings leaves alone the other charts of an umbrella release with components of the same names.
KUBERNETES_SERVICE_HOSTstringemptyextraEnvSet by Kubernetes in every container, never by hand: the gateway knows from it that it runs in a cluster, also when no service account token is mounted.
HOSTNAMEstringemptyextraEnvSet by the container runtime, never by hand: the pod's name, so that the orchestrator running a rollout of Administration → Settings evicts itself last.

Scripting environments (Kubernetes)

Administration → Scripting environments: the Python and R environments the Script node runs in, each built once into an image by a Kubernetes Job (rootless BuildKit) and checked by a second Job. Off unless SCRIPTS__ENABLED is true; the chart sets the whole block, the three namespaces and the service accounts of the gateway and the orchestrator allowed to run Jobs there (only the orchestrator's creates the builds), with scripts.enabled. The Kubernetes API is reached as in KUBERNETES__API_URL, KUBERNETES__TOKEN_FILE and KUBERNETES__CA_FILE (Scripting environments).

VariableTypeDefaultHelm valueDescription
SCRIPTS__ENABLEDbooleanfalsescripts.enabledTurns the scripting environments on: the admin section and the image builds.
SCRIPTS__NAMESPACEstringemptyscripts.namespaceNamespace where the scripts and the image checks run (Pod Security level restricted is enough).
SCRIPTS__TRIAL_NAMESPACEstringemptyscripts.trialNamespace (default <namespace>-scripts-dev), quota scripts.trialQuotaNamespace where the editor's «Run» of a Script node runs, apart from the flows' runs and with a ResourceQuota of its own, so trials never take resources from scheduled flows. A trial that does not fit in the quota fails at once, saying so. Empty = SCRIPTS__NAMESPACE.
SCRIPTS__BUILD_NAMESPACEstringemptyscripts.buildNamespaceNamespace where the image builds run. Rootless BuildKit needs its own user namespace (seccomp and AppArmor Unconfined), which only the privileged Pod Security level admits; its pods still run as a user, without privileges. Empty = SCRIPTS__NAMESPACE, which must then be privileged too: keep them apart.
SCRIPTS__REGISTRYstringemptyscripts.registry.repositoryWhere the environment images are pushed, as host[:port]/path: an environment's image is <registry>/tabularia-env-<id>:v<n>. The cluster's nodes pull from it.
SCRIPTS__REGISTRY_INSECUREbooleanfalsescripts.registry.insecureThe registry speaks plain HTTP or has a self-signed certificate. A test cluster only.
SCRIPTS__REGISTRY_PUSH_SECRETstringemptyscripts.registry.pushSecretName of a Secret of type kubernetes.io/dockerconfigjson in the build namespace, used by the builds to push. Empty = a registry that takes pushes without credentials.
SCRIPTS__REGISTRY_PULL_SECRETstringemptyscripts.registry.pullSecretName of a Secret of type kubernetes.io/dockerconfigjson in the scripts namespace, used by the Jobs to pull the images. Empty = the nodes pull without credentials.
SCRIPTS__REGISTRY_CA_CONFIGMAPstringemptyset by the chartA ConfigMap of the build namespace holding ca.crt: the certificate of the authority that signed the registry's, when it is not a public one (a company CA). The builds verify the registry with it. The chart creates it from scripts.registry.caCert.
SCRIPTS__BUILDER_IMAGEstringmoby/buildkit:v0.33.1-rootlessscripts.builderImageThe image that builds the environments: BuildKit, rootless. Replace it with a mirror on a cluster without internet access.
SCRIPTS__BASE_IMAGESstringemptyscripts.baseImagesJSON: the language versions offered, and the image each starts from, e.g. {"python": {"3.12": "python:3.12-slim"}, "r": {"4.5": "rocker/r-ver:4.5.3"}}. Empty = Python 3.11, 3.12, 3.13, 3.14 (python:3.x-slim) and R 4.5, 4.6 (rocker/r-ver). Pin them by digest, or point them at a mirror, here.
SCRIPTS__PYPI_INDEX_URLstringemptyscripts.pypiIndexUrlThe Python package index the builds install from. Empty = PyPI.
SCRIPTS__CRAN_URLstringhttps://cran.r-project.orgscripts.cranUrlThe CRAN repository the R builds install from, and the one the scripts' R sees: CRAN itself or one of its mirrors. CRAN publishes sources, so R packages compile during the build; the images carry the system libraries the usual packages need.
SCRIPTS__BUILD_TIMEOUT_SECONDSinteger, ≥ 603600extraEnvThe longest an image build may take before it fails. Also adjustable in Administration → Settings.
SCRIPTS__BUILD_CPUstring2extraEnvCPU an environment's image build may use. With SCRIPTS__BUILD_MEMORY it sets how many compilers run at once (one per core at most), which is what makes R packages built from CRAN's sources take minutes or tens of minutes. Also adjustable in Administration → Settings.
SCRIPTS__BUILD_MEMORYstring4GiextraEnvMemory an environment's image build requests and may use: it lands only on a node that has it. One compiler runs per 1.5 GiB (arrow's C++ takes over 1 GB each); a build that needs more fails alone, saying its compiler was killed. Raise it, with SCRIPTS__BUILD_CPU, to build R environments faster on large nodes. Also adjustable in Administration → Settings.
SCRIPTS__CHECK_TIMEOUT_SECONDSinteger, ≥ 30300extraEnvThe longest the check of a new image may take: pulling it on a node, starting it, listing its packages. Also adjustable in Administration → Settings.
SCRIPTS__POLL_SECONDSnumber, > 05extraEnvHow often the orchestrators look at the builds in progress, and the longest wait between two looks at a Script node's Job (the first come sooner). Also adjustable in Administration → Settings.
SCRIPTS__LOG_MAX_BYTESinteger, ≥ 4096262144extraEnvHow much of the end of a build's log is kept and shown in the panel. Also adjustable in Administration → Settings.
SCRIPTS__JOB_TTL_SECONDSinteger, ≥ 603600extraEnvKubernetes removes a finished Job after this long, if Tabularia has not removed it first. Also adjustable in Administration → Settings.
SCRIPTS__LINTbooleantrueextraEnvThe code editor underlines what Ruff finds in a Script node's Python code (undefined names, syntax errors, unused imports), and the imports the environment does not have, while it is typed. Each check runs Ruff in the gateway for a few milliseconds, at most twice a second per person. Also adjustable in Administration → Settings.

Container registries (Kubernetes)

Administration → Container registries: the registries the images of the Container node come from, their credentials, how to trust their certificate and the repositories allowed (Container registries). Off unless CONTAINERS__ENABLED is true; the chart sets it with containers.enabled.

VariableTypeDefaultHelm valueDescription
CONTAINERS__ENABLEDbooleanfalsecontainers.enabledTurns on Administration → Container registries: the registries the images of the Container node come from, on Kubernetes. The Helm chart sets it with containers.enabled.
CONTAINERS__NAMESPACEstringemptyset by the chartWhere the flows' runs run the Container nodes, and where each registry's pull Secret is kept. The chart sets it (containers.namespace, <release namespace>-containers by default), at the baseline Pod Security level.
CONTAINERS__TRIAL_NAMESPACEstringemptyset by the chartWhere the editor's «Run» of a Container node and «Try in the cluster» run, with a ResourceQuota of its own. Empty = CONTAINERS__NAMESPACE. The chart sets it (containers.trialNamespace).
CONTAINERS__PULL_CHECK_SECONDSinteger, ≥ 30300extraEnv«Try in the cluster»: how long a node may take to download an image before the check says it did not. The check answers within 270 s (the ingress cuts at 300 s): a longer download is said to be going on, and goes on in the cluster up to this. Also adjustable in Administration → Settings.
CONTAINERS__POLL_SECONDSnumber, > 03extraEnvThe longest wait between two looks at a Job of the containers' namespaces while the gateway waits for it (the first come sooner). Also adjustable in Administration → Settings.
CONTAINERS__JOB_TTL_SECONDSinteger, ≥ 603600extraEnvKubernetes removes a finished Job of the containers' namespaces after this long, if Tabularia has not removed it first. Also adjustable in Administration → Settings.
CONTAINERS__MAX_CPUstring4containers.limits.cpuThe most CPU a Container node may ask for (its request and its limit). The chart sets it (containers.limits.cpu). Also adjustable in Administration → Settings.
CONTAINERS__MAX_MEMORYstring16Gicontainers.limits.memoryThe most memory a Container node may ask for (its request and its limit). The chart sets it (containers.limits.memory). Also adjustable in Administration → Settings.
CONTAINERS__MAX_TIMEOUT_SECONDSinteger, ≥ 6086400containers.limits.timeoutSecondsThe longest timeout a Container node may have. The chart sets it (containers.limits.timeoutSeconds). Also adjustable in Administration → Settings.
CONTAINERS__LOG_MAX_BYTESinteger, ≥ 4096262144extraEnvHow much of the end of a container's log the run keeps and the run page shows. Also adjustable in Administration → Settings.

Notebooks (Kubernetes)

The marimo notebooks' pods: one per notebook being used, its app or its editor, in the scripts namespace with the image of the notebook's Python environment, started when someone opens it and stopped when nobody uses it. On with the scripting environments (SCRIPTS__ENABLED); the chart sets the gateway's address and these limits from scripts.notebooks.

VariableTypeDefaultHelm valueDescription
NOTEBOOKS__IDLE_MINUTESinteger, 1–144015set by the chartA notebook's pod nobody has used for this long is stopped (an open page keeps it alive); an editor's unsaved file becomes the notebook's draft first.
NOTEBOOKS__MAX_LIFETIME_HOURSinteger, 1–16812set by the chartThe longest a notebook's pod lives, used or not; the next visit starts a new one.
NOTEBOOKS__MAX_LIVEinteger, 1–100020set by the chartHow many notebook pods may run at once, apps and editors together; past it, opening another one says so.
NOTEBOOKS__MAX_VISITORS_PER_APPinteger, 1–100010scripts.notebooks.maxVisitorsPerAppHow many people may have a notebook's app open at once, unless the notebook sets its own number. marimo gives every visitor a kernel of their own, with their own copy of the data the code reads, all in the app's one pod: past the memory limit of the notebook's environment the pod stops for everyone. Past this number the app says it is full and opens by itself as soon as someone leaves. A visitor is an app open in a browser tab (marimo's WebSocket), counted across the gateway's replicas; the editor has no limit. Size it on the environment's memory limit divided by what one visitor's data takes. Never above NOTEBOOKS__MAX_VISITORS_CEILING. Also adjustable in Administration → Settings.
NOTEBOOKS__MAX_VISITORS_CEILINGinteger, 1–100050scripts.notebooks.maxVisitorsCeilingThe most people at once a notebook may set for its own app (whoever may edit it sets it on the notebook's page). Lowering it below what a notebook chose caps that notebook too. Also adjustable in Administration → Settings.
NOTEBOOKS__START_TIMEOUT_SECONDSinteger, 30–3600300set by the chartA notebook's pod that has not answered after this long has failed to start, and says why (no node free, an image not downloaded).
NOTEBOOKS__GATEWAY_URLstringemptyset by the chartThe gateway's address inside the cluster, which the notebooks' pods call to read datasources. The chart sets it to the gateway's Service; empty, a notebook cannot read datasources.
NOTEBOOKS__POD_IP_CHECKbooleantrueset by the chartA notebook's token is honoured only when the call comes from its own pod's address, and never with a forwarding header (X-Forwarded-For, Forwarded, X-Real-IP): a token the notebook's code printed is useless anywhere else. Turn it off only where the gateway cannot see the pods' addresses, behind a service mesh's sidecars (Istio, Linkerd).
NOTEBOOKS__ORIGINstringemptyscripts.notebooks.host (with origin)Production: with notebooks, set it to a host name of their own next to the interface's, routed to the gateway: without it their apps and editors do not open. The address the notebooks' apps and editors are served from, an origin of their own such as https://notebooks.example.com: marimo shows whatever HTML and JavaScript a notebook's code writes, and on the interface's address that code would act in Tabularia with the session of whoever opens the notebook. There the gateway answers only for the notebooks, and only with a cookie that Tabularia's page obtains for one notebook with a one-use ticket. A host other than the interface's, a sibling one best; https, or http only for localhost and its subdomains; never in APP__CORS_ORIGINS. The frontend needs the same value (NUXT_PUBLIC_NOTEBOOKS_ORIGIN). Empty: the apps and the editors do not open, unless NOTEBOOKS__ALLOW_SAME_ORIGIN. See An address of their own.
NOTEBOOKS__ALLOW_SAME_ORIGINbooleanfalsescripts.notebooks.allowSameOriginWithout NOTEBOOKS__ORIGIN, serve the notebooks' apps and editors on the interface's own address, as before: whoever may edit a notebook could then act in Tabularia as whoever opens it. Only to try notebooks alone on one machine, never where other people sign in; the pages show a warning while it is on.
TABULARIA_NOTEBOOK_TOKENstringemptyset in the notebooks' podsNot to set: the token of a notebook's session, which the gateway puts in the pod's Secret; it holds only on the notebooks' data endpoints, and only while the session lives.
TABULARIA_URLstringemptyset in the notebooks' podsNot to set: the gateway gives it to every notebook's pod, from NOTEBOOKS__GATEWAY_URL; the tabularia module in the pod reads datasources there.

Reports

Pixel-perfect reports: the gateway checks a report's definition at every save, reads its datasets with the rights of whoever opens it and fills its parameters in; the engine renders it, within the rows, pages, memory and time its own REPORTS__* allow.

VariableTypeDefaultHelm valueDescription
REPORTS__API_WAIT_SECONDSinteger, 5–3600120extraEnvGET /reports/{id}/render, the API: how long the request waits for the file before answering 504 and stopping the render. Keep the reverse proxy's read timeout above it (the chart's ingress allows 300 s).

Engine and workers

The data plane: in Compose the backend, worker, preview-worker and beat services; on Kubernetes the engine, the two worker Deployments and beat. Same image, same settings: a variable that concerns only one role says so.

Engine API and previews

The internal API the gateway calls (never exposed publicly). It hands previews to the preview queue and waits for the answer.

Read by both services, so listed under Shared by the gateway and the engine: APP__CORS_ORIGINS, APP__ENV_NAME.

VariableTypeDefaultHelm valueDescription
APP__ENGINE_API_THREADSinteger, ≥ 40200extraEnvSynchronous requests the engine API serves at once. Every open preview holds a thread while it waits for a worker, so this is how many previews can be in flight; they sleep and cost little. Also adjustable in Administration → Settings, applied after a restart of the engine API.
PREVIEW_TIMEOUT_SECONDSnumber120engine.previewTimeoutSecondsLongest the engine API waits for a preview worker; then it revokes the job and answers 504. Keep any reverse proxy's read timeout above it. Also adjustable in Administration → Settings, applied after a restart of the engine API.

Broker and cache index (Valkey or Redis)

VariableTypeDefaultHelm valueDescription
REDIS__HOSTstringlocalhostexternalServices.redis.hostHost of the Celery broker and of the step-cache index.
REDIS__PORTinteger6379externalServices.redis.portIts port.
REDIS__DBinteger0externalServices.redis.dbDatabase number.
REDIS__PASSWORDstringemptysecrets.redisPasswordPassword, when the broker requires one (managed services usually do).

Object storage

Any S3-compatible service: MinIO, AWS S3, Cloudflare R2, Backblaze B2, Scaleway, Google Cloud Storage through its S3 API (needed by the BigQuery engine). Every datasource snapshot, cached step and run output lives here.

Read by both services, so listed under Shared by the gateway and the engine: STORAGE__ENDPOINT.

VariableTypeDefaultHelm valueDescription
STORAGE__ACCESS_KEYstringdevelopment value (hidden)secrets.storageAccessKeyProduction: use credentials of your own (minioadmin is the development MinIO's). Access key of the storage.
STORAGE__SECRET_KEYstringdevelopment value (hidden)secrets.storageSecretKeyProduction: use credentials of your own. Secret key of the storage.
STORAGE__BUCKETstringdata-prepexternalServices.storage.bucketBucket of every snapshot, cached step and output. The gateway (ENGINE__BUCKET) and the frontend (NUXT_PUBLIC_BUCKET) need the same name; the Compose file and the chart derive them from this one.
STORAGE__REGIONstringus-east-1externalServices.storage.regionRegion of the storage (auto for Google Cloud Storage).
STORAGE__DIRECT_READbooleantrueextraEnvThe DuckDB and chDB engines read sources and cached steps in place, only the columns and row groups a query needs. false: download each file first, for a service that does not serve range reads.
DUCKDB_EXTENSION_DIRECTORYstringempty (the image sets /opt/duckdb-extensions)extraEnvWhere DuckDB finds its extensions (httpfs, for reading the storage in place). The image sets it to the extensions it ships, so nothing is downloaded at runtime.

Where the database backups go

The gateway makes the backups of its database (see Database backups above); the engine stores them, since it holds the storage credentials. Every field left empty falls back to the object storage's.

VariableTypeDefaultHelm valueDescription
BACKUP__BUCKETstringemptybackup.bucketThe bucket of the backups; empty = STORAGE__BUCKET. A bucket of their own is advised in production, so that losing one does not lose both.
BACKUP__PREFIXstringbackups/postgres/backup.prefixThe folder of the backups in the bucket. Only files named like a backup (tabularia-<time>.dump) are listed, downloaded or removed there.
BACKUP__ENDPOINTstringemptybackup.endpointAnother object storage for the backups; empty = STORAGE__ENDPOINT.
BACKUP__REGIONstringemptybackup.regionThe region of BACKUP__ENDPOINT; empty = STORAGE__REGION.
BACKUP__ACCESS_KEYstringemptysecrets.backupAccessKeyKeys of the backups' bucket when they are not the storage's; empty = STORAGE__ACCESS_KEY and STORAGE__SECRET_KEY. Read by the engine only: the chart keeps them out of the gateway.
BACKUP__SECRET_KEYstringemptysecrets.backupSecretKeyThe secret of BACKUP__ACCESS_KEY.

Workers and task limits (Celery)

How long a run may take is decided by three coupled values: CELERY__TASK_TIME_LIMIT (the worker kills the task), CELERY__TASK_SOFT_TIME_LIMIT (the task is told to stop shortly before) and the gateway's ENGINE__RUN_STALE_TIMEOUT_SECONDS (the gateway declares a silent run lost), which must stay above the hard limit. To allow longer runs, raise the hard limit and the stale timeout together.

Read by both services, so listed under Shared by the gateway and the engine: CELERY__TASK_TIME_LIMIT.

VariableTypeDefaultHelm valueDescription
CELERY__BROKER_URLstringemptyextraEnvBroker URL, to put the queue somewhere else entirely. Empty = built from REDIS__*.
CELERY__RESULT_BACKENDstringemptyextraEnvResult store, likewise. Empty = built from REDIS__*.
CELERY__WORKER_CONCURRENCYinteger2worker.concurrency, previewWorker.concurrencyProcesses per worker container (each runs one task at a time). The preview workers have their own value (PREVIEW_WORKER_CONCURRENCY in Compose).
CELERY__TASK_SOFT_TIME_LIMITinteger3300engine.taskSoftTimeLimitSecondsSeconds after which a task is told to stop, so it can close files and connections and report the failure before the hard limit. Keep it below CELERY__TASK_TIME_LIMIT. Also adjustable in Administration → Settings.
CELERY__TASK_SERIALIZERstringjsonextraEnvFormat of the task messages. JSON is a security property (no pickled objects): leave it.
CELERY__RESULT_SERIALIZERstringjsonextraEnvFormat of the results. Leave it.
CELERY__TIMEZONEstringUTCextraEnvClock of beat's periodic tasks. Leave it on UTC: the gateway converts schedules to UTC before queuing them.
CELERY__ENABLE_UTCbooleantrueextraEnvCelery's own UTC switch. Leave it.
CELERY__MAX_TASKS_PER_CHILDinteger50extraEnvA worker process is replaced after this many tasks, so memory the allocator keeps does not pile up. Also adjustable in Administration → Settings, applied after a restart of the workers.
CELERY__MAX_MEMORY_PER_CHILD_KBintegeremptyextraEnvResident memory (KB) past which a worker process is replaced at the end of its task. Empty = worked out from the container's memory limit × CELERY__MAX_MEMORY_HEADROOM_FRAC ÷ concurrency, never below the floor: set it only to force a fixed value. Also adjustable in Administration → Settings, applied after a restart of the workers.
CELERY__MAX_MEMORY_HEADROOM_FRACnumber0.75extraEnvShare of the container's memory limit given to the worker processes; the rest is margin for the main process and the page cache. Also adjustable in Administration → Settings, applied after a restart of the workers.
CELERY__MAX_MEMORY_PER_CHILD_FALLBACK_KBinteger1500000extraEnvThe cap used when the container has no readable memory limit. Also adjustable in Administration → Settings, applied after a restart of the workers.
CELERY__MAX_MEMORY_PER_CHILD_FLOOR_KBinteger512000extraEnvThe cap is never below this, so processes are not replaced too often. Also adjustable in Administration → Settings, applied after a restart of the workers.
WORKERS__RUN_CONCURRENCYinteger0extraEnvProcesses per run-worker container, resized within seconds without stopping running tasks. 0 = keep CELERY__WORKER_CONCURRENCY. Also adjustable in Administration → Workers.
WORKERS__PREVIEW_CONCURRENCYinteger0extraEnvThe same for the preview workers. Also adjustable in Administration → Workers.

Step cache and metrics

The editor keeps the output of intermediate steps, so the next preview starts from there instead of from the source.

VariableTypeDefaultHelm valueDescription
CACHE__TTL_SECONDSinteger86400cache.ttlSecondsA cached step unused for this long is deleted (its parquet and its index entry). Also adjustable in Administration → Settings.
CACHE__SWEEP_INTERVAL_SECONDSinteger3600cache.sweepIntervalSecondsHow often the cache is swept. Also adjustable in Administration → Settings, applied after a restart of the scheduler (beat).
CACHE__MAX_STEP_ROWSinteger1000000extraEnvSteps with more rows are not cached: a copy as large as the source saves nothing over re-reading it and costs gigabytes of writes. 0 = no cap. Also adjustable in Administration → Settings.
METRICS__STORAGE_STATS_INTERVAL_SECONDSinteger60extraEnvHow often the size of the storage prefixes is measured and published as a metric. Also adjustable in Administration → Settings, applied after a restart of the scheduler (beat).

Embedded engines (chDB, DuckDB, Polars)

The engines that run inside the worker process. See engine differences.

VariableTypeDefaultHelm valueDescription
TMP_SIZE_LIMITstringemptyengineApi.tmpSizeLimitSet by the Helm chart on the workers and the engine: the sizeLimit of their /tmp (an emptyDir), a Kubernetes quantity (50Gi). Past it the kubelet evicts the pod at once, so DuckDB spills at most half of it, shared by the worker's processes, and a query that needs more fails saying so. Unset (Compose): DuckDB's own cap, most of the free disk.
ENGINE_MAX_CROSS_JOIN_ROWSinteger50000000extraEnvRows a cross join may produce in a run before it is refused with a clear error instead of exhausting memory. 0 = no cap. Also adjustable in Administration → Settings, applied after a restart of the workers.
CHDB_MAX_MEMORY_USAGEinteger1500000000extraEnvMemory budget of the embedded chDB engine, in bytes; keep it under the worker's per-process cap. Also adjustable in Administration → Settings, applied after a restart of the workers.
CHDB_MAX_BYTES_BEFORE_EXTERNAL_GROUP_BYinteger536870912 (512 MiB)extraEnvPast this, a chDB GROUP BY spills to disk. Also adjustable in Administration → Settings, applied after a restart of the workers.
CHDB_MAX_BYTES_BEFORE_EXTERNAL_SORTinteger536870912 (512 MiB)extraEnvPast this, a chDB ORDER BY spills to disk. Also adjustable in Administration → Settings, applied after a restart of the workers.
CHDB_MAX_BYTES_IN_JOINinteger536870912 (512 MiB)extraEnvPast this, a chDB join fails instead of growing. Also adjustable in Administration → Settings, applied after a restart of the workers.

External ClickHouse engine

Optional: flows run on a remote ClickHouse server (managed or self-hosted) instead of in the worker. Off while CLICKHOUSE_EXTERNAL__HOST is empty. Use the HTTP(S) port (8123 / 8443), not the native one. The connection can also be set, or changed, by an administrator in Administration → Settings → ClickHouse connection, which overrides these variables (they stay the starting values) and has a connection test: see the guide.

VariableTypeDefaultHelm valueDescription
CLICKHOUSE_EXTERNAL__HOSTstringemptyexternalServices.clickhouse.hostServer host. Empty = engine off. Also adjustable in Administration → Settings, applied after a restart of the engine API and workers and scheduler (beat).
CLICKHOUSE_EXTERNAL__PORTinteger8123externalServices.clickhouse.portHTTP(S) port: 8123 plain, 8443 TLS. Also adjustable in Administration → Settings, applied after a restart of the engine API and workers and scheduler (beat).
CLICKHOUSE_EXTERNAL__USERNAMEstringdefaultexternalServices.clickhouse.usernameUser the engine works as. Also adjustable in Administration → Settings, applied after a restart of the engine API and workers and scheduler (beat).
CLICKHOUSE_EXTERNAL__PASSWORDstringemptysecrets.clickhousePasswordIts password. Also adjustable in Administration → Settings, applied after a restart of the engine API and workers and scheduler (beat).
CLICKHOUSE_EXTERNAL__AI_USERNAMEstringemptyexternalServices.clickhouse.aiUsernameA dedicated read-only user for the AI assistant's queries, a second barrier behind the SQL allow-list. Used only when both it and its password are set; otherwise the assistant runs as the user above. Grants: the AI user. Also adjustable in Administration → Settings, applied after a restart of the engine API and workers and scheduler (beat).
CLICKHOUSE_EXTERNAL__AI_PASSWORDstringemptysecrets.clickhouseAiPasswordIts password. Also adjustable in Administration → Settings, applied after a restart of the engine API and workers and scheduler (beat).
CLICKHOUSE_EXTERNAL__DATABASEstringdefaultexternalServices.clickhouse.databaseWorking database; it must exist. In push mode it holds the staging tables (the user needs CREATE, INSERT and DROP on it). Also adjustable in Administration → Settings, applied after a restart of the engine API and workers and scheduler (beat).
CLICKHOUSE_EXTERNAL__SECUREbooleanfalseexternalServices.clickhouse.secureTLS; managed services require it (port 8443). Also adjustable in Administration → Settings, applied after a restart of the engine API and workers and scheduler (beat).
CLICKHOUSE_EXTERNAL__CONNECT_TIMEOUTinteger10externalServices.clickhouse.connectTimeoutSeconds to wait for the handshake. Also adjustable in Administration → Settings.
CLICKHOUSE_EXTERNAL__TRANSPORTstring; s3 or pushs3externalServices.clickhouse.transportHow the server reaches the data. s3: ClickHouse reads and writes the parquet files directly on the storage, nothing passes through the worker (the storage must be reachable from the server). push: the worker uploads the source into a staging table and streams the result back; works with any server. Also adjustable in Administration → Settings, applied after a restart of the engine API and workers and scheduler (beat).
CLICKHOUSE_EXTERNAL__S3_ENDPOINTstringemptyexternalServices.clickhouse.s3EndpointThe storage endpoint as the server sees it (s3 mode), e.g. https://s3.fr-par.scw.cloud. Empty = STORAGE__ENDPOINT, right only when the server is on the same network. Also adjustable in Administration → Settings, applied after a restart of the engine API and workers and scheduler (beat).
CLICKHOUSE_EXTERNAL__S3_NAMED_COLLECTIONstringemptyexternalServices.clickhouse.s3NamedCollectionA named collection created on the server with the storage credentials, so the keys never travel in queries nor reach the query_log. Also adjustable in Administration → Settings, applied after a restart of the engine API and workers and scheduler (beat).
CLICKHOUSE_EXTERNAL__MAX_EXECUTION_TIMEinteger0externalServices.clickhouse.maxExecutionTimePer-query execution cap in seconds. 0 = none (previews are still bounded by PREVIEW_TIMEOUT_SECONDS). Also adjustable in Administration → Settings.
CLICKHOUSE_EXTERNAL__MATERIALIZE_MIN_ROWSinteger0externalServices.clickhouse.materializeMinRowss3 mode: a dataset the viewer reads with more rows than this is copied once into a MergeTree table on the server, 2–6× faster on full scans. 0 = off: the copy is built inside the request and, with large row groups, direct reads are already fast. Also adjustable in Administration → Settings.
CLICKHOUSE_EXTERNAL__MATERIALIZE_DATABASEstringemptyexternalServices.clickhouse.materializeDatabaseDatabase of those copies. Empty = the working database. Also adjustable in Administration → Settings, applied after a restart of the engine API and workers and scheduler (beat).
CLICKHOUSE_EXTERNAL__MATERIALIZE_TTL_SECONDSinteger1800externalServices.clickhouse.materializeTtlSecondsA copy unused for this long is dropped. Also adjustable in Administration → Settings.
CLICKHOUSE_EXTERNAL__PARQUET_SCAN_MAX_THREADSinteger8externalServices.clickhouse.parquetScanMaxThreadsOn scans (aggregations, pivots, sorts, joins) the engine asks for at least this many threads, to hide the latency of reading from S3; never on LIMIT queries, never below the server's cores. 0 = leave max_threads alone. Also adjustable in Administration → Settings.

BigQuery engine

Optional: flows run on Google BigQuery, which reads the parquet files from the bucket as temporary external tables, so STORAGE__ENDPOINT must be Google Cloud Storage (https://storage.googleapis.com, HMAC keys). The service account needs BigQuery Job User on the project and read access to the bucket. Off while the project or the credentials are empty.

VariableTypeDefaultHelm valueDescription
BIGQUERY__PROJECTstringemptyexternalServices.bigquery.projectGoogle Cloud project the jobs run in. Empty = engine off.
BIGQUERY__CREDENTIALS_FILEstringemptyextraEnvPath, inside the container, of the service account's JSON key. Either this or BIGQUERY__CREDENTIALS_B64.
BIGQUERY__CREDENTIALS_B64stringemptysecrets.bigqueryCredentialsB64The same key in base64 on one line (base64 -w0 key.json).
BIGQUERY__LOCATIONstringemptyexternalServices.bigquery.locationRegion of the jobs: the bucket's, or the multi-region containing it (e.g. EU). Empty = read from the bucket.
BIGQUERY__MAXIMUM_BYTES_BILLEDinteger21474836480 (20 GiB)externalServices.bigquery.maximumBytesBilledBytes billed one query may reach: BigQuery refuses a query that would go over before running it (20 GiB ≈ 0.12 $). 0 = no cap. Also adjustable in Administration → Settings.
BIGQUERY__CACHE_DATASETstringtabularia_cacheextraEnvDataset the step cache writes expiring native tables into (created if missing, which needs BigQuery Data Editor). Empty = no cache.

Imports and downloads

Database imports stream Arrow batches into parquet snapshots; a CSV or Excel download goes through the browser.

VariableTypeDefaultHelm valueDescription
INGEST__PARQUET_ROW_GROUP_ROWSinteger, 1–20,000,0001000000extraEnvRows per parquet row group written by imports. Large groups let the engines read only the columns a query needs; the group is held in memory before it is written (about 570 MB per million rows of a 50-column table), so raise the worker memory with it. Also adjustable in Administration → Settings.
INGEST__DB_READ_TIMEOUT_SECONDSinteger, ≥ 103000extraEnvHow long an import from ClickHouse, or a write into it, waits while the server sends nothing (it may sort a whole table before the first row). Keep it below CELERY__TASK_SOFT_TIME_LIMIT, so people read this error and not a killed task. Also adjustable in Administration → Settings.
EXPORT__MAX_ROWSinteger, ≥ 05000000extraEnvRows of a CSV download from the editor, the viewer or the chat: the browser holds the whole file in memory. Larger results go through an Output node. 0 = no cap. Also adjustable in Administration → Settings.
EXPORT__MAX_RUNNING_PER_PERSONinteger, ≥ 13extraEnvExports and documents (CSV, Excel, SQL scripts' results, reports, dashboards) one person or service account may have being prepared at once, on the queue of the flows' runs. From the interface past it people are asked to wait; code (the REST API with a token, a notebook's tabularia.report()) waits for its turn, up to REPORTS__API_WAIT_SECONDS. Also adjustable in Administration → Settings.

SharePoint datasources

Excel and CSV files on SharePoint are read through Microsoft Graph with an app registered on Entra ID; tenant, client and site belong to the connection. These two endpoints are deployment settings on purpose: whoever creates a connection cannot point the backend at another host.

VariableTypeDefaultHelm valueDescription
SHAREPOINT__GRAPH_BASEstringhttps://graph.microsoft.com/v1.0extraEnvMicrosoft Graph endpoint; change it only on a sovereign cloud (US Gov: https://graph.microsoft.us/v1.0).
SHAREPOINT__LOGIN_BASEstringhttps://login.microsoftonline.comextraEnvMicrosoft sign-in endpoint; likewise (US Gov: https://login.microsoftonline.us).

SFTP datasources

Caps of the «SFTP folder» datasource, checked on the listed sizes before anything is downloaded: going over one fails the import, naming the file. See SFTP.

VariableTypeDefaultHelm valueDescription
SFTP__MAX_FILE_BYTESinteger, ≥ 1536870912 (512 MiB)extraEnvBytes of one file: each one is downloaded to the worker's disk and read in memory to be stacked. Also adjustable in Administration → Settings.
SFTP__MAX_FILESinteger, ≥ 1500extraEnvFiles one refresh may read. Also adjustable in Administration → Settings.
SFTP__MAX_TOTAL_BYTESinteger, ≥ 12147483648 (2 GiB)extraEnvBytes of all the matching files together. Also adjustable in Administration → Settings.
SFTP__TIMEOUT_SECONDSnumber, > 030extraEnvConnection and SSH handshake: a silent server fails within this time. Also adjustable in Administration → Settings.

S3 folder datasources

Caps of the «S3 folder» datasource, checked on the object listing before anything is downloaded. See S3 datasources.

VariableTypeDefaultHelm valueDescription
S3_SOURCE__MAX_FILE_BYTESinteger, ≥ 1536870912 (512 MiB)extraEnvBytes of one object: downloaded to the worker's disk and read in memory. Also adjustable in Administration → Settings.
S3_SOURCE__MAX_FILESinteger, ≥ 1500extraEnvFiles one refresh may read. Also adjustable in Administration → Settings.
S3_SOURCE__MAX_TOTAL_BYTESinteger, ≥ 12147483648 (2 GiB)extraEnvBytes of all the matching objects together. Also adjustable in Administration → Settings.
S3_SOURCE__MAX_LISTEDinteger, ≥ 1100000extraEnvKeys the globs of one refresh may scan, all their expansions together: ** on a huge bucket stops here. Also adjustable in Administration → Settings.
S3_SOURCE__MAX_REQUEST_SECONDSnumber, > 0600extraEnvWall-clock time of one object's download: a server that drips the bytes stops here. Also adjustable in Administration → Settings.

API datasources

Caps and manners of the «API call» datasource. A datasource may lower the per-call, per-refresh and row limits for itself, never raise them above these; going over one fails the refresh with a message that names it, the data is never cut silently. See API datasources.

VariableTypeDefaultHelm valueDescription
API__TIMEOUT_SECONDSnumber, > 030extraEnvConnection and read timeout of one request, the token request included. Also adjustable in Administration → Settings.
API__MAX_PAGESinteger, ≥ 11000extraEnvPages of one call: a pagination that never ends stops here. Also adjustable in Administration → Settings.
API__MAX_CALLSinteger, ≥ 1500extraEnvCalls of one refresh (the combinations of the list variables). Also adjustable in Administration → Settings.
API__MAX_ROWSinteger, ≥ 12000000extraEnvRows one refresh may receive. Also adjustable in Administration → Settings.
API__MAX_RESPONSE_BYTESinteger, ≥ 1104857600 (100 MiB)extraEnvBytes of one response, decompressed: it is held in memory to be parsed. Also adjustable in Administration → Settings.
API__MAX_TOTAL_BYTESinteger, ≥ 11073741824 (1 GiB)extraEnvBytes of all the responses of one refresh. Also adjustable in Administration → Settings.
API__MAX_RETRIESinteger, ≥ 04extraEnvRetries of a request that failed with 429, 500, 502, 503, 504 or a network error, with exponential backoff or the server's Retry-After. Also adjustable in Administration → Settings.
API__MAX_RETRY_WAIT_SECONDSnumber, ≥ 0120extraEnvA Retry-After longer than this fails the refresh instead of holding a worker. Also adjustable in Administration → Settings.
API__MAX_REDIRECTSinteger, ≥ 05extraEnvRedirects followed per request. Also adjustable in Administration → Settings.
API__MAX_REQUEST_SECONDSnumber, > 0300extraEnvWall-clock time of one request, from sending it to the last byte of the answer. Also adjustable in Administration → Settings.
API__MIN_INTERVAL_SECONDSnumber, ≥ 00extraEnvMinimum pause between two requests of one refresh; a datasource can ask for more. Also adjustable in Administration → Settings.

Placeholders

Dates and lists that move by themselves in the names of inputs and outputs. See placeholders.

VariableTypeDefaultHelm valueDescription
PLACEHOLDERS__MAX_VALUESinteger, ≥ 11000extraEnvValues of one range variable, and combinations of all the variables of a file datasource; checked when the datasource is saved and at every refresh. An API call is also bounded by API__MAX_CALLS. Also adjustable in Administration → Settings.

Email

The SMTP connections of email outputs and notices are set up in the app; the deployment decides only how their servers are trusted.

VariableTypeDefaultHelm valueDescription
SMTP__VERIFY_TLSbooleantrueextraEnvVerify the SMTP server's certificate and name, with STARTTLS and with implicit TLS (465). false is the explicit opt-out for a relay with a self-signed certificate: the connection is still encrypted, but to a server nobody vouched for, and it receives the SMTP password.

Reports

What one render of a pixel-perfect report may read and use. The run workers render reports (the celery queue, as downloads); the pages are laid out by a child process of the worker, with its own memory and time, and the charts are drawn by the chart service, ECharts to SVG, a sidecar of the worker pods on Kubernetes and the chart-renderer service on Compose.

VariableTypeDefaultHelm valueDescription
REPORTS__MAX_ROWSinteger, 1–10,000,0003000000reports.maxRowsThe most rows a report reads from each of its datasets; one more stops the render and names the dataset (aggregate it in SQL, or filter it). The rows stay in the worker's memory, in columns, while the report renders, and DuckDB works out its groups and totals: a million take some 500 MiB and a second or two, ten million some 2.2 GiB and ten seconds (measured), so give the worker that much for every render that may run at once.
REPORTS__MAX_PAGESinteger, 1–10,000200reports.maxPagesThe most pages of one report: a longer one stops with the number it would have.
REPORTS__RENDER_MEMORY_MBinteger, ≥ 1281024reports.renderMemoryMbThe memory of the child process that lays out a report's pages (Typst), in MiB: it needs about 4.5 MiB a page of tables (measured), so 1024 holds 200. Past it the render stops and says so; give the worker as much more memory for every render that may run at once.
REPORTS__RENDER_SECONDSinteger, ≥ 5120reports.renderSecondsHow long the layout of one report may take: past it the child process is killed and the render stops.
REPORTS__CHART_URLstringhttp://127.0.0.1:8090set on the run workers from reports.chartRendererWhere the workers find the chart service: the sidecar on 127.0.0.1 on Kubernetes (reports.chartRenderer), the chart-renderer service on Compose. Empty = no charts: a report that has one fails, saying so.

Dashboards

What one request for a dashboard's numbers may read. The preview workers compute them (the preview queue), with the reports' engine.

VariableTypeDefaultHelm valueDescription
DASHBOARDS__MAX_ROWSinteger, 1–10,000,000REPORTS__MAX_ROWSdashboards.maxRowsThe most rows a dashboard reads from each of its datasets; empty, REPORTS__MAX_ROWS. The tiles that read a dataset with more say so and how to aggregate it, and show no number: a total is never computed on part of the rows. The rows stay in the preview worker's memory, in columns, while a request is worked out: a request of 9 tiles on a million rows of 8 columns took it from 0.3 to 1.2–1.5 GiB (measured), so give that worker as much for every request that may run at once.
DASHBOARDS__CACHE_SECONDSinteger, 10–604,800300dashboards.cacheSecondsHow long the rows of a dataset and the numbers of a tile are shared by everyone who may read them, so that a hundred people on a dashboard run one query; a dashboard whose refresh_seconds is longer keeps them for that. Rights are checked at every request all the same. Also adjustable in Administration → Settings.
DASHBOARDS__CACHE_TILE_BYTESinteger, at least 1,0241048576dashboards.cacheTileBytesThe largest numbers of one tile kept in Valkey; above it they are worked out again at each request, from the rows kept, and counted in the dataprep_dashboard_cache metric. Also adjustable in Administration → Settings.
DASHBOARDS__CACHE_VALKEY_BYTESinteger, at least 1,048,576536870912dashboards.cacheValkeyBytesWith Valkey using more memory than this, no tile's numbers are kept (Valkey also carries the queues, and has no limit of its own). Also adjustable in Administration → Settings.
DASHBOARDS__CACHE_BUCKET_BYTESinteger21474836480dashboards.cacheBucketBytesThe most the rows of dashboards take on the bucket, under cache/: those about to expire leave first, and rows larger than this are not kept (then each new table page or click reads the source again). 0 keeps none. Also adjustable in Administration → Settings.

Frontend

The Nuxt server reads these at startup (NUXT_PUBLIC_… is Nuxt's own runtime configuration), so one image serves every environment without a rebuild. The browser calls the gateway directly; the frontend server never does. The development Compose file sets them in the frontend service itself, not from .env.

VariableDefaultHelm valueDescription
NUXT_PUBLIC_API_BASEhttp://localhost:8000frontend.publicApiBaseThe gateway's URL as the browser calls it: /api when one host serves interface and API, or a full URL on another host.
NUXT_PUBLIC_BUCKETdata-prepexternalServices.storage.bucketDefault bucket; must match STORAGE__BUCKET.
NUXT_PUBLIC_GRAFANA_URLhttp://localhost:3001frontend.publicGrafanaUrlGrafana as the browser reaches it, embedded in the Monitoring tab (administrators only). Optional.
NUXT_PUBLIC_SOURCE_URLhttps://github.com/JumpAsAService/Tabulariafrontend.publicSourceUrlWhere Source code in the settings menu leads. Empty: the project's repository. If you offer a changed version to others over a network, the AGPL asks you to offer them its source: point this at it.
NUXT_PUBLIC_NOTEBOOKS_ORIGINemptyfrom scripts.notebooks.hostThe notebooks' own address, the gateway's NOTEBOOKS__ORIGIN: the pages' Content-Security-Policy lets them show the notebooks' apps and editors from it.

Compose and Helm only

Not read by Tabularia's code: the Compose files and the Helm chart use them to configure containers.

VariableDefaultWhereDescription
MINIO_IMAGEpgsty/minio:RELEASE.2026-08-04T00-00-00ZCompose (infrastructure/.env)Image of the development MinIO (the official one was withdrawn from the registries).
WITH_MSSQLfalseCompose (build argument)Build argument: true adds Microsoft's SQL Server driver to the backend image you build. Not in the published images, for licensing reasons: see SQL Server.
BACKEND_MEM_LIMIT4gCompose (infrastructure/.env)Memory ceiling of the engine API container.
WORKER_MEM_LIMIT6gCompose (infrastructure/.env)Memory ceiling of the run worker. Keep the sum of the ceilings below the machine's RAM; the per-process cap is derived from it.
PREVIEW_WORKER_CONCURRENCY4Compose (infrastructure/.env)Processes of the preview worker (its CELERY__WORKER_CONCURRENCY). The per-process memory cap is the ceiling divided by this.
PREVIEW_WORKER_MEM_LIMIT4gCompose (infrastructure/.env)Memory ceiling of the preview worker.
GRAFANA_ADMIN_USERadminCompose (infrastructure/.env)Grafana administrator of the development stack.
GRAFANA_ADMIN_PASSWORDdevelopment value (hidden)Compose (infrastructure/.env)Production: change it wherever Grafana is reachable by others. Its password.
SAMPLES_SCALEsmallCompose (infrastructure/.env)Size of the sample data: small (seconds, fits a laptop), medium, robusta (42M order lines, minutes and a few GB). Generated once.
SAMPLES_ENGINEemptyCompose (infrastructure/.env)Engine of the sample flows. Empty = the first one the policy allows.
SAMPLES_RUN_FLOWStrueCompose (infrastructure/.env)Run the sample flows once at initialisation, so their outputs and views exist.
FORWARDED_ALLOW_IPS127.0.0.1Compose (infrastructure/.env), read by the gateway's serveruvicorn's own variable: the addresses (comma-separated, networks allowed) from which the gateway believes X-Forwarded-For. From anyone else the header is ignored and the client is the connection's address, which is what the sign-in throttle, the sessions and the audit log record. Behind your own reverse proxy put its address here; never * while the gateway's port is reachable by others, or anyone can choose the address they sign in from. On Kubernetes the chart sets it from gateway.trustedProxies.
MALLOC_ARENA_MAX2Compose and Helm (set on the engine containers)glibc's variable, set to 2 on the engine containers: one memory arena per thread inflates the resident memory of Polars and Arrow thread pools.

Changing this page

The page is generated. Names, types, defaults, bounds and Helm keys come from the code: the settings classes of gateway/app/core/config.py and backend/app/core/config.py, the direct reads of the environment in both apps, frontend/nuxt.config.ts, the development Compose file, infrastructure/.env.example and the chart's templates. The explanations come from .github/scripts/config_reference.toml. After changing either, run python3 .github/scripts/config_reference.py; --check (run by the Configuration reference workflow, and by python3 .github/scripts/test_config_reference.py) fails while the page and the code disagree.

Edit this page on GitHub