Environment variables
Environment variables say where the platform runs: the connections it opens, the keys it holds, the ports it binds, the directories it writes and the per-process settings the SDK reads. What a deployment offers (the design agent, the services, the skill registries, the OAuth apps) is not set here: it is the config, one document whose secrets are written as placeholders naming variables on this page.
The API and the dispatcher read the same variables. Each validates its environment when it starts,
and then reads the config, before it connects to anything: a missing or malformed variable is
the first line of output, naming the variable, and the process exits. Locally both load .env
from the repository root when one exists (tsx --env-file-if-exists=../../.env); the image
holds no .env, so a container gets its variables from --env-file, -e or its host.
A worker reads none of them. The API or dispatcher that
starts one builds its environment from five of them (NODE_ENV, APP_VERSION,
ARCHMAX_PROMPT_CACHE, ARCHMAX_PROMPT_CACHE_TTL, ARCHMAX_ALLOW_PRIVATE_URLS) and the
worker’s own kind and memory limit, and a worker refuses to start holding anything
else. A worker host reads only its own variables (see Worker host).
Boot rules
Section titled “Boot rules”The API and the dispatcher refuse to start unless these hold:
| Rule | Variables |
|---|---|
| Always required | MONGODB_URI, S3_BUCKET |
| Both or neither | S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY |
Required when NODE_ENV is production |
ARCHMAX_SIGN_IN (oidc or aws-alb, never dev-user), MASTER_KEY unless ARCHMAX_SECRET_STORE=aws-secrets-manager, REDIS_URL (the image’s all-in-one container sets it when it runs its own Redis) |
Required with ARCHMAX_SIGN_IN=oidc |
ARCHMAX_OIDC_ISSUER, ARCHMAX_OIDC_CLIENT_ID |
Required with ARCHMAX_SIGN_IN=aws-alb |
ARCHMAX_AWS_ALB_ARN (an Application Load Balancer’s ARN), ARCHMAX_AWS_ALB_SIGN_OUT_URL |
Required with ARCHMAX_SECRET_STORE=aws-secrets-manager |
ARCHMAX_AWS_SECRETS_MANAGER_REGION |
| Only the named mode’s variables | an ARCHMAX_OIDC_* or ARCHMAX_AUTH0_* variable without ARCHMAX_SIGN_IN=oidc, or an ARCHMAX_AWS_ALB_* one without ARCHMAX_SIGN_IN=aws-alb, refuses the start |
| Only the named secret store’s variables | MASTER_KEY with ARCHMAX_SECRET_STORE=aws-secrets-manager, or an ARCHMAX_AWS_SECRETS_MANAGER_* variable without it, refuses the start |
| Within its allowed values | every variable below with a fixed set of values, a URL rule or a positive-integer rule |
Production has one more rule, in the config rather than here: the config’s designAgent
section is required (see the config). The image sets
NODE_ENV=production, so every container is held to the production rules unless it overrides
the variable.
Runtime
Section titled “Runtime”| Variable | Required | Default | Meaning |
|---|---|---|---|
NODE_ENV |
no | development |
development, test or production. production turns on the boot rules above and the config’s designAgent rule, and makes the platform ignore ARCHMAX_ALLOW_PRIVATE_URLS. |
APP_VERSION |
no | dev |
The release this process runs. GET /api/health answers it as version: { "status": "ok", "version": "1.5.0" }. The image sets it from its build argument: X.Y.Z for a release, unstable-<sha> for an unstable image, pr-<number> for a pull request image. |
PORT |
no | 3001 |
The port the API listens on. In the image, PORT is nginx’s public port instead (see The image). |
API_HOST |
no | every interface | The address the API binds to. Leave it unset when another container reaches the API, as in single-process mode. The image’s all-in-one entrypoint sets 127.0.0.1 for the API it starts, so only the container’s own nginx reaches it, and with it nginx’s response headers and the client address nginx records. |
CORS_ALLOWED_ORIGINS |
no | http://localhost:5173 |
The browser origins the API allows, comma-separated; each entry is trimmed and empty entries are dropped. |
APP_BASE_URL |
no | the first CORS_ALLOWED_ORIGINS entry |
The platform’s public origin, where people and services outside reach it: shareable links such as organization invitations, and the callback address a callback trigger registers with its service. Must be a URL. The dispatcher needs it as well as the API, because it registers that address while a trigger activates. |
ARCHMAX_ALLOW_NEWER_DATA |
no | false |
true or false; any other value refuses the start. Lets the API and the dispatcher start against documents a newer release’s data update already rewrote, for rolling back to an older release, instead of refusing. Each boot then logs one [data-updates] warning per collection holding newer documents, leaves those documents as they are, and otherwise starts as usual; a boot with nothing newer left warns that the variable can be removed. It never lifts the refusal for documents below a collection’s baseline or without a schema version. Safe only when the newer release’s pull request says its data update can be read by the release before it; see rolling back past a data update. Honoured in production. |
ARCHMAX_USER_RATE_PER_MINUTE |
no | 60 |
Per-user ceiling, per minute, on expensive operations: starting turns, evaluations, design-agent turns, chat messages, trigger replays and connection tests. A request over it is answered 429 with Retry-After. A positive integer. |
Storage
Section titled “Storage”MongoDB holds the records, Redis backs the queues and locks, and S3 (or an S3-compatible store such as MinIO) is the only durable store for files: agent repositories as packed bundles, every environment’s draft plane and published versions, and every session’s files.
| Variable | Required | Default | Meaning |
|---|---|---|---|
MONGODB_URI |
yes | — | The MongoDB connection string. |
REDIS_URL |
production | redis://localhost:6379 outside production |
The Redis connection URL. rediss://<user>:<password>@<host>:6379 brings TLS and an ACL user, as AWS MemoryDB needs. Whether the server runs in cluster mode is asked from the server when the process starts, so no setting says so. In the image’s all-in-one container, leaving it unset makes the container run its own Redis, for that container alone (see the image). |
S3_BUCKET |
yes | — | The bucket every object is stored in. |
S3_ACCESS_KEY_ID |
with the secret | — | The S3 access key id. Used only to construct the S3 client. Set neither key to sign with the AWS SDK’s default credential chain: an ECS task role, an EKS pod identity, an instance profile. |
S3_SECRET_ACCESS_KEY |
with the key id | — | The S3 secret access key. |
S3_REGION |
no | us-east-1 |
The S3 region. |
S3_ENDPOINT |
no | — | A custom endpoint for an S3-compatible store, such as http://minio:9000. Must be a URL. Leave it unset for AWS S3. |
S3_PREFIX |
no | — | A key prefix every object is stored under, to share one bucket between deployments. |
S3_FORCE_PATH_STYLE |
no | false |
true or false. Path-style addressing, which MinIO and most S3-compatible stores need. |
For AWS S3, set the bucket and the region, and either the two keys or neither (the host’s role).
For MinIO, set the keys, S3_ENDPOINT and S3_FORCE_PATH_STYLE=true.
Local directories
Section titled “Local directories”Only git touches local disk. The scratch and the registry clones are disposable: deleting either costs a re-materialization or a re-clone, never data, so neither needs a volume.
| Variable | Required | Default | Meaning |
|---|---|---|---|
ARCHMAX_SCRATCH_DIR |
no | <os tmpdir>/archmax |
The disposable git scratch: each git operation materializes the agent’s repository from its bundle here, runs, and writes the bundle back. A relative path resolves against the repository root. |
ARCHMAX_TEMPLATE_REPO |
no | .archmax-template |
The directory whose files seed every new agent repository. A relative path resolves against the repository root. When the directory is missing or empty, a built-in file set seeds it instead. |
ARCHMAX_SKILL_REGISTRIES_DIR |
no | .archmax-skills |
Where the registries are cloned, one directory per registry id. A relative path resolves against the process’s working directory. A missing or foreign directory is cloned again. |
Sign-in
Section titled “Sign-in”A deployment signs people in one way, its sign-in mode, which
ARCHMAX_SIGN_IN names. Each mode reads only its own group below, and a variable of another
mode’s group refuses the start, so a leftover from an earlier setup is caught rather than
ignored. The SPA reads the mode from GET /api/sign-in-settings when it loads, so the image
carries no sign-in setting and no frontend variable exists for one.
| Variable | Required | Default | Meaning |
|---|---|---|---|
ARCHMAX_SIGN_IN |
in production | the dev user, outside production | The sign-in mode: oidc (OIDC sign-in), aws-alb (AWS ALB sign-in) or dev-user (one fixed local user for every request, refused in production). Any other value refuses the start. |
ARCHMAX_OIDC_ISSUER |
with oidc |
— | The issuer the SPA signs in at and the API verifies access tokens through, exactly as its discovery document names it (Auth0: https://<your-auth0-domain>/, with the trailing slash). Must be a URL. |
ARCHMAX_OIDC_CLIENT_ID |
with oidc |
— | The public client id of the SPA application, which the SPA signs in with. A token without an audience must name it in client_id or azp. Not a secret. |
ARCHMAX_OIDC_AUDIENCE |
no | — | The API identifier access tokens must name in aud, such as https://api.example.com; the SPA asks for tokens for it. Auth0 needs it, or it issues tokens only Auth0 can read; leave it unset for Cognito, whose access tokens carry no audience. |
ARCHMAX_OIDC_SIGN_OUT_URL |
no | — | Where signing out sends the browser when the provider’s logout isn’t standard OIDC, such as Cognito’s /logout?client_id=…&logout_uri=…. Unset, the discovery document’s end-session endpoint. Must be a URL. |
ARCHMAX_AUTH0_DB_CONNECTION |
no | — | Auth0 only: the name of the database connection password users sign in with, under Authentication → Database in the Auth0 dashboard (Username-Password-Authentication unless renamed). Set, those users are offered Auth0’s change-password email; unset, nobody is. |
ARCHMAX_AWS_ALB_ARN |
with aws-alb |
— | The Application Load Balancer whose signed x-amzn-oidc-data header the API trusts. Its region names the host the header’s keys are fetched from. See AWS. |
ARCHMAX_AWS_ALB_SIGN_OUT_URL |
with aws-alb |
— | The identity provider’s full logout URL, returning to https://<host>/signed-out. Must be a URL. |
ARCHMAX_AWS_ALB_SESSION_COOKIE |
no | AWSELBAuthSessionCookie |
The name the load balancer’s authentication action gives its session cookie, whose shards signing out expires. |
ARCHMAX_MANAGED_DOMAIN_CLAIM |
no | https://archmax.ai/managed_domain |
The claim in which the identity provider asserts the domain that manages an identity, for auto-onboarding. Cognito exposes a custom attribute as custom:<name>. |
ARCHMAX_EMAIL_VERIFICATION |
no | on | Whether a person’s email address must be one the identity provider verifies. Accepts 1/0, true/false, yes/no and on/off. Off, every address the provider supplies counts as verified, for access, invitations, taking over a user by email and auto-onboarding; safe only when the provider admits no address you don’t control (no self sign-up, federation limited to your own directories). The API logs a warning while it is off. |
Secret store
Section titled “Secret store”Where the credentials’ secret fields are kept, which ARCHMAX_SECRET_STORE names. Like the
sign-in modes, each store reads only its own variables, and a variable of the store not named
refuses the start. Switching stores moves the secrets with tools/move-secrets.ts (see
AWS).
| Variable | Required | Default | Meaning |
|---|---|---|---|
ARCHMAX_SECRET_STORE |
no | db |
db, the database store: the secret fields sealed with AES-256-GCM under MASTER_KEY on each credential’s record in MongoDB. aws-secrets-manager: one AWS Secrets Manager secret per credential, signed for with the AWS SDK’s default credential chain (the task role), and no master key. Any other value refuses the start. |
MASTER_KEY |
production, with db |
— | The AES-256 key the database store seals every credential’s secret fields with: 32 bytes, base64-encoded. Generate one with openssl rand -base64 32. A key that does not decode to 32 bytes stops the boot. Outside production the platform starts without it, and only storing or using a credential fails. |
ARCHMAX_AWS_SECRETS_MANAGER_REGION |
with aws-secrets-manager |
— | The region the secrets live in, as an AWS region code such as eu-west-1. |
ARCHMAX_AWS_SECRETS_MANAGER_PREFIX |
no | archmax/credentials/ |
What every secret’s name begins with: a credential’s secret is <prefix><credential id>. Ends in /, at most 200 of the characters a secret’s name allows. The platform reaches no secret outside it, so the task role is granted secret:<prefix>* alone. |
ARCHMAX_AWS_SECRETS_MANAGER_KMS_KEY_ID |
no | the account’s aws/secretsmanager key |
A customer-managed KMS key (id, ARN or alias) new secrets are encrypted with. |
ARCHMAX_AWS_SECRETS_MANAGER_ENDPOINT |
no | — | Another endpoint for the Secrets Manager API, such as a local stand-in in development. Must be a URL. |
Credentials
Section titled “Credentials”| Variable | Required | Default | Meaning |
|---|---|---|---|
OAUTH2_REDIRECT_URL |
no | — | The one callback URL of the OAuth2 authorization-code flow, such as https://archmax.example.com/api/oauth2/callback. It must match the redirect URI registered on every OAuth app exactly. It also locates the platform’s client ID metadata document, which the authorization server of a Remote MCP Server that offers no dynamic client registration may fetch to learn the client a sign-in runs on: the same origin’s /api/oauth2/client-metadata, served only when this URL is https with the path /api/oauth2/callback, and reachable by such an authorization server only on a deployment it can reach. Unset, the platform still starts and only the Connect flow fails. Must be a URL. |
ARCHMAX_DNS_VERIFICATION |
no | on | Whether an organization’s email domain needs a DNS TXT record before it counts as verified, for DNS verification and auto-onboarding. Accepts 1/0, true/false, yes/no and on/off, in any case; any other value refuses the start. On, the API reads TXT records over the host’s DNS when an Organization Owner verifies a domain and when someone at an onboarding domain first signs in, so it needs outbound DNS. Off, a domain is verified as soon as an Organization Owner adds it and nothing is read. Auto-onboarding is available either way and starts off on each domain. Turn it off only on a single-tenant deployment whose Organization Owners are all trusted: without the check, any organization’s Organization Owner can claim any unclaimed domain, a public email domain included. Read by the API only. |
Outbound URLs
Section titled “Outbound URLs”| Variable | Required | Default | Meaning |
|---|---|---|---|
ARCHMAX_ALLOW_PRIVATE_URLS |
no | false |
true or false. Lets owner-supplied outbound URLs (model provider base URLs, remote MCP servers, OIDC issuers) point at localhost or a private network and use plain http, for local development against a model or an MCP server on the same machine. With it set, the API and the dispatcher also leave every outbound request unguarded, which otherwise refuses a private address when it connects. Ignored when NODE_ENV is production. |
Dispatcher
Section titled “Dispatcher”| Variable | Required | Default | Meaning |
|---|---|---|---|
ARCHMAX_TRIGGER_POLL_CONCURRENCY |
no | 4 |
Trigger polls one dispatcher process runs at once. Every active polling trigger is polled once a minute by whichever dispatcher process claims it, so the polling capacity is this number times the dispatcher processes. Each poll runs in a call worker, so keep it below ARCHMAX_WORKER_CALL_SLOTS. A positive integer. |
The dispatcher also reads APP_BASE_URL (see Runtime) and the prompt-cache switches it
hands its workers (see The SDK).
Workers
Section titled “Workers”Every turn and every call through a connection executes in a worker that the API or the dispatcher starts and supervises (see operating). Both read these:
| Variable | Required | Default | Meaning |
|---|---|---|---|
ARCHMAX_WORKER_TURN_MEMORY_MB |
no | 1024 |
Memory a turn worker may hold, in MiB. A turn past it fails naming the limit. At least 128. |
ARCHMAX_WORKER_CALL_MEMORY_MB |
no | 512 |
Memory a call worker may hold, in MiB: a poll, a dropdown, a connection test, a trigger’s lifecycle, a remote mount listing, the catalog. At least 128. |
ARCHMAX_WORKER_TURN_SECONDS |
no | 3600 |
How long a turn worker may run before it is ended. |
ARCHMAX_WORKER_CALL_SECONDS |
no | 300 |
How long a call worker may run before it is ended. |
ARCHMAX_WORKER_TURN_SLOTS |
no | 3 |
Turn workers one process runs at once: workflow, evaluation and design-agent turns together. Work beyond it waits for a slot. |
ARCHMAX_WORKER_CALL_SLOTS |
no | 6 |
Call workers one process runs at once. |
ARCHMAX_WORKER_INSPECT |
no | off |
on or off. Starts every worker with an inspector on a free port, which the dispatcher logs, and without the Node.js permission model. Refused in production. |
ARCHMAX_WORKER_HOST |
no | — | A worker host, as host:port: workers start there instead of in this process. Requires ARCHMAX_WORKER_HOST_KEY. |
ARCHMAX_WORKER_HOST_KEY |
with ARCHMAX_WORKER_HOST |
— | The key the host and every API and dispatcher dispatching to it share, at least 32 bytes, base64-encoded (openssl rand -base64 32). It authenticates both sides of each connection and opens no data. Without ARCHMAX_WORKER_HOST, it refuses the start. |
Worker host
Section titled “Worker host”A worker host is the image’s worker-host command: a process that runs only
workers. It reads these, and the two prompt-cache switches, NODE_ENV and APP_VERSION,
which it builds its workers’ environment from:
| Variable | Required | Default | Meaning |
|---|---|---|---|
ARCHMAX_WORKER_HOST_KEY |
yes | — | The same key the API and the dispatcher hold. |
ARCHMAX_WORKER_HOST_PORT |
no | 7340 |
The port it listens on for dispatchers. |
ARCHMAX_WORKER_HOST_BIND |
no | every interface | The address it listens on. |
It refuses to start, naming each variable and never its value, when its environment holds a
data store’s address (MONGODB_URI, REDIS_URL, any S3_*), a key (MASTER_KEY, the secret
store’s settings, an ARCHMAX_SERVICE_*_TOKEN, a client secret, a private key, a password), the
config (ARCHMAX_CONFIG, ARCHMAX_CONFIG_FILE, or a config.yaml in its app directory), or a
cloud credential (AWS keys and session tokens, an ECS task role’s or a pod identity’s endpoint,
Google’s application credentials, Azure’s client secret or managed identity). Anything else an
orchestrator adds is left alone.
The config
Section titled “The config”| Variable | Required | Default | Meaning |
|---|---|---|---|
ARCHMAX_CONFIG |
no | — | The config as one line of JSON, or that document base64-encoded. It wins over a config file, with one warning naming the file it shadowed. pnpm config:env produces it from a config.yaml. |
ARCHMAX_CONFIG_FILE |
no | — | A config file other than config.yaml in the app directory. A named file that does not exist refuses the start. A relative path resolves against each process’s own working directory, so name an absolute one. |
With neither set, the config is config.yaml in the app directory when one is there (the
repository root locally, /app in the image), and otherwise the empty document. The whole rule
is on the config page.
Secrets the config references
Section titled “Secrets the config references”The config holds no secret. It writes each one as a ${{NAME}} placeholder, and the boot
resolves it from the variable of that name. The names are the deployment’s own choice; these
are the ones config.yaml.example uses:
| Variable | Required | Default | Meaning |
|---|---|---|---|
ARCHMAX_DESIGN_AGENT_API_KEY |
when the config names it | — | The design agent’s key on the platform’s own OpenAI-compatible endpoint (designAgent.apiKey). |
ARCHMAX_SERVICE_<ID>_TOKEN |
when the config names it | — | One per service, such as ARCHMAX_SERVICE_BROWSER_TOKEN: the token that one service checks (services[].token), at least 16 characters and the same value set on the service itself. Generate each with openssl rand -hex 32. |
ARCHMAX_OAUTH_APP_<ID>_CLIENT_ID |
when the config names it | — | One per OAuth app: the client id of this deployment’s own app (oauthApps[].clientId). |
ARCHMAX_OAUTH_APP_<ID>_CLIENT_SECRET |
when the config names it | — | One per OAuth app: its client secret (oauthApps[].clientSecret). |
ARCHMAX_OAUTH_APP_GITHUB_SLUG |
when the config names it | — | The GitHub App’s slug, which the address of its installation page carries (oauthApps[].reach.url). |
A placeholder whose variable is unset or empty leaves out the entry holding it, with one line on
standard output naming the entry, the field and the variable, and the process starts with the
rest of the config. The design agent is the exception in production: leaving out designAgent
refuses the start. No variable sets a config field on its own: a variable reaches the config
only through a placeholder that names it.
The SDK
Section titled “The SDK”The SDK reads its own ARCHMAX_* variables from the environment of the worker a turn runs in, which
receives the two prompt-cache switches from the process that started it and none of the others. The harness configuration reference
lists all of them; these are the ones a deployment sets.
| Variable | Required | Default | Meaning |
|---|---|---|---|
ARCHMAX_PROMPT_CACHE |
no | on | Provider prompt caching of the stable prefix (tool schemas and system prompt) on every model call. One setting governs both agents: the SDK reads it for the workflow agent and the platform for the design agent. Accepts 1/0, true/false, yes/no and on/off, in any case; any other value refuses the start. |
ARCHMAX_PROMPT_CACHE_TTL |
no | 5m |
The lifetime of a cached prefix: 5m or 1h. |
ARCHMAX_API_BASE_URL |
for a rubric that names a model | — | The OpenAI-compatible endpoint the SDK builds a model on. |
ARCHMAX_API_KEY |
for a rubric that names a model | — | The key for that endpoint. |
ARCHMAX_MODEL |
for a rubric that names a model | — | The SDK’s default model id. The SDK requires it with the other two; the rubric’s own model: is the id that is called. |
The platform hands the SDK the model of every turn, built from the agent’s
model provider, so the SDK builds a model from
ARCHMAX_API_BASE_URL, ARCHMAX_API_KEY and ARCHMAX_MODEL only for a rubric hook that names
its own model:. A worker receives none of the three, so such a rubric fails with
Missing required environment variable. With prompt caching on, also give the design agent its cache rates in the config
(designAgent.prices.cacheRead and cacheWrite).
The image
Section titled “The image”The image’s entrypoint reads these variables of its own. They mean nothing outside the image.
| Variable | Required | Default | Meaning |
|---|---|---|---|
PORT |
no | 8080 |
The public port nginx listens on. A host that injects PORT (Railway does) is honoured as it is. The API is started on API_PORT instead, whatever PORT holds. |
API_PORT |
no | 3001 |
The internal port the API binds to on 127.0.0.1, which nginx proxies /api/ to and the healthcheck probes. Not public. |
ACCESS_LOG |
no | errors |
How much of nginx’s HTTP access log is kept: errors (only the requests whose status is not 2xx or 3xx), all, or off. Any other value is read as errors, with a line on standard error. |
A container started with a command (single-process mode) skips the entrypoint’s own steps and
runs that command alone, so PORT is then the API’s own port again. See
the image.
Set by the image
Section titled “Set by the image”The image sets these, and a container can override any of them:
| Variable | Value in the image |
|---|---|
NODE_ENV |
production |
APP_VERSION |
the APP_VERSION build argument: X.Y.Z, unstable-<sha>, pr-<number>, or dev for a build without one |
API_PORT |
3001 |
ARCHMAX_SCRATCH_DIR |
/data/scratch |
ARCHMAX_SKILL_REGISTRIES_DIR |
/data/skill-registries |
ARCHMAX_TEMPLATE_REPO |
/app/.archmax-template |
When REDIS_URL is unset, the entrypoint also runs the container’s own Redis, with a password
it makes at each start, and points the API’s and the dispatcher’s REDIS_URL at it, password
included. No other process receives the password, and the Redis refuses a client without it. It
also sets ARCHMAX_EMBEDDED_REDIS=on, which makes the dispatcher take the
single-dispatcher lease; it is the entrypoint’s to
set, never a deployment’s. One variable of the entrypoint tunes that Redis:
| Variable | Required | Default | Meaning |
|---|---|---|---|
ARCHMAX_EMBEDDED_REDIS_MAXMEMORY |
no | 256mb |
The memory ceiling of the container’s own Redis. The job queue needs it never to evict, so a full Redis refuses new jobs rather than dropping old ones. |
One more tells the container’s nginx which proxies in front may name the client:
| Variable | Required | Default | Meaning |
|---|---|---|---|
ARCHMAX_TRUSTED_PROXIES |
behind a load balancer | — | The addresses or CIDR ranges of the proxies in front, comma-separated (a VPC’s range behind an ALB). A request from one of them is counted as from the client its X-Forwarded-For names; anything else, as from the address it came from. Unset, every request counts as from the load balancer itself, so the API’s per-caller rate limits share one count among every caller. An entry that is no address or range stops the container. |
At build time
Section titled “At build time”| Variable | Required | Default | Meaning |
|---|---|---|---|
VITE_API_BASE_URL |
no | empty | The base URL the SPA calls the API at, inlined into the bundle when it is built. Empty or unset, the SPA calls the origin it is served from, which is what the image does: nginx proxies /api/ on that origin to the API. In development the Vite dev server proxies /api to http://localhost:3001. |
APP_VERSION |
no | dev |
The build argument the image’s APP_VERSION is set from. |
The SPA has no other build-time setting: everything a deployment differs by is read when the container starts, which is what lets one image serve every deployment.