Files
TREK/wiki/Environment-Variables.md
Maurice f901982903 fix(llm-parse): LLM_MAX_OUTPUT_TOKENS sets the answer cap, a cut-off answer says so
The OpenAI-compatible client always asked for 4096 tokens, and a document
whose bookings needed more came back as "no reservations" with nothing that
pointed at the cap. LLM_MAX_OUTPUT_TOKENS now sets the cap for every
provider; unset keeps 4096 and 8192 so a small local model's context still
fits. A finish_reason of "length" is reported like Anthropic's max_tokens
stop, naming the limit and the variable that raises it.

Closes #2677
2026-10-18 13:21:50 +02:00

68 KiB

Environment Variables

Complete reference for all environment variables TREK reads.

How to Set Variables

  • Docker Compose — use the environment: block or a .env file alongside docker-compose.yml
  • Docker run — pass each variable with -e VARIABLE=value
  • Helm — use env: for plain values and secretEnv: for sensitive values in values.yaml. The chart only passes through the keys it declares (37 in templates/configmap.yaml, the credentials in templates/secret.yaml), so a variable that is not one of them (DEFAULT_LANGUAGE, FILE_UPLOAD_LIMIT_MB, RESTORE_FROM_BACKUP and MCP_MAX_SESSION_PER_USER among them) is dropped silently. Patch it onto the Deployment or add it to the chart
  • Unraid — set in the container template editor
  • Proxmox Community Script — set in /opt/trek/server/.env

Startup Validation

TREK checks almost all of this surface once at boot. A variable that is unset or blank falls back to its documented default; a variable that is present but malformed aborts startup, with an aggregated report naming every offending value:

Invalid environment configuration:
  - PORT="not-a-port": must be a port number (1-65535)
  - SESSION_DURATION="bogus": must be a duration like "1h", "7d" or "30d"

The value of a secret is never printed: ENCRYPTION_KEY, SMTP_PASS, OIDC_CLIENT_SECRET, VAPID_PRIVATE_KEY and the other keys, secrets and passwords appear as ***, so the report names the variable and the problem only.

In Docker this crash-loops the container until the value is corrected or removed. Boolean switches accept true/false, 1/0, on/off, yes/no in any casing — anything else counts as malformed. Variables TREK does not know are passed through untouched.

TREK_DB_JOURNAL_MODE and TREK_DB_SYNCHRONOUS are exceptions: they log a warning and fall back instead of aborting, because reset-admin.js — the way back into a locked-out instance — reads the same two variables and has to keep working. The three plugin caps TREK_PLUGIN_AI_PER_DAY, TREK_PLUGIN_NOTIFY_PER_DAY and TREK_PLUGIN_AUDIT_MAX_ROWS are read by the plugin host rather than by the boot schema, so a malformed value there falls back to the default without a warning. NODE_ENV and TZ are not validated at all, so a non-standard value like NODE_ENV=staging still boots. Spaces around NODE_ENV, LOG_LEVEL and DEFAULT_LANGUAGE are trimmed at startup with a warning in the log; the web UI is only served with NODE_ENV=production in lowercase.


Core

Variable Description Default
PORT Server port Sources: 3001, Docker: 3000
HOST Bind address for the HTTP server (e.g. 127.0.0.1, 10.0.0.72). Source / Proxmox installs only — do not set this in Docker or any containerized deployment. See note below. all interfaces
NODE_ENV Environment (production / development) production
ENCRYPTION_KEY At-rest encryption key — see resolution order below auto
TZ Timezone for logs, reminders, and cron jobs (e.g. Europe/Berlin) UTC
LOG_LEVEL info = concise user actions; debug = verbose details info
DEFAULT_LANGUAGE Default language on the login page — see supported codes below en
SESSION_DURATION How long a login session stays valid before re-login is required. Used when "Remember me" is unchecked on the login form (the default): applies to the trek_session JWT exp claim, and the cookie is issued as a browser-session cookie (no maxAge, cleared when the browser closes). Accepts ms-style strings: 1h, 12h, 7d, 30d, 90d. Invalid values abort startup — see Startup Validation above. Does not affect the short-lived MFA challenge token or MCP OAuth tokens (those keep their own TTL). 24h
SESSION_DURATION_REMEMBER Session length used when the user ticks "Remember me" on login: a longer-lived JWT exp claim plus a persistent trek_session cookie whose maxAge matches, so the session survives browser restarts. Same ms-style format and the same startup validation as SESSION_DURATION. Also the lifetime of every SSO session when OIDC_ONLY=true, where the login page has no such switch. 30d
ALLOWED_ORIGINS Comma-separated origins for CORS and email notification links same-origin
ALLOW_INTERNAL_NETWORK Allow outbound requests to private/RFC-1918 IPs. Set true if Immich, a document store bound through Document-Sync or other integrated services are on your local network. Loopback (127.x) and link-local (169.254.x) addresses remain blocked regardless, apart from a link-local address listed in ALLOW_LINK_LOCAL_IPS. false
ALLOW_LINK_LOCAL_IPS Single link-local addresses (169.254.x.x) TREK may reach after all, comma-separated. Meant for the host gateway of a rootless Podman container, 169.254.1.2, with an identity provider or another service behind it. OIDC, AI Parsing, plugin OAuth and your own routing engines reach a listed address directly; the other integrations treat it as internal, so they also need ALLOW_INTERNAL_NETWORK=true. 169.254.169.x and 169.254.170.x, where cloud providers serve metadata and credentials, cannot be listed, and an entry that is not a single link-local IPv4 aborts startup. See Internal-Network-Access. unset
APP_URL Public base URL (e.g. https://trek.example.com). Required when OIDC is enabled — must match the redirect URI registered with your IdP. Also used as the base URL for email notification links and subscribable calendar feed URLs (the webcal:///https:// links the Subscribe dialog hands to Google/Apple/Outlook). —
TREK_WIKI_DIR Where the in-app Help pages (/help) read their content from. TREK ships this wiki and serves it from disk, so the docs always match the version you are running. You should not need to set this — it is an escape hatch for unusual layouts. If the directory cannot be found, Help falls back to fetching the repository's wiki/ folder from the main branch on GitHub (which can be ahead of the release you are running, and needs outbound network access). the bundled wiki/ directory

HOST — Source and Proxmox installs only

By default TREK binds to all network interfaces (0.0.0.0), which is the correct behaviour inside a container because Docker handles port exposure at the host level. Setting HOST overrides the bind address at the Node.js level.

When to use it: only when running TREK directly on a host (git sources or the Proxmox community script) and you need to restrict which interface the server listens on — for example, to expose TREK only on a LAN interface while keeping it off the public-facing one.

Never set HOST in Docker, Docker Compose, Helm, or Unraid deployments. Use Docker's -p <host-ip>:<host-port>:<container-port> syntax or your orchestrator's port binding instead.

# .env — source / Proxmox installs only
HOST=10.0.0.72   # bind only on this LAN interface
PORT=3001

When HOST is set, the startup banner includes a Host: line confirming the bound address.

ENCRYPTION_KEY — Resolution Order

server/src/config.ts resolves the encryption key in this order:

  1. ENCRYPTION_KEY env var — explicit value, always takes priority. Persisted to data/.encryption_key automatically.
  2. data/.encryption_key file — present on any install that has started at least once.
  3. data/.jwt_secret file — one-time fallback for existing installs upgrading without a pre-set key. The value is immediately persisted to data/.encryption_key so JWT rotation cannot break decryption later.
  4. Auto-generated — fresh install with none of the above; persisted to data/.encryption_key.

Setting ENCRYPTION_KEY explicitly is recommended so you can back it up independently of the data volume.

DEFAULT_LANGUAGE — Supported Codes

You can set DEFAULT_LANGUAGE to any of the 27 languages TREK ships. The currently supported codes are:

Code Language
en English
de Deutsch
es Español
et Eesti
fr Français
hu Magyar
nl Nederlands
br Português (Brasil)
cs Česky
sk Slovenčina
pl Polski
ru Русский
zh 简体中文
zh-TW 繁體中文
it Italiano
tr Türkçe
ar العربية
az Azərbaycanca
id Bahasa Indonesia
ja 日本語
ko 한국어
th ไทย
uk Українська
gr Ελληνικά
sv Svenska
vi Tiếng Việt
ca Català

If you set a code that isn't on this list, TREK refuses to start and prints DEFAULT_LANGUAGE="…": must be one of: …. Leave the variable unset to use English (en). This list grows as new translations are added to TREK.


Outbound HTTP(S) Proxy

TREK can route supported outbound HTTP(S) requests through a proxy by setting the standard variables below. Outbound proxying is disabled by default.

Variable Description Default
HTTP_PROXY Proxy URL for outbound HTTP requests —
HTTPS_PROXY Proxy URL for outbound HTTPS requests —
NO_PROXY Comma-separated hosts or domains that should bypass the proxy —

Note: Requests that go through TREK's SSRF protection (place photos, link previews, webhooks, Immich, Synology, Dawarich and the other integrations) read the same three variables themselves and use the proxy as well, on any install. Everything else follows Node's own environment proxy, which needs NODE_USE_ENV_PROXY=1 (see below). NO_PROXY takes *, a host (which also matches its subdomains, a leading dot is optional) or host:port.

Container only. Node ignores these variables unless it is started with NODE_USE_ENV_PROXY=1, and the official image sets that for you. On a source or Proxmox install, set NODE_USE_ENV_PROXY=1 alongside them or nothing will change. On Helm the image already has it, and the chart passes HTTP_PROXY, HTTPS_PROXY and NO_PROXY from env: through to the container.

Set NO_PROXY. Without it every request goes to the proxy, including the ones TREK makes to itself, such as the container health check. localhost,127.0.0.1 is a sensible minimum; add your own hosts as needed.


HTTPS / Reverse Proxy

These three variables work together behind a TLS-terminating reverse proxy. See Reverse-Proxy for the full explanation.

Variable Description Default
FORCE_HTTPS When true: 301-redirects HTTP→HTTPS, sends HSTS (max-age=31536000), adds CSP upgrade-insecure-requests, forces cookie secure flag. Only useful behind a TLS proxy. Your proxy must send X-Forwarded-Proto: https. false
HSTS_INCLUDE_SUBDOMAINS When true: adds the includeSubDomains directive to the HSTS header, extending HTTPS enforcement to all subdomains. Only effective when HSTS is active (FORCE_HTTPS=true or NODE_ENV=production). Leave false if you run other services on sibling subdomains over plain HTTP. false
TRUST_PROXY Number of trusted proxy hops. Tells Express how far into X-Forwarded-For to look for the real client IP, and where to read X-Forwarded-Proto. Count your hops: with two proxies in front of TREK and TRUST_PROXY=1, the IP on every audit row is the inner proxy's. 0 trusts nothing and always uses the socket address. Not required for the FORCE_HTTPS redirect, which reads the X-Forwarded-Proto header directly — set it for correct client IPs in the audit log, and for the COOKIE_SECURE auto-derivation, which goes through req.secure. 1 (production)
HTTP_KEEP_ALIVE_TIMEOUT_MS How long (in milliseconds) the server keeps an idle keep-alive connection open; the headers timeout follows one second above it. Keep it above your reverse proxy's upstream idle timeout (60 s for nginx and most load balancers, 90 s for Traefik), otherwise the proxy reuses a connection the server just closed and answers that request with a 502. Between 1000 and 290000. 95000
COOKIE_SECURE Controls the secure flag on the trek_session cookie. Auto-derived as true when NODE_ENV=production, when FORCE_HTTPS=true, or when the request itself arrived over TLS on the outermost hop (X-Forwarded-Proto: https with TRUST_PROXY set). Set to false only as an escape hatch for LAN testing without TLS — not recommended in production. auto

Warning: FORCE_HTTPS=true behind a proxy that does not forward X-Forwarded-Proto: https causes a redirect loop — every request looks like plain HTTP to TREK and gets 301'd again. Fix the proxy to send the header. Setting TRUST_PROXY does not help: the redirect accepts the raw X-Forwarded-Proto: https header on its own, whatever trust proxy is set to. (TRUST_PROXY only feeds the second half of the check, req.secure, which additionally covers a proxy chain that sends a comma-joined X-Forwarded-Proto — and production already trusts one hop by default.)


OIDC / SSO

For setup instructions, see OIDC-SSO.

Variable Description Default
OIDC_ISSUER OpenID Connect provider URL (e.g. https://auth.example.com) —
OIDC_CLIENT_ID OIDC client ID —
OIDC_CLIENT_SECRET OIDC client secret —
OIDC_DISPLAY_NAME Label shown on the SSO login button SSO
OIDC_ONLY Force SSO-only mode: disables password login and registration, overrides Admin > Settings toggles, cannot be changed at runtime. First SSO login becomes admin on a fresh instance. Since the login page then has no "Remember me" switch, every SSO session gets the SESSION_DURATION_REMEMBER lifetime; shorten that variable for shorter sessions, SESSION_DURATION does not apply here. false
OIDC_ADMIN_CLAIM OIDC claim inspected for the admin role. Only takes effect once OIDC_ADMIN_VALUE is set. groups
OIDC_ADMIN_VALUE Value of the OIDC claim that grants admin role (e.g. app-trek-admins) —
OIDC_USERNAME_CLAIM Claim a new account's username is built from, e.g. preferred_username when your provider sends the full name in name. Only used when the account is created; an empty claim falls back to the default order. name, then preferred_username
OIDC_SCOPE Space-separated OIDC scopes to request. Fully replaces the default — always include openid email profile plus any extra scopes (e.g. add groups when using OIDC_ADMIN_CLAIM) openid email profile
OIDC_DISCOVERY_URL Override the auto-constructed OIDC discovery endpoint. Required for providers with a non-standard path (e.g. Authentik) —

WebAuthn / Passkeys

Passkey (WebAuthn) login is configured from the Admin panel, but the two cryptographically sensitive values can be pinned via environment variables. Env vars take priority over the corresponding database settings. These values are only ever derived from server-side config — never from request Host / X-Forwarded-Host headers (mirroring OIDC redirect-URI handling).

Variable Description Default
WEBAUTHN_RP_ID Relying-Party ID — the registrable domain passkeys are bound to (e.g. trek.example.com). Overrides the webauthn_rp_id DB setting. When unset, it is derived from the hostname of APP_URL. Bare IP literals (IPv4/IPv6) are rejected. If it cannot be resolved, passkeys are disabled. derived from APP_URL
WEBAUTHN_ORIGINS Comma-separated list of allowed origins for passkey ceremonies (e.g. https://trek.example.com). Overrides the webauthn_origins DB setting; trailing slashes are stripped. When unset and the RP ID is not localhost, a single origin is derived from APP_URL. In dev (RP ID localhost) http://localhost:5173 and http://localhost:3001 are added automatically. derived from APP_URL

Email / SMTP

SMTP settings can be configured via the Admin panel or overridden with environment variables. Env vars take priority over the database values.

Variable Description Default
SMTP_HOST SMTP server hostname (e.g. smtp.example.com) —
SMTP_PORT SMTP server port. Port 465 enables implicit TLS (secure: true); all other ports use STARTTLS or plain. —
SMTP_USER SMTP authentication username —
SMTP_PASS SMTP authentication password —
SMTP_FROM Sender address for outbound emails (e.g. TREK <noreply@example.com>) —
SMTP_SKIP_TLS_VERIFY Set true to disable TLS certificate validation. Useful for self-signed certs on internal SMTP relays — not recommended in production. false

SMTP_HOST, SMTP_PORT, and SMTP_FROM are all required for email delivery to work. SMTP_USER and SMTP_PASS are optional (for unauthenticated relays).


Web Push

Web Push signs every message with a VAPID key pair. None of these variables is needed: without them TREK generates a pair on first start and keeps it in the database, the private half encrypted with ENCRYPTION_KEY, so it moves with every backup and survives a restore.

Variable Description Default
VAPID_PUBLIC_KEY Public half of your own key pair: the uncompressed P-256 point, base64url (65 bytes). Only used together with VAPID_PRIVATE_KEY. generated
VAPID_PRIVATE_KEY Private half of that pair: the 32-byte P-256 key, base64url. generated
VAPID_SUBJECT The contact the push services see with every message: a mailto: address or an https:// URL. APP_URL when it is https://, else https://github.com/liketrek/TREK

A value in the wrong shape aborts startup like any other variable on this page; for VAPID_PRIVATE_KEY the report prints *** instead of the value. A pair whose halves do not belong together, or only one half of a pair, does not stop TREK from starting, but push is off with an error in the log until the variables are fixed: the Push card and column disappear from Settings → Notifications, the routes that register a device answer 503, nothing is sent, and every registered device stays registered. TREK never signs with another pair in its place, neither one stored in the database (from a start without the variables) nor a newly generated one: no device registered with yours holds that key, and the first message sent with it would drop every one of them. Once both variables hold the two halves of your pair again, every device receives as before. Any tool that prints the standard Web Push pair works, for example npx web-push generate-vapid-keys.

Without VAPID_SUBJECT, the contact is APP_URL when that is an https:// address other than localhost, and otherwise the project page. Unlike links in emails, it never falls back on the first ALLOWED_ORIGINS entry, which may be another site that is only allowed to call the API. TREK never fills in an email address on its own, so no admin address travels to the push services.

TREK generates a pair only while neither web_push_vapid_public_key nor web_push_vapid_private_key exists in app_settings, and never overwrites a pair that is there. When no pair comes from the variables and the stored private key cannot be decrypted with the current ENCRYPTION_KEY (after a restore under a different key, for example), push is off: the log says why, the Push card and column disappear from every user's Settings → Notifications (the routes that register a device answer 503), nothing is sent, and every registered device stays registered. Starting TREK with the original ENCRYPTION_KEY again brings push back on every device as it was. A stored private key that does not belong to the stored public key, or only one of the two rows, turns push off the same way. To start over with a new pair instead, delete both rows from app_settings; TREK then generates a new pair, and every device has to subscribe again.

Browsers bind each subscription to the public key it was made with. After a switch to another pair (setting the variables on an instance that has been using its generated pair, changing them, removing them again, or a newly generated pair), TREK drops each old subscription the first time it would have sent to it. A device subscribes again with the new key on its own the next time TREK is opened on it, where the browser allows that without a tap; elsewhere, push shows as off in Settings → Notifications and has to be turned on again there.

On Helm, VAPID_PUBLIC_KEY and VAPID_SUBJECT go under env: and VAPID_PRIVATE_KEY under secretEnv:.


Initial Setup

These variables only take effect on first boot, before any user exists.

Variable Description Default
ADMIN_EMAIL Email for the first admin account admin@trek.local
ADMIN_PASSWORD Password for the first admin account random

Both variables must be set together. If either is omitted, the account is created with email admin@trek.local and a randomly generated password that is printed to the server log. Once any user exists, these variables have no effect.


MCP

For setup instructions, see MCP-Overview.

Variable Description Default
MCP_RATE_LIMIT Max MCP API requests per minute, counted per user and per OAuth client — a user connected from two MCP clients gets this budget twice. Requests made with a static trek_ token or a session JWT carry no client and share one further bucket per user. 300
MCP_MAX_SESSION_PER_USER Max concurrent MCP sessions per user. At the cap the user's least-recently-active session is closed to make room — requests are not rejected. 20
MCP_SESSION_TTL Session idle timeout in seconds (max 86400) 3600
MCP_SSE_KEEPALIVE SSE keep-alive ping interval in seconds — keeps the stream alive through reverse proxies. 0 disables the pings; an open stream still refreshes the session's idle timeout. 25

API Docs

Variable Description Default
TREK_API_DOCS_ENABLED Serve interactive OpenAPI/Swagger docs at /api/docs (raw spec at /api/docs-json). The spec enumerates every route including the admin surface, so it is off by default. false

With the flag on, /api/docs lists every REST endpoint with try-it-out; authorize with a session JWT via the Bearer button (the API accepts Authorization: Bearer <jwt> everywhere as the cookie fallback). Request bodies validated with Zod are documented automatically from the same schemas.


Booking Import (KDE Itinerary)

Variable Description Default
KITINERARY_EXTRACTOR_PATH Full path to the kitinerary-extractor binary. When unset, TREK searches /usr/lib/*/libexec/kf6/kitinerary-extractor and then PATH. Set this if you install the binary to a non-standard location. auto-detected

The official TREK Docker image bundles the binary automatically: on both amd64 and arm64 it installs libkitinerary-bin via apt (Debian trixie) and symlinks it to /usr/local/bin/kitinerary-extractor, which the image also pins via KITINERARY_EXTRACTOR_PATH. When running TREK from source, install libkitinerary-bin (Debian trixie / Ubuntu 25.04+). There is no static binary to download; to run a newer extractor than your distribution packages, build one and point KITINERARY_EXTRACTOR_PATH at it, or derive an image from the official one with your own apt sources. The extractor's version is in the startup log and in GET /api/admin/system-info (admin only) — worth checking before reporting that a provider is unsupported, since a missing vendor script and an unsupported provider both come back empty. LOG_LEVEL=debug additionally passes the extractor's raw stderr through, including the JS ERROR lines that name a failing script; it is read once at startup, so it needs a restart. The GET /api/health/features endpoint returns { "bookingImport": true } when the binary is found. The Import button in the Reservations panel is hidden only when neither extractor is available — an instance running the AI Parsing addon still offers it with bookingImport: false.

Booking import can also fall back to an AI model for documents KDE Itinerary can't read. That feature (the AI Parsing addon) is configured in the UI; the only environment variables it reads are LLM_TIMEOUT_MS and LLM_MAX_OUTPUT_TOKENS below. See AI-Booking-Import.


Public Transit (Transitous)

Public-transit routing in the planner is powered by Transitous, a free community MOTIS service — no API key is required. See Transport: Flights, Trains, Cars for the feature itself.

An admin can switch the backend under Admin → Settings → API Keys → Transit Provider: Transitous (free) is the default and the fallback, Google answers with the Google API key set on the same card and is billed per search. While no Google key is set, searches keep going to Transitous. TRANSIT_API_URL below only applies to the Transitous path.

Variable Description Default
TRANSIT_API_URL Base URL of the transit routing API. TREK's server proxies requests to it. Point this at your own self-hosted MOTIS instance if you want zero third-party egress. A trailing slash is stripped. https://api.transitous.org

When left at the default, using the transit feature makes the TREK server send outbound HTTPS requests to api.transitous.org (with an identifying User-Agent, as the Transitous usage policy asks). No transit request is made until a user actually searches for a journey.


Image Search (Unsplash)

TREK can search Unsplash for trip cover images and place images. By default the server queries Unsplash's public web endpoint without an API key, so no configuration is needed on most installs.

Some hosting environments — commonly VPS and datacenter IP ranges (and many Kubernetes clusters) — are blocked or rate-limited by that unauthenticated endpoint, which surfaces in the UI as "Unsplash search unavailable". Configuring a free Unsplash Access Key switches the server to Unsplash's official, authenticated API (api.unsplash.com), which is not subject to that block. See issue #1449.

Variable Description Default
UNSPLASH_ACCESS_KEY Unsplash Access Key used to authenticate cover/place image search against https://api.unsplash.com. When set, it takes priority over any key configured per-admin in Admin → Settings. When unset, the server falls back to the unauthenticated endpoint (which some datacenter/VPS IPs are blocked from). Get a free key at unsplash.com/developers. unauthenticated endpoint

Two ways to configure it — pick one; the env var wins if both are present:

  1. Environment variable (this page) — instance-wide, ideal for Docker/Helm/Unraid where you already manage config as env. The Unsplash API Key field in the admin panel is then read-only and names the variable.
  2. Admin → Settings → API Keys — paste the key into the Unsplash API Key field. Stored encrypted at rest and used as a fallback for every user when no env var is set. This is the better option if you'd rather not restart the container to change it.

To get a key: create a free account at unsplash.com/developers, register a new application, and copy its Access Key (not the Secret Key). The Unsplash free tier (demo) allows 50 requests/hour, which is ample for cover search.


Place Search (TREK Places API)

TREK's own place index, the TREK Places API, answers the suggestions and, together with OpenStreetMap, the full search in the place form, the category buttons on the trip map, the Road trip search along the drive, import geocoding and the offline download of a trip's surroundings. It needs no key, it is on by default, and there is no switch for it in the admin panel: whether searches leave the instance is set here.

Variable Description Default
TREK_PLACES_ENABLED Set to false to stop asking the index. Search then works as it did before 4.3.0: OpenStreetMap on an install without a key, the keyed provider (Google or Amap) when one is set, the Overpass mirrors for the category buttons, and no places downloaded for offline search. It is a boolean switch like the others on this page: false, 0, off and no in any casing switch it off, a value that is not boolean-like aborts startup, and unset or blank leaves the index on. on
TREK_PLACES_URL Base URL of a copy of the service you run yourself; it has to answer the same /v1 API. Unset or blank uses the public service. A trailing slash is stripped, and a value that is not a full URL aborts startup. It is configuration rather than user input and is not run through the SSRF guard, so an address on your LAN or Docker network works without ALLOW_INTERNAL_NETWORK. https://places.liketrek.com

On Helm both go under env: in values.yaml. The chart passes TREK_PLACES_ENABLED through whenever it is set at all, so an unquoted false or --set env.TREK_PLACES_ENABLED=false reaches the container as well.


Place Search (Google Places)

A Google Maps API key gives place search Google as the keyed provider beside the TREK Places index and OpenStreetMap, and switches on Google photos, place details and the Google transit backend. What the key is used for, and the switches that limit it, are on Places and Search.

Variable Description Default
PLACES_API_KEY Google Maps API key with the Places API (New) enabled. When set, it takes priority over the key configured in Admin → Settings → API Keys and is used for every member of the instance. unset

Two ways to configure it, pick one; the env var wins if both are present:

  1. Environment variable (this page): instance-wide, ideal for Docker/Helm/Unraid where you already manage config as env. In the chart it is a credential and goes under secretEnv:, not env:.
  2. Admin → Settings → API Keys: paste it into the Google Maps API Key field. Stored encrypted at rest.

While the variable is set, the Google Maps API Key field in the admin panel is read-only and names PLACES_API_KEY instead of showing a value. Test beside it still works and checks the key from the environment. The same goes for UNSPLASH_ACCESS_KEY and AMAP_API_KEY and their fields. The value itself never reaches the browser.

If you restrict the key to HTTP referrers in Google Cloud Console, set APP_URL as well: TREK sends it as the Referer header on every Google request, and without it Google rejects them.


Place Search (Amap / 高德地图)

Google Places is unreachable from most networks inside mainland China, and OpenStreetMap's coverage of Chinese POIs (restaurants, shops, the things a trip is actually made of) is thin and rarely in Chinese. TREK can put Amap (高德地图) in the slot Google otherwise holds: the TREK Places index and OpenStreetMap still answer first, Amap answers when they have nothing, and autocomplete, place details and reverse geocoding go through it. There is an Amap basemap to match (see Map Settings).

Variable Description Default
AMAP_API_KEY Amap Web 服务 (web service) key. When set, it takes priority over any key configured in Admin → Settings → API Keys. unset
AMAP_API_SECRET The private secret (数字签名) of that key. Required exactly when the key was created with signing enabled: such a key rejects every unsigned request. Leave unset otherwise. unset
AMAP_API_BASE Send the calls somewhere other than https://restapi.amap.com: an egress proxy, a cache, a gateway that holds the credential. The replacement has to speak the same API. https://restapi.amap.com

Get a key at console.amap.com: create an application, then add a key of type Web 服务. A Web 端 (JS API) key is a different kind of credential and will be rejected with INVALID_USER_KEY. This is the single most common misconfiguration.

Two ways to configure it, pick one; the env var wins if both are present:

  1. Environment variable (this page): instance-wide, ideal for Docker/Helm where you already manage config as env. In the chart the key and the secret are credentials and go under secretEnv:, not env:; only AMAP_API_BASE is a plain env: value. The Amap (高德地图) API Key field in the admin panel is then read-only and names the variable.
  2. Admin → Settings → API Keys: paste it into the Amap (高德地图) API Key field. Stored encrypted at rest.

Setting a key is not enough on its own: Admin → Settings → API Keys → Place search provider decides which keyed provider answers. Automatic, the default, keeps Google when a Google key is configured, then takes Amap, then nobody, so adding an Amap key never silently moves an existing install off Google. Choose Amap explicitly to make it the provider.

Coordinates

Amap speaks GCJ-02, the offset datum Chinese law requires published maps to use; everything TREK stores is WGS-84. The conversion happens at the boundary, in both directions, so what lands in the database, in a GPX export or on a map is always WGS-84: a place added through Amap and opened in OpenStreetMap later is in the right spot. The place also keeps the Amap id it was found by, so it keeps opening against Amap whichever provider is selected later. You do not need to configure anything for this, but it is worth knowing if you compare raw coordinates against Amap's own website, which will differ by a few hundred metres.


Storage & Paths

Storage backends and category assignment are configured in Admin: Storage (or a seed-once storage-config.json), not through environment variables. TREK_PLACE_PHOTO_DIR below is unaffected.

Variable Description Default
TREK_PLACE_PHOTO_DIR Directory where cached Google place photos are stored. Created recursively on boot. Set this to point photo storage at a dedicated mounted volume. uploads/photos/google
FILE_UPLOAD_LIMIT_MB Maximum size (in MB) of a file uploaded to a trip, a booking or a collab note, and of a file Document Sync brings in. The upload dialogs use the same limit and refuse a larger file before the upload starts. Videos keep their own 500 MB cap, and trip covers and place images their 20 MB. The MCP tool upload_trip_file takes at most 10 MB, or this limit when it is lower. If you sit behind a reverse proxy, raise its upload limit as well. 50
BACKUP_UPLOAD_LIMIT_MB Maximum compressed size (in MB) of a restore-backup archive that may be uploaded. Raise it if your backups (which include the uploads/ directory) exceed the default. Non-positive or invalid values abort startup. 500
RESTORE_FROM_BACKUP Path to a backup ZIP to restore on the first start, before any setup. Only acts while no database exists; afterwards it is ignored with a warning. A backup that fails the restore checks stops the start. See Backups. none
BACKUP_MAX_DECOMPRESSED_MB Maximum decompressed size (in MB) of a restore-backup archive — the zip-bomb guard. Independent of BACKUP_UPLOAD_LIMIT_MB and enforced on both restore paths, so a restore that fits the upload cap can still be refused with Backup exceeds the maximum decompressed size. Raise both when restoring a very large instance. 5120 (5 GB)
TREK_DB_JOURNAL_MODE SQLite journal mode: DELETE, TRUNCATE, PERSIST, MEMORY, WAL or OFF. Set DELETE when the data directory lives on network storage — see below. Values SQLite doesn't know log a warning and fall back. WAL
TREK_DB_PRE_MIGRATE_SNAPSHOT Copy the database to pre-migrate-<schema>-<time>.db next to it before a startup applies pending migrations, so the previous image can be started on that copy again. If the copy cannot be written the startup refuses rather than migrate without one. Set false only to get past that when you cannot free space and have a backup of your own. See Updating. true
TREK_DB_PRE_MIGRATE_SNAPSHOT_KEEP How many of those copies stay in the data directory, newest first (1 to 100). 3
TREK_DB_SYNCHRONOUS SQLite synchronous level: OFF, NORMAL, FULL or EXTRA. The default follows the journal mode — NORMAL under WAL (what SQLite itself uses there), FULL otherwise, because a rollback journal at NORMAL can lose committed transactions on a power cut. NORMAL / FULL

Running the database on network storage

WAL is the right mode on a local disk and stays the default. It coordinates readers and writers through a shared-memory file (travel.db-shm) and memory mapping, and SQLite's own documentation says that combination is not safe on filesystems that don't implement those primitives properly. In practice that means Azure App Service (Linux), and SMB/NFS volumes mounted from a NAS or a PaaS host. If your data/ directory is one of those, set:

TREK_DB_JOURNAL_MODE=DELETE

The journal mode is written into the database file header, not held per connection, so it survives restarts and applies from the next boot onward. The startup log prints what is actually in effect:

[DB] journal_mode=DELETE, synchronous=FULL

The maintenance tools that open the same file — reset-admin.js and scripts/migrate-encryption.ts — read the same two variables, so run them with the same environment (docker exec into the container does this for you). Otherwise the next key rotation or admin reset would quietly switch the file back to WAL.


Advanced / Tuning

Variable Description Default
IDEMPOTENCY_TTL_SECONDS How long (in seconds) stored idempotency keys are kept before garbage collection. The offline client replays queued mutations with their X-Idempotency-Key on reconnect, so this must exceed the longest expected offline window or a replay could create a duplicate. Invalid values abort startup. 2592000 (30 days)
NOMINATIM_URL Custom HTTP(S) Nominatim base URL for every geocoding call the server makes: place search, reverse lookup, place details and the Atlas region fill. When set it replaces the public OpenStreetMap service for all of them, with no fallback, so point it at an instance whose import covers the places your users plan and that speaks the Nominatim API (/search, /reverse, /lookup). Photon, Geoapify and LocationIQ are not drop-in replacements. TREK keeps its client-side spacing of roughly one request a second whichever instance answers, so this changes where the requests go, not how fast they are sent. Unset or blank uses the default. With the TREK Places API on, suggestions while typing come from the TREK API's own OpenStreetMap layer and reach Nominatim only when the TREK API has nothing. https://nominatim.openstreetmap.org
OVERPASS_URL Custom Overpass API endpoint(s), comma-separated, for the map's category buttons, the Road trip search along the drive and the search_pois MCP tool whenever the TREK Places API has no answer or is switched off. When set it replaces the bundled public mirrors: point it at an internal or self-hosted Overpass instance when the public mirrors are unreachable from your network (e.g. firewalled/locked-down egress in a Kubernetes cluster). Entries that aren't valid http(s) URLs are ignored. If you don't run your own Overpass but the public mirrors throttle TREK, first make sure APP_URL (or ALLOWED_ORIGINS) is set: that alone gives outbound Overpass/Nominatim requests a unique User-Agent, which the public mirrors rate-limit far less. One request is not covered: the tag lookup behind an OpenStreetMap place's details (website, phone, Wikidata) still goes to overpass-api.de whatever this is set to, and where that host is unreachable those details fall back to the tags Nominatim returns. bundled public mirrors
OVERPASS_TIMEOUT_MS Per-endpoint timeout (in milliseconds) for Overpass POI requests. Endpoints race in parallel and one that hasn't answered within this window is abandoned so a faster mirror can win. The query itself gives Overpass 20 seconds of work, so a lower value hangs up on answers that were still coming; that is why the default rose from 12000 in 4.3.0. Drop any lower value you set by hand, and raise it only for a slow self-hosted Overpass instance. Invalid values abort startup. 25000
LLM_TIMEOUT_MS How long (in milliseconds) one AI-parsing call may take before it is abandoned. One ceiling for every provider, applied to the abort signal and to the underlying HTTP client alike. The default is generous so heavier parsing work fits without a code change; lower it if you use a cloud provider and would rather fail fast. Invalid values abort startup. 900000 (15 min)
LLM_MAX_OUTPUT_TOKENS The longest answer (in tokens) one AI-parsing call may ask the model for, sent to every provider. A document with many bookings needs a long answer; when the model hits the cap the import says so and names this variable. Raise it for such documents, but keep prompt plus cap inside the context your model has, or the server refuses the request. Unset keeps each provider's own cap. A whole number from 256 to 1000000, anything else aborts startup. 4096 (OpenAI-compatible), 8192 (Anthropic)

Routing is not set here. The routing engines behind the planner and the Road-Trip addon (OSRM for routes, Valhalla for avoiding toll roads, motorways and ferries) have no environment variable. An admin sets them under Admin → User Defaults as Own routing engine and Own Valhalla instance, and they take effect after a restart. OVERPASS_URL and OVERPASS_TIMEOUT_MS above also apply to the road trip's search along the route.


Demo Mode

Demo mode runs TREK as a public, self-resetting sandbox. Not intended for regular deployments.

Variable Description Default
DEMO_MODE Enable demo mode: seeds example data, resets the database hourly, exposes the demo-login endpoint, and blocks destructive mutations (password change, account deletion, uploads) for demo users. Logs a security warning at startup if combined with NODE_ENV=production. false
DEMO_ADMIN_USER Username of the seeded demo admin account. admin
DEMO_ADMIN_EMAIL Email of the seeded demo admin account. admin@trek.app
DEMO_ADMIN_PASS Initial password for the seeded demo admin (bcrypt-hashed at seed time). admin12345

The DEMO_ADMIN_* variables only take effect when DEMO_MODE=true, and only at the moment the demo data is first seeded.


Plugins

The plugin system is on by default. The runtime and the Admin → Plugins panel are available out of the box, but installed plugins still have to be activated one by one — so no third-party code runs until an admin turns a specific plugin on. Set TREK_PLUGINS_ENABLED=false to switch the whole system off. See Plugins for the full system and Plugin-Permissions for the isolation model.

Variable Description Default
TREK_PLUGINS_ENABLED Master switch for the plugin system. Enabled unless set to false (also accepts 0, off, no, case-insensitive). Turning it off is a kill switch — installed plugins stay on disk but nothing runs. enabled
TREK_PLUGINS_DIR Directory where installed plugin code is stored. Persist it as a volume if you use plugins. <data>/plugins
TREK_PLUGINS_DATA_DIR Directory for each plugin's own data (its private SQLite file). Kept separate from the code tree; persist it as a volume too. <data>/plugins-data
TREK_PLUGIN_REGISTRY_URL Override the plugin registry index the Discover tab browses. Point it at your own fork or mirror of the registry. https://raw.githubusercontent.com/liketrek/TREK-Plugins/main/dist/index.json
TREK_PLUGIN_MAX_RSS_MB Per-plugin memory ceiling in MB. A plugin process that exceeds it is stopped. 300
TREK_PLUGIN_PERMISSIONS Set to any falsy value (off, false, 0, no — any casing) to opt out of the Node.js OS-level permission sandbox for plugin child processes (not recommended). A truthy value, or leaving it unset, keeps the sandbox on; anything outside that set is rejected at boot. on
TREK_PLUGIN_ALLOW_PRIVATE_EGRESS Set to on to let a plugin's declared outbound hosts resolve to private/internal addresses (e.g. a service on your LAN). By default connections to private, loopback, link-local and metadata addresses are refused. off (private egress blocked)
TREK_PLUGINS_DEV_LINK Development only. Set to any truthy value (1, true, on, yes — any casing) to enable dev-link: registering a plugin from a local build directory and hot-reloading it against a live instance's data. Dev-linked code bypasses the install-time signature/integrity checks and (under npm run dev) runs with the OS permission jail off, so it must never be reachable in production — absent, blank or an explicitly falsy value (0, false, off, no) keeps it off. Data access is still fully gated by the capability host. off (disabled)
TREK_PLUGINS_IGNORE_TREK_RANGE Set to any truthy value (1, true, on, yes — any casing) to turn the plugin TREK-version gate into a warning. A plugin whose trek range does not admit the running TREK — or that declares no range at all — can then be installed (registry, sideload, dev-link), updated and activated, and "install latest" takes the newest published version rather than the newest compatible one. Every bypass is logged, the install response carries a trekRangeBypassed marker, and the admin panel shows a Version checks off pill, a warning dialog before/after each such install, and a persistent chip on the row. Use it only when a plugin author has not yet updated their range for your TREK: nothing guarantees the plugin works, and in rare cases a mismatched plugin can corrupt TREK data. The plugin-API version gate is not lifted. Absent, blank or falsy keeps the gate hard. off (gate enforced)
TREK_PLUGIN_AI_PER_DAY Per-plugin daily cap on shared-LLM broker calls (ai.complete / ai.extract). Bounds how much a single plugin can spend on the admin's LLM quota per UTC day, independent of its granted permissions. Counts persist across restarts within the same day; set to 0 to disable the AI broker entirely. Generous by design — only bites a runaway plugin. 200
TREK_PLUGIN_NOTIFY_PER_DAY Per-plugin daily cap on user-notification broker calls (notify.send), so one plugin can't spam a user. Same UTC-day window and restart-safe counting as TREK_PLUGIN_AI_PER_DAY; set to 0 to disable the notify broker. 100
TREK_PLUGIN_RPC_PER_SEC Sustained rate limit (calls per second) for a plugin's host RPC calls (ctx.*) once its burst allowance is spent. Prevents a tight-loop plugin from starving the single-threaded host. 20
TREK_PLUGIN_RPC_BURST Burst allowance — how many host RPC calls a plugin may fire back-to-back before the per-second limit applies. 60
TREK_PLUGIN_RPC_INFLIGHT Max concurrent host→plugin RPC dispatches allowed for a single plugin at once (concurrency cap). 16
TREK_PLUGIN_LOG_PER_SEC Sustained rate limit (log lines per second) for a plugin's log output — ctx.log.*, the child process's stdout/stderr, and unknown plugin event topics — once its burst allowance is spent. This path is not covered by the RPC limiter above, and warn/error lines land in a synchronous plugin_error_log INSERT + prune on the host thread, so this is what stops a while (true) ctx.log.error(...) loop from pinning the instance. Excess lines are dropped; when logging resumes, one warn line reports how many were dropped, so the throttling is visible to an operator. 10
TREK_PLUGIN_LOG_BURST Burst allowance — how many log lines a plugin may emit back-to-back before the per-second limit applies. 50
TREK_PLUGIN_AUDIT_MAX_ROWS Per-plugin retention cap for the capability audit log (kept in the shared data/travel.db). The newest N rows per plugin are retained and older ones pruned; the retained window stays tamper-evident. Set to 0 to disable pruning. 20000

All of these are optional — the defaults are safe. Set TREK_PLUGINS_ENABLED=false if you want to switch the plugin system off entirely.