Self-hosting
Configuration
Every TM_* environment variable the API reads, with defaults and what it controls.
All server configuration is environment variables with the TM_ prefix. Set them in the shell,
in Compose environment:, or in api/.env for local development. Restart the API after
changing any of them.
| Variable | Default | Purpose |
|---|
TM_HTTP_PORT | 8080 | API listen port. PORT (Railway/Heroku style) is honored only when TM_HTTP_PORT is unset |
TM_DATABASE_URL | (unset → SQLite) | Unified DB URL — sqlite:///data/tradermemos.db or postgres://user:pass@host:5432/db?sslmode=require. Setting it to an empty string is a startup error |
TM_DB_PATH | data/tradermemos.db | Legacy SQLite path, used only when TM_DATABASE_URL is unset |
TM_ATTACH_DIR | (derived) | Attachment/media disk root. Defaults to <db dir>/attachments for SQLite, data/attachments for Postgres — set it explicitly for Postgres |
TM_DEFAULT_CURRENCY | USD | Default account currency |
TM_LOG_LEVEL | info | Log level (debug, info, warn, error) |
| Variable | Default | Purpose |
|---|
TM_JWT_SECRET | dev-insecure-change-me | JWT signing key, minimum 32 characters. The server refuses to start on a known-weak value unless TM_ALLOW_INSECURE_JWT=true. Generate with openssl rand -hex 32 |
TM_ALLOW_INSECURE_JWT | false (true in the bundled Compose for first-run convenience) | Permits a weak/short JWT secret. Never enable on a public host |
TM_ALLOW_REGISTRATION | false | Opens POST /auth/register after the first-user setup. Off by default — only the owner exists unless you opt in |
TM_CORS_ORIGINS | (empty — CORS disabled) | Comma-separated allowlist for split CDN + API deployments. Supports wildcard hosts like https://*.vercel.app. Leave empty for same-origin setups |
| Variable | Default | Purpose |
|---|
TM_ATTACH_MAX_BYTES | 10485760 (10 MiB) | Per-file cap for trade screenshots and note media |
TM_IMPORT_MAX_BYTES | 10485760 (10 MiB) | Cap for CSV/JSON import uploads |
TM_OCR_MAX_BYTES | 10485760 (10 MiB) | Cap for screenshot-scan images (falls back to the attachment cap if ≤ 0) |
The bundled nginx allows client_max_body_size 20m. If you raise any cap above 20 MiB, raise
the nginx value (and any upstream reverse proxy) too.
These are defaults only: once you save AI settings in Settings → AI, the database values
win. Keys are stored server-side and never sent to the browser. See
AI features.
| Variable | Default | Purpose |
|---|
TM_OCR_ENABLED | false | Enable screenshot fill extraction |
TM_OCR_VISION_BASE_URL | https://api.openai.com/v1 | OpenAI-compatible endpoint for vision |
TM_OCR_VISION_API_KEY | (empty) | Vision API key |
TM_OCR_VISION_MODEL | gpt-4o-mini | Vision model |
TM_OCR_VISION_TIMEOUT_SEC | 90 | Vision request timeout in seconds |
TM_COACH_ENABLED | false | Enable the LLM trade coach |
TM_COACH_BASE_URL | https://api.openai.com/v1 | OpenAI-compatible endpoint for the coach |
TM_COACH_API_KEY | (empty) | Coach API key |
TM_COACH_MODEL | gpt-4o-mini | Coach model |
| Variable | Default | Purpose |
|---|
TM_MARKET_DATA_ENABLED | true | Enable the market-data service |
TM_MARKET_DATA_PROVIDER | yahoo | Provider name |
TM_MARKET_DATA_API_KEY | (empty) | Provider API key, if the provider needs one |
TM_ECON_CALENDAR_ENABLED | true | Enable the economic-events calendar feed |
TM_ECON_CALENDAR_FEED_URL | (built-in) | Override the events feed URL |
TM_ECON_CALENDAR_REFRESH_MIN | 60 | Minutes between feed refreshes |
| Variable | Default | Purpose |
|---|
TM_SHARE_LINKS_ENABLED | false | Turns on public share pages. Off by default — a share link makes part of the journal reachable without auth |
TM_PUBLIC_WEB_URL | (empty) | Public web-app origin used when building share-link URLs. Leave empty when the API origin also serves the web app (the bundled nginx deployment) |
TM_JOBS_ENABLED | true | Global background-job switch. Disable on extra replicas so scheduled work runs on one instance only |
TM_JOB_EXCURSION_INTERVAL_MIN | 360 | Minutes between MAE/MFE backfill passes (0 disables) |
TM_JOB_EXCURSION_LIMIT | 10 | Trades attempted per backfill pass (0 disables) |
TM_JOB_FLEX_SYNC_INTERVAL_MIN | 360 | Minutes between IBKR Flex auto-sync runs (0 disables) |
TM_JOB_ALERTS_INTERVAL_MIN | 60 | Minutes between alert-rule evaluations (0 disables) |
TM_ALERTS_ALLOW_PRIVATE_WEBHOOKS | false | Allow alert webhooks to target private/internal addresses (SSRF guard — leave off unless your webhook receiver is on your LAN) |
The SPA is static — these are baked in at build time, not read by the server:
| Variable | Purpose |
|---|
VITE_API | Fixed API base (e.g. https://api.example.com/api/v1) compiled into the build. Leave empty to use relative /api/v1, or let users set the Server field at login |
Read by docker compose from the root .env, never passed into containers:
| Variable | Default | Purpose |
|---|
DOCKERHUB_USERNAME | sinhong2011 | Docker Hub namespace for tradermemos-api / tradermemos-web images |
TM_IMAGE_TAG | latest | Image tag — pin a release like 0.1.6 in production |