Skip to main content

Hub environment variables

Hub is a Next.js app and takes all of its configuration from environment variables.env locally, container env when deployed. The API is configured separately through appsettings.json; its keys are under Configuration, and the URL the API puts in emails is API Hub URL, not a Hub variable.

hub/.env.example is the annotated master list. Every key on this page links to itself, so you can point a teammate or an agent at one variable.

Minimum to boot

ENDATIX_BASE_URL=https://localhost:5001 # or ENDATIX_API_URL — see API origin
SESSION_SECRET=... # openssl rand -hex 32
AUTH_SECRET=... # npx auth secret

Everything else has a default or stays off. Without an API origin, any request that reaches the API throws:

Endatix API URL is not configured. Set ENDATIX_BASE_URL (optional ENDATIX_API_PREFIX) or a complete ENDATIX_API_URL.

Behind a reverse proxy or on a non-root domain, add AUTH_URL and AUTH_TRUST_HOST — see Reverse proxy.

Where values come from

  • .env, .env.local — local pnpm dev. Restart the dev server to pick up a change.
  • Container env — a published image, wherever it runs. Restart the process.
  • withEndatix(nextConfig) in next.config.ts — build-time settings only, so a change needs a rebuild. Always export the wrapped config, or image hosts and storage-derived images.remotePatterns are never compiled in.

Runtime vs build

Next.js inlines every NEXT_PUBLIC_* variable into the client bundle at build time, and no later process.env value can override it (Next.js docs).

Hub therefore keeps its browser-facing settings on request-time names (ENDATIX_*, POSTHOG_*). The server reads them per request and projects the public subset into the React tree. One image runs every environment: point it at another API, flip debug, or replace the SurveyJS licence by changing env and restarting.

Only two things are still baked at build — NEXT_PUBLIC_BASE_PATH (subfolder deployment) and the blob hostnames compiled into images.remotePatterns.

Never pass the API origin as --build-arg

Baking an API URL into the image ties that artifact to one environment and defeats promote-the-same-image releases. Set ENDATIX_BASE_URL at runtime.

Prefix new keys by owner, not by "this is Hub": ENDATIX_* for the Endatix API origin and Endatix product settings, and the vendor or subsystem stem otherwise — POSTHOG_*, STORAGE_*, AUTH_*, FLAG_*, SLACK_*, OTEL_*, MAINTENANCE_*, PDF_*. Whether a value is public depends on which module reads it, not on the letters in the name. Do not add new NEXT_PUBLIC_* keys.

API origin

Hub needs exactly one of two ways to reach the API — a complete URL, or an origin plus a path prefix. Neither is deprecated; pick by what your platform hands you:

  • One complete URL — use ENDATIX_API_URL. One variable carries the origin and any prefix, which is why the Helm chart sets it by default and why it suits containers and platform config screens.
  • An origin you compose — use ENDATIX_BASE_URL, plus ENDATIX_API_PREFIX when the API is not under /api. This is the pair the reverse proxy and subfolder guides use, and what .env.example ships for local development.
ENDATIX_API_URLone of these two

Complete API URL including its prefix, http or https only — https://api.example.com/api. Hub splits the origin and prefix back out of it, so any path prefix works here without a second variable.

ENDATIX_BASE_URLone of these two

API origin without the path prefix — https://localhost:5001 in development, the API's internal service URL in a cluster. Combined with the prefix to form every API call.

ENDATIX_API_PREFIXDefault /api

Path prefix appended to ENDATIX_BASE_URL, ignored in ENDATIX_API_URL mode. Normalised to a leading / and no trailing /; an empty value or / means no prefix.

Set one, not both

ENDATIX_BASE_URL takes precedence for routing, and at startup Hub writes the URL it composed into ENDATIX_API_URL only when that variable is empty. Set both to different hosts and the value you left in ENDATIX_API_URL survives — a public data-list lookup reads it directly and will call the other host while the rest of Hub talks to the first.

A value that does not parse resolves to "not configured" rather than being passed through — a typo surfaces as the error above instead of a failed fetch to a malformed host.

Request-time client config

These are the groups Platform Admin → Environment reports, in the same order — the fastest way to confirm what a running deployment actually resolved. Public keys and hosts appear there in full; the SurveyJS licence only as Set / Not set.

Debug

ENDATIX_IS_DEBUG_MODEDefault follows NODE_ENV

"true" or "false" to force debug output. Unset means on in development and off everywhere else, which is the right setting for most deployments — turn it on temporarily to diagnose a specific environment.

Experimental

Registers the SurveyJS extensions — data-list questions and custom widgets. The value must be exactly true; anything else reads as off. Set it wherever forms use data lists, or those questions render as plain inputs.

Analytics (PostHog)

PostHog's own variable names without the NEXT_PUBLIC_ prefix. Earlier Hub versions spelled these differently and there is no fallback for them — see Renamed and removed keys.

Project token (phc_…). Empty disables product analytics and PostHog feature flags. Required if you set FLAG_PROVIDER=posthog.

POSTHOG_HOSTDefault https://us.i.posthog.com

Capture host, and the origin server-side flag evaluation calls. Must be an absolute http(s) origin — a relative path falls back to the default. Left at the default, the browser posts through Hub's same-origin /ingest rewrite so ad blockers do not drop events; any other host (an EU project, a self-hosted instance) is called directly, because that rewrite has one destination compiled in.

UI host used to build deep links into PostHog. Cosmetic — set it when your project is not on the US cloud.

On Azure Static Web Apps, do not set POSTHOG_SECRET_KEY: it moves flag evaluation off the remote /flags endpoint.

Feature flags

Server-evaluated product gates, not the PostHog analytics isFeatureEnabled helpers. One provider wins — it is not a chain.

FLAG_PROVIDERDefault environment

posthog evaluates flags through PostHog; omitted or environment reads FLAG_* variables and falls back to each flag's code default. The choice is frozen at the first flag evaluation in the process, so switching it means restarting Hub — flag values themselves still resolve per request.

FLAG_<FLAG_KEY>environment provider only

One variable per flag: upper-snake the flag key, so ai-features becomes FLAG_AI_FEATURES. Booleans accept "true"; anything else reads as false. Flags ship disabled — ai-features, experimental-features, advanced-analytics, form-analytics, storage-stats, reporting-export, multi-tenancy.

posthog without a token falls back silently

FLAG_PROVIDER=posthog with an empty POSTHOG_PROJECT_TOKEN runs the environment provider instead, with no error in the logs. Confirm the resolved provider on Platform Admin → Environment after a deploy.

reCAPTCHA

Google reCAPTCHA v3 site key — the public half of the pair, already visible in page source, so it is shown in full on the Environment page. Empty disables reCAPTCHA on public forms.

SurveyJS

SurveyJS Creator licence. Read server-side and handed only to the authenticated designer — never part of the browser projection, because that object is serialised into anonymous form pages too. Empty runs Creator unlicensed, with its non-commercial banner.

The licence is perpetual: a running deployment keeps working, and you need a new key only when you upgrade the SurveyJS packages past the window the current one covers. Store it as a secret and replace it as a config change — never rebuild the image for it.

Submitter labels

Label for the submitter filter and column. Set it to the word your product uses — "Patient", "Applicant", "Student".

Comma-separated profile fields to add as columns in the submissions grid.

PDF export

How long a submission PDF may take to render before the export gives up. Browsers land on an error page; API clients get an RFC 7807 502 with "errorCode": "pdf_render_timeout".

The default sits just under the 45-second cap Azure Static Web Apps puts on a backend request — past that the platform returns its own Backend call failure, which cannot be styled. Raise it where there is no cap (standalone container, self-hosted), lower it where the cap is shorter. An unparseable, zero or negative value falls back to 40.

Stopping the wait does not stop the render — the renderer has no abort, so an overrunning export keeps using CPU until it finishes.

Sessions and sign-in

Encrypts the Hub session cookie. Generate with openssl rand -hex 32, keep one value per environment, and rotate it to invalidate every session at once.

Session lifetime, in minutes900 is 15 hours, not 900 days. Shorten it for shared workstations.

AUTH_SECRETRequired

Auth.js encryption key, separate from SESSION_SECRET. Generate with npx auth secret.

Public origin of this Hub, e.g. https://hub.example.com. Set it whenever Hub sits behind a proxy or an external identity provider is in play, or callbacks return to the internal hostname.

true behind a reverse proxy, so Auth.js trusts the forwarded host headers.

Keycloak and Google add AUTH_KEYCLOAK_* and AUTH_GOOGLE_* — see Authentication.

Configured elsewhere

AreaKeysPage
File uploadsSTORAGE_PROVIDER, STORAGE_AZURE_*, STORAGE_S3_*Asset storage · Azure Blob · RustFS
Maintenance pageMAINTENANCE_MODE, MAINTENANCE_* copyMaintenance mode
TelemetryOTEL_*, APPLICATIONINSIGHTS_CONNECTION_STRING, TELEMETRY_CONSOLE_FALLBACK, Helm otel.*, NEXT_OTEL_VERBOSEHub observability
Path prefixNEXT_PUBLIC_BASE_PATHSubfolder deployment
Sign-in providersAUTH_KEYCLOAK_*, AUTH_GOOGLE_*Authentication

Smaller keys live only in .env.example: NEXT_FORMS_COOKIE_NAME and NEXT_FORMS_COOKIE_DURATION_DAYS (the cookie that resumes a partly filled form), REMOTE_IMAGE_HOSTNAMES, RESIZE_IMAGES, RESIZE_IMAGES_WIDTH, ROBOTS_ALLOWED_DOMAINS, and the SLACK_* credentials.

Containers and CI

Set these in the pod spec, Compose file, or host app settings. Process env applies on restart, never a rebuild — same digest you tested is the digest you promote. Exception: NEXT_PUBLIC_BASE_PATH (and blob hostnames in images.remotePatterns) are baked at build; changing them needs a new image. See Subfolder deployment.

Hold secrets (SESSION_SECRET, AUTH_SECRET, ENDATIX_SURVEY_LICENSE_KEY) in your platform's secret store rather than in a values file or a CI variable that lands in logs. The Helm chart maps its values onto these variables and documents its own defaults.

After a deploy, open Platform Admin → Environment to confirm what the process resolved: the API URL it will call, the flag provider in force, and whether the licence is present.

Renamed and removed keys

Two rounds of renaming. The first kept the old names working; the second did not.

NEXT_PUBLIC_* public config — deprecated in 0.7.6

Next.js inlines every NEXT_PUBLIC_* value into the client bundle at build time, so each of these could only be changed by rebuilding and redeploying the image. Hub 0.7.6 moved the public client config to request-time ENDATIX_* names, resolved per request.

They still work. At startup Hub copies each deprecated value into its current name once: a non-blank current value always wins, a whitespace-only value counts as unset on either side, and the two are never merged at read time. Migrate when convenient — the old names cannot be changed without a rebuild, which is the problem the rename fixes.

An abbreviation no one could expand, holding the value with the strongest claim to runtime replacement: rotating a SurveyJS licence should never mean rebuilding an image.

Renamed so one image can serve environments with different reCAPTCHA keys, or none.

Renamed so you can turn debug output on in one environment without a rebuild, then turn it off again.

Renamed for the same reason, and to sit under the ENDATIX_* owner stem with the rest of the product settings.

Renamed alongside the label it belongs with.

NEXT_PUBLIC_BASE_PATH is deliberately not in this list. It is a genuine Next.js build-time setting and keeps its name.

PostHog names — no fallback

The PostHog keys were renamed without a fallback of any kind: set the current name, or analytics and feature flags stay off.

Build-time inlined and under the wrong stem. PostHog owns POSTHOG_*. No fallback — empty token disables analytics and PostHog flags.

Same rename. Capture host is now POSTHOG_HOST.

Same rename. UI deep-link host is now POSTHOG_UI_HOST.

Next steps