Skip to content

Environment variables ​

A Mankomail instance is configured entirely through environment variables, read once when the process starts. This page lists every variable the application reads, grouped by topic, then the variables that only the reference Docker Compose file, the image build and the backup scripts use.

To change a value, edit it and restart the application. For installation steps, see Self-hosting; for how configuration fits into day-to-day operations, see Configuration.

How the application reads its configuration ​

These rules apply to every variable on this page that the application reads (all sections except the last two).

  • No variable is strictly required by the application. Each one has a default or is optional, and the process never refuses to start because of a configuration value. A real installation still needs at least DATABASE_URL, ENCRYPTION_KEY, PUBLIC_BASE_URL and the object storage variables; the reference Compose file refuses to start without them (see Variables of the reference Compose file).
  • An invalid value falls back to the default. The process logs a warning such as configuration: invalid value, falling back to the default (…) with the variable name, then carries on with the default. For an optional variable without a default, the warning is configuration: invalid value, ignored and the variable is treated as absent. Read the boot logs after any change.
  • An empty value counts as absent. PUBLIC_BASE_URL= behaves exactly like a missing PUBLIC_BASE_URL.
  • Every variable has a <NAME>_FILE form. ENCRYPTION_KEY_FILE=/run/secrets/encryption_key reads the value from that file (surrounding whitespace removed), which suits Docker and Kubernetes secrets. When both forms are set, the file wins. An unreadable or empty file produces a warning, and the plain variable is used instead, if set.
  • Booleans accept true, false, 1, 0, yes and no, in any case.
  • Bounds are inclusive. A number outside the documented range is invalid, and therefore replaced by the default.

The one exception to "never stops the boot" is DATABASE_SSL_CA: see Database.

Runtime and HTTP ​

VariableDefaultAccepted valuesEffect
NODE_ENVdevelopmentdevelopment, test, productionProduction mode. It turns on COOKIE_SECURE by default and the Strict-Transport-Security header. The Docker image sets it to production.
APP_ROLEallall, api, workerWhat this process runs. all: HTTP API, web interface and background work. api: HTTP only, no background work. worker: background work; the process still listens on its HTTP port.
APP_VERSION0.0.0Non-empty textVersion returned by /healthz and attached to every log line. The Docker image sets it from the APP_VERSION build argument.
HOST0.0.0.0Non-empty textInterface the HTTP server listens on.
PORT3000Integer from 1 to 65535Port the HTTP server listens on.
PUBLIC_BASE_URLhttp://localhost:3000Absolute URLThe address your users reach. See the warning below.
UI_DIST_DIRpublicPath, or emptyDirectory of the web interface files. If it is empty or does not exist, the process serves the API only.
COOKIE_SECUREtrue when NODE_ENV=production, false otherwiseBooleanWhether the session cookie carries the Secure attribute.
TRUST_PROXYtrueBoolean, number of hops, or addresses / CIDR ranges separated by commasWhich X-Forwarded-* headers the application believes.

PUBLIC_BASE_URL

If PUBLIC_BASE_URL is absent, empty or invalid, the instance starts with http://localhost:3000, without an error. Everything built from it is then wrong for your users:

  • the OAuth redirect URIs, <PUBLIC_BASE_URL>/api/v1/oauth/<provider>/callback, so connecting a Google or Microsoft mailbox fails at the provider;
  • the invitation links that administrators copy for new members, and the links in approval emails;
  • the Microsoft Graph push notification URL, <PUBLIC_BASE_URL>/hooks/push/msgraph, when MSGRAPH_NOTIFICATION_URL is not set.

When the variable is not set, two more things change: the Content Security Policy keeps a broad connect-src instead of being restricted to your own origin, and no site URL is announced to AI providers that accept one. Set it to the exact public URL, scheme, host and port included, without a trailing /.

COOKIE_SECURE

With COOKIE_SECURE=true, a sign-in request that does not arrive over HTTPS is refused with the error auth.https_required, because the browser would silently drop the cookie. HTTPS terminated by a reverse proxy counts, through the X-Forwarded-Proto header. COOKIE_SECURE=false sends the session cookie in clear text: use it only on a machine or network you control.

TRUST_PROXY

Keep true behind a reverse proxy: without it, every request appears to come from the proxy, so the sign-in rate limit applies to all users at once and HTTPS termination is invisible. Set false if the application port is exposed directly; otherwise a client can forge X-Forwarded-For and bypass the sign-in rate limit. 1 and 0 are read as booleans, not as a number of hops: write 2 or more for a hop count. The resolved value is logged at boot (proxy trust policy resolved).

Branding ​

The brand of a self-hosted instance. Every variable except BRAND_NAME is optional: left empty, the product default applies (monogram, theme palette). On an instance linked to a control plane, the brand it sends takes precedence field by field. See White label.

VariableDefaultAccepted valuesEffect
BRAND_NAMEMankomailNon-empty textName shown in the interface (sidebar, sign-in page, tab title), in emails sent by the instance (approval requests) and on the invitation page; announced to IMAP servers and AI providers.
BRAND_LOGO_URLnoneAbsolute URL (https recommended)Logo shown in the sidebar and on the sign-in page, in place of the monogram. A logo carries the name: the name text next to it is hidden.
BRAND_LOGO_DARK_URLnoneAbsolute URLLogo for the dark mode. Without it, BRAND_LOGO_URL is used in both modes.
BRAND_FAVICON_URLnoneAbsolute URLBrowser tab icon. Without it, the monogram is drawn in the primary color.
BRAND_PRIMARY_COLORnone#RRGGBBBecomes the theme accent (buttons, active navigation item, links, focus ring). Its lightness is recomputed for each theme, in light and dark mode, so that text stays readable (4.5:1).
BRAND_ACCENT_COLORnone#RRGGBBSecondary brand color (sign-in page panel).
BRAND_SUPPORT_URLnoneAbsolute URL“Help” link on the sign-in page and in the member menu.
BRAND_HIDE_POWERED_BYfalsetrue, falseHides the “Powered by Mankomail” line shown on the sign-in page and in system emails when the brand name differs from the product name.

Images are loaded by browsers: the page's content security policy allows https: images, plus the exact origin of an http: URL given here.

Control plane ​

Set the three variables together, or none. Without them (self-hosting), the instance calls nothing and sends nothing anywhere. With them, it sends a signed heartbeat every minute, reports its usage every five minutes, and reads its configuration (brand, plan, entitlements). If only one or two are set, the link stays off and the boot log says which one is missing. See White label.

VariableDefaultAccepted valuesEffect
CONTROL_PLANE_URLnoneAbsolute URL, http or httpsBase of the calls, for example https://api.example.com/api/instances/v1. A trailing slash is removed.
CONTROL_PLANE_INSTANCE_IDnoneText (ins_…)Public identifier of the instance.
CONTROL_PLANE_SECRETnoneTextShared HMAC secret. Treat it as a password; CONTROL_PLANE_SECRET_FILE is accepted.

Logs ​

VariableDefaultAccepted valuesEffect
LOG_LEVELinfofatal, error, warn, info, debug, trace, silentMinimum level written to standard output.
LOG_FORMATjsonjson, prettyShape of each log line. json for production; pretty for a development terminal.

With json, logs are JSON lines on standard output, with level, an ISO timestamp, role and version on every line: the format log tools such as jq or Loki read. With pretty, each line reads 10:04:12.345 INFO [mirror] message key=value, coloured in a terminal, with an error's stack trace indented below it; role and version are left out. In both formats, secrets such as the encryption key are redacted before the line is written. See Monitoring.

Database ​

PostgreSQL is the only mandatory dependency: it holds all the state of the instance, including the job queue.

VariableDefaultAccepted valuesEffect
DATABASE_URLpostgres://postgres:postgres@localhost:5432/postgresNon-empty connection stringThe PostgreSQL connection.
DATABASE_POOL_MAX10Positive integerMaximum number of connections per process.
DATABASE_SSLdisabledisable, no-verify, verify; also true/1/yes (= no-verify) and false/0/no (= disable)TLS mode of the connection.
DATABASE_SSL_CAnonePath to a PEM fileCertificate authority used by verify, for a private authority. Without it, the system authorities are used.
DATABASE_RUN_MIGRATIONStrueBooleanApply pending migrations at boot. With false, the process logs a warning and starts without migrating.
DATABASE_STATEMENT_TIMEOUT_MS30000Integer from 0 to 86400000Maximum duration of a query, in milliseconds. 0 sends no limit, so the server or role setting applies.
  • DATABASE_SSL modes. disable is right when the database is reached over a private network, as in the reference Compose file. no-verify encrypts the connection without authenticating the server: it protects against passive eavesdropping only. verify checks the server certificate chain, and is the mode to use as soon as the database is remote.
  • DATABASE_SSL_CA stops the boot if the file is unreadable. This is the only configuration value that does: with DATABASE_SSL=verify and an unreadable file, the process stops with DATABASE_SSL_CA: unreadable PEM file. It is ignored in the other modes.
  • Migrations run under a PostgreSQL advisory lock: several processes can start together, only one migrates. They are never affected by DATABASE_STATEMENT_TIMEOUT_MS.
  • The statement timeout applies to the connection pool only. A query that exceeds it is cancelled by PostgreSQL and the request returns an error, instead of holding a connection.
  • At boot, the process waits up to 60 seconds for the database to accept connections. A wrong password, an unknown role or a missing database stop it immediately.

Object storage ​

Email bodies, attachments and files added to runs are stored in an S3-compatible object storage (s3) or in a local directory (fs).

VariableDefaultAccepted valuesEffect
STORAGE_DRIVERs3s3, fsStorage backend: an S3-compatible bucket, or a directory on the server's disk.
STORAGE_ENDPOINTnoneAbsolute URLEndpoint of the S3-compatible service. Without it, the default AWS endpoint for the region is used.
STORAGE_REGIONus-east-1Non-empty textBucket region.
STORAGE_BUCKETproductNon-empty textBucket name. The application does not create it.
STORAGE_ACCESS_KEY_IDnoneNon-empty textAccess key.
STORAGE_SECRET_ACCESS_KEYnoneNon-empty textSecret key.
STORAGE_FORCE_PATH_STYLEtrueBooleanPath-style addressing (endpoint/bucket/key), needed by MinIO and most S3-compatible services.
STORAGE_FS_ROOT./.data/blobsNon-empty pathDirectory of the fs driver, relative to the working directory (/app in the Docker image). Ignored with s3.

The STORAGE_ENDPOINT to STORAGE_FORCE_PATH_STYLE variables apply to s3 only. Use one bucket per instance: object keys are not prefixed.

STORAGE_DRIVER=fs

The fs driver suits a single-server installation without MinIO. It gives the same guarantees as s3: each file is written to a temporary file then renamed, so a crash never leaves a half-written email; keys cannot escape the directory; deleting a mailbox removes its files. The process logs blob storage is a local directory at boot.

  • Put STORAGE_FS_ROOT on a persistent volume: in a container, a directory outside a volume disappears with the container, and every email body with it.
  • Every process of the instance must see the same directory. With several servers, use s3.
  • The backup scripts copy the MinIO bucket of the reference Compose file, not this directory: include STORAGE_FS_ROOT in your own backups.

Encryption key and first administrator ​

VariableDefaultAccepted valuesEffect
ENCRYPTION_KEYnone32 bytes: 64 hexadecimal characters, or base64Instance key (AES-256-GCM) that encrypts stored secrets: mailbox OAuth tokens, IMAP passwords, AI provider keys, other connections.
BOOTSTRAP_ADMIN_EMAILnoneEmail address (stored in lower case)Email of the first administrator.
BOOTSTRAP_ADMIN_PASSWORDnoneAt least 12 charactersPassword of the first administrator.

ENCRYPTION_KEY

Generate it with openssl rand -hex 32 or openssl rand -base64 32. It is never generated automatically.

  • Absent or invalid: the instance starts, logs ENCRYPTION_KEY is not set or ENCRYPTION_KEY is invalid, and secret storage is disabled: no mailbox, AI provider or connection secret can be saved.
  • Lost or changed: every stored secret becomes unreadable, and every mailbox has to be reconnected. Keep a copy outside the server. Backups do not contain it.

The first administrator is created at boot only if the instance has no member yet, and only if both variables are set. A password shorter than 12 characters is refused with a warning, and no account is created. Once a member exists, the two variables have no effect: they cannot reset a password or add an account.

Mailbox synchronisation ​

These variables govern the mirror, the local copy of each connected mailbox. See Mailboxes and the mirror.

VariableDefaultAccepted valuesEffect
BACKFILL_MONTHS12Integer from 1 to 240Depth of history copied when a mailbox is connected, in months.
BACKFILL_CHUNK_DAYS30Integer from 1 to 365Width of one slice of that initial copy, in days. An interrupted copy resumes from the last completed slice.
POLL_INTERVAL_SECONDS300Integer from 30 to 86400Interval of the periodic check of each mailbox. Push notifications speed synchronisation up; polling guarantees it.
WORKER_CONCURRENCY4Integer from 1 to 64Background jobs processed at the same time by this process. Keep it below DATABASE_POOL_MAX.
PUSH_SHARED_SECRETnoneAt least 16 charactersSecret expected in the token query parameter of the push endpoints /hooks/push/gmail and /hooks/push/msgraph.
GMAIL_PUBSUB_TOPICnoneNon-empty text, projects/<project>/topics/<topic>Google Cloud Pub/Sub topic used for Gmail push notifications.
MSGRAPH_NOTIFICATION_URLderivedAbsolute URLURL Microsoft Graph calls for notifications.
  • Without PUSH_SHARED_SECRET, both push endpoints answer 404 to every call, and synchronisation relies on polling alone. That is a valid set-up.
  • Without GMAIL_PUBSUB_TOPIC, Gmail mailboxes are not registered for push notifications; polling is used.
  • MSGRAPH_NOTIFICATION_URL is derived from PUBLIC_BASE_URL and PUSH_SHARED_SECRET (<PUBLIC_BASE_URL>/hooks/push/msgraph?token=…). Set it only if Microsoft must reach the instance through a different public address. Without PUSH_SHARED_SECRET there is no Microsoft push at all.

Sending ​

VariableDefaultAccepted valuesEffect
SEND_ENABLEDtrueBooleanInstance-wide kill switch. false stops every real send; drafts are not blocked.
SEND_MAX_PER_HOUR100Integer from 1 to 10000Sends per hour and per mailbox, used until an administrator sets the organisation's own rate.
SEND_MAX_BYTES26214400 (25 MB)Integer from 10000 to 67108864Maximum size of a composed message, encoded attachments included.
  • SEND_ENABLED=false cannot be reopened from the interface. The organisation's own switch, in Administration › Sending, can only restrict further. While sending is stopped, held sends are kept, not lost, and leave when sending reopens. Drafts are not blocked: saving a draft sends nothing. Set SEND_ENABLED=false on any copy of a production instance, such as a restored backup, before starting it: otherwise the copy sends real emails.
  • SEND_MAX_PER_HOUR is the default of the rate set in Administration › Sending. It is counted per sending mailbox, for every send: webmail, workflow runs and approval requests. A send over the limit is postponed until the mailbox has budget again (workflows, approvals) or refused with webmail.rate_limited and the delay to wait (webmail); it is never lost.
  • SEND_MAX_BYTES is also enforced when an attachment is uploaded in the webmail: a file that alone would exceed it is refused right away.

AI models ​

AI provider keys are not environment variables: an administrator enters them in Connections, and they are stored encrypted with ENCRYPTION_KEY.

VariableDefaultAccepted valuesEffect
LLM_REQUEST_TIMEOUT_MS120000Integer from 5000 to 600000Time limit of one model call, in milliseconds.
LLM_MAX_REQUESTS_PER_MINUTE60Integer from 1 to 10000Calls per minute and per provider, shared by every process of the instance. A call over the limit is postponed, not failed.
LLM_DEFAULT_MAX_OUTPUT_TOKENS4096Integer from 16 to 128000Output token limit of a call that does not set its own.

A slow local model (for example Ollama on CPU) may need a longer LLM_REQUEST_TIMEOUT_MS. Set LLM_MAX_REQUESTS_PER_MINUTE according to the rate allowed by your own provider account.

Mailbox analyzer ​

Thresholds of the analyzer, which studies a mailbox and proposes automations.

VariableDefaultAccepted valuesEffect
ANALYZER_TARGET_MESSAGES150Integer from 10 to 100000The analysis uses the shortest window among 30, 90, 180 and 365 days that holds at least this many received emails; otherwise 365 days.
ANALYZER_MIN_GROUP_VOLUME3Integer from 2 to 1000Minimum size of a group of similar emails.
ANALYZER_MIN_GROUP_SHARE0.02Number from 0 to 0.5Proportional threshold. A group must reach the larger of ANALYZER_MIN_GROUP_VOLUME and this share of the window's emails, rounded up.
ANALYZER_MAX_CLUSTERS12Integer from 1 to 30Groups submitted to the model per analysis.
ANALYZER_LLM_BATCH_SIZE1Integer from 1 to 10Groups per model call. 1 (one call per group) is the most reliable.
ANALYZER_LLM_CONCURRENCY3Integer from 1 to 10Model calls running at the same time for one analysis.
ANALYZER_SAMPLE_SIZE5Integer from 1 to 20Subjects and previews sampled per group. Email bodies are never sent.
ANALYZER_MAX_OPPORTUNITIES6Integer from 1 to 20Proposals kept per report.
ANALYZER_MAX_OUTPUT_TOKENS16000Integer from 1000 to 128000Output token limit of each call.
ANALYZER_SUGGESTION_WINDOW_DAYS30Integer from 1 to 365Window of the scan for emails no workflow handles, in days.
ANALYZER_SUGGESTION_MIN_VOLUME15Integer from 1 to 10000Unhandled emails from one domain, within that window, before a suggestion is made.

Approvals and waits ​

VariableDefaultAccepted valuesEffect
APPROVAL_REMINDER_FRACTION0.5Number from 0 to 0.9When a pending approval gets its reminder, as a fraction of its time limit. 0.5 = halfway; 0 disables reminders.
WAIT_MAX_DAYS730Integer from 1 to 3650Longest wait a workflow can request, in days.
SIGNAL_RETENTION_HOURS24Integer from 1 to 720How long a signal stays valid for a wait that is not in place yet, in hours.

The time limit of an approval is set in each workflow, on the approval node; the reminder fraction adapts to it. See Review and approvals.

Assistant ​

Limits of one conversation with the assistant that builds and fixes workflows. The provider and model are chosen in Connections.

VariableDefaultAccepted valuesEffect
ASSISTANT_MAX_TURNS16Integer from 1 to 60Model calls at most for one message of the member.
ASSISTANT_MAX_TOKENS_PER_CONVERSATION2000000Integer from 10000 to 50000000Tokens (input and output) at most for a whole conversation.
ASSISTANT_MAX_TOOL_RESULT_CHARS30000Integer from 2000 to 200000Characters at most of one tool result given back to the model.
ASSISTANT_MAX_OUTPUT_TOKENS6000Integer from 512 to 64000Output token limit of one call.

Loops ​

Instance limits for loop nodes. A loop node's own settings apply on top and can only be stricter.

VariableDefaultAccepted valuesEffect
LOOP_MAX_ITERATIONS500Integer from 1 to 500Most iterations a loop may run. A loop over more items fails: it never processes part of the list silently.
LOOP_MIN_ITERATIONS10Integer from 1 to 500Floor of the previous limit: the effective limit is the larger of the two values, so the instance cannot make loops unusable by mistake.
LOOP_MAX_CONCURRENCY5Integer from 1 to 5Iterations run in parallel, whatever the node asks.
LOOP_SIMULATED_MAX_ITERATIONS3Integer from 1 to 50Iterations started by a test run in the editor. The result of the test run says it was cut short.
LOOP_MAX_COLLECTED_BYTES262144 (256 KB)Integer from 4096 to 8388608Size budget of the results gathered when a loop finishes. Beyond it, per-iteration data is left out and the result is marked truncated.

Integrations and run files ​

VariableDefaultAccepted valuesEffect
INTEGRATION_POLL_MIN_MINUTES5Integer from 1 to 1440Shortest interval of a trigger that polls a third-party service, in minutes. A node can ask for a longer interval, never a shorter one.
EXECUTION_ATTACHMENT_MAX_BYTES26214400 (25 MB)Integer from 1024 to 67108864Maximum size of one file a node adds to a run.
EXECUTION_ATTACHMENTS_MAX_TOTAL_BYTES104857600 (100 MB)Integer from 1024 to 536870912Total size of the files added to one run.
EXECUTION_ATTACHMENTS_MAX_COUNT20Integer from 1 to 500Number of files added to one run.

Files added to a run are kept as long as the run itself.

Tables ​

Limits of Tables. Each one has an absolute maximum that no configuration can exceed.

VariableDefaultAccepted valuesEffect
TABLES_MAX_TABLES100Integer from 1 to 1000Tables the organisation can create.
TABLES_MAX_COLUMNS60Integer from 1 to 200Columns per table.
TABLES_MAX_ROWS50000Integer from 1 to 500000Rows per table.
TABLES_MAX_CELL_CHARS4000Integer from 1 to 20000Characters per cell.
TABLES_OP_RETENTION_DAYS30Integer from 1 to 365Days the record of each table write made by a node is kept. This record lets a retried step recognise a write it already made, so a retry never adds a second row or comment. The hourly maintenance job deletes older records; table rows are never affected.

Retention ​

VariableDefaultAccepted valuesEffect
SIMULATED_EXECUTIONS_RETENTION_DAYS7Integer from 1 to 365Days test runs from the editor are kept, with their steps.
AUDIT_LOG_RETENTION_DAYS365Integer from 30 to 3650Days entries of the audit log are kept.
SCHEDULED_TASKS_RETENTION_DAYS7Integer from 1 to 90Days the trace of completed or abandoned scheduled tasks (polls, renewals, maintenance) is kept.

The other retention periods are fixed in the current version and applied by an hourly maintenance job:

DataKept for
Live runs180 days
Outbound operations (sends, moves, flags)180 days
Ingestion journal365 days
AI usage accounting365 days
Jobs that exhausted their attempts30 days

Telemetry ​

The instance sends no telemetry, and no variable configures it.

Variables of the reference Compose file ​

The reference Compose file, compose.reference.yaml, reads these variables from .env for its own use. The application never reads them directly.

VariableDefaultUse
POSTGRES_PASSWORDrequiredPassword of the PostgreSQL database, also used to build DATABASE_URL. It is fixed when the database volume is first created: changing it afterwards in .env does not change it in PostgreSQL.
POSTGRES_USERSet in the Compose filePostgreSQL user.
POSTGRES_DBSet in the Compose filePostgreSQL database name.
STORAGE_ACCESS_KEY_IDrequiredAlso the MinIO root user.
STORAGE_SECRET_ACCESS_KEYrequiredAlso the MinIO root password.
ENCRYPTION_KEYrequiredPassed to the application.
PUBLIC_BASE_URLrequiredPassed to the application.
STORAGE_BUCKETSet in the Compose fileBucket created by the one-off createbucket job and used by the application.
COMPOSE_PROJECT_NAMESet in the Compose filePrefix of containers and volumes. Change it to run two instances on one machine.
APP_IMAGESet in the Compose file (a locally built image)Application image.
APP_BIND127.0.0.1Interface the application port is published on. 0.0.0.0 exposes plain HTTP on the network.
APP_PORT3000Port published on the host.
APP_CPUS, APP_MEMORY2, 2gResource limits of the application container.
POSTGRES_IMAGEpostgres:16PostgreSQL image.
POSTGRES_CPUS, POSTGRES_MEMORY, POSTGRES_SHM_SIZE2, 2g, 256mResource limits and shared memory of PostgreSQL.
MINIO_IMAGE, MC_IMAGEpinned MinIO release, minio/mc:latestImages of MinIO and of the bucket job.
MINIO_BROWSERoffMinIO web console.
MINIO_CPUS, MINIO_MEMORY1, 1gResource limits of MinIO.

The Compose file refuses to start when one of the five required variables is missing. It also sets some application variables itself, whatever .env says: NODE_ENV=production, PORT=3000, STORAGE_DRIVER=s3, STORAGE_ENDPOINT=http://minio:9000, STORAGE_FORCE_PATH_STYLE=true, and DATABASE_URL built from the POSTGRES_* variables.

Only listed variables reach the application

The Compose file passes an explicit list of variables to the application container: identity, runtime, database, storage, encryption, first administrator, synchronisation, sending and two retention settings. Any other variable from this page, such as TRUST_PROXY, COOKIE_SECURE, LLM_*, ANALYZER_*, TABLES_* or LOOP_*, must be added under services.app.environment, for example in an extra Compose file, to take effect.

Image build and backup scripts ​

Build arguments of the Docker image:

ArgumentDefaultUse
APP_VERSION0.0.0-dev (0.0.0-selfhost through the reference Compose file)Becomes the APP_VERSION variable of the image.
GIT_SHAunknownRecorded as an image label and an environment variable; the application does not read it.
NODE_IMAGEnode:24-slimBase image.

The image also sets NODE_ENV=production, APP_ROLE=all, HOST=0.0.0.0 and PORT=3000.

Backup and restore scripts read these variables from their own environment. They also read POSTGRES_USER, POSTGRES_DB, POSTGRES_PASSWORD, STORAGE_*, ENCRYPTION_KEY, APP_PORT and APP_VERSION from the .env file. See Backups.

VariableDefaultUse
COMPOSE_FILEcompose.reference.yaml at the repository rootCompose file the scripts drive.
ENV_FILE.env at the repository rootEnvironment file the scripts read.
BACKUP_KEEP7Number of backups kept in the output directory; older ones are deleted. 0 keeps them all.
RESTORE_YESnone1 skips the confirmation prompts of the restore, which otherwise ask you to type YES.

The scripts write their messages in English.