TREK

TREK

Docker app from TREK's Repository

Overview

TREK is a self-hosted, real-time collaborative travel planner with interactive maps, budgets, bookings, packing lists, file management, and more. Plan trips together with your group — changes sync instantly across all connected users. Includes OIDC/SSO support, dark mode, PWA, and a modular addon system (Vacay, Atlas, Collab, Budget, Packing).
TREK
your trip. your plan.

A self-hosted, real-time collaborative travel planner — with maps, budgets, packing lists, a journal, and AI built in.


Demo   Docker   Sonar Quality Gate   GitHub Actions Workflow Status   Discord   Roadmap   Ko-fi   BMAC
License Latest Release Docker Pulls Stars


TREK, a tour of the app

What you get

Plan, track, share
See all features

Most of what follows is an addon an admin switches on or off. Lists, Costs, Documents, Collab, Vacay and Atlas ship on; Journey, Collections, MCP, AI Parsing and AirTrail ship off and are marked below.

🧭 Planning

  • Day plans: drag places between days and reorder inside a day, with undo. Notes and bookings drag the same way, and a map marker drops straight onto a day
  • Maps: Leaflet, Mapbox GL or MapLibre GL (OpenFreeMap, no token), with clustering, photo markers and route lines. 3D buildings and terrain are Mapbox only
  • Place search: Google Places when a key is set (photos, ratings, opening hours), otherwise OpenStreetMap with no key
  • Place enrichment: descriptions, facts, hours and photo candidates from OpenStreetMap, Wikipedia, Wikidata and Wikimedia Commons
  • POI explore: pull OpenStreetMap POIs by category for the current viewport over Overpass
  • Import: shared Google Maps and Naver Maps lists, plus GPX, KML and KMZ files
  • Export: GPX of a trip's places and tracks, and an ICS feed per trip or across all of them
  • Routes: auto-sort a day (nearest neighbour then 2-opt, locked stops and hotel anchors stay put), driving, walking or cycling profiles over OSRM, then open it in Google Maps or CoMaps
  • Public transport: door-to-door itineraries over Transitous
  • Weather: 16-day forecast from Open-Meteo, no key. Dates outside that window read the archive for the same date instead
  • Day notes: markdown body with an icon and a colour, reordered by drag and drop or moved to another day
  • Trip dates: move a trip and the days re-date themselves, either dragging the bookings along or re-anchoring them. Trips also copy and archive

🧳 Bookings and money

  • Reservations: 16 booking types with status, confirmation code, travellers and attached files
  • Flights and trains: several legs with stopovers, per-leg times and endpoint timezones, against 4,045 bundled airports so local times resolve without a key
  • Accommodation: a stay spans a range of days with a check-in window, and shows on every night it covers
  • Booking import: EML, PDF, PKPass, HTML and TXT confirmations through KItinerary. Needs the kitinerary-extractor binary, which ships in the Docker image
  • AirTrail (off by default): link a self-hosted AirTrail instance to import flights as reservations and keep them in sync
  • Costs: split expenses in integer cents with equal or custom shares, several payers per expense, settle-up suggestions, a settlement log, and CSV export
  • Currencies: a currency per expense with the rate frozen at entry. Rates come from Frankfurter, no key
  • Packing lists: categories, admin-managed templates, assignees, three visibility tiers, and a packed-of-total line. Bags with weight roll-up are a separate admin switch, off by default
  • To-dos: assignee, due date, priority, and a reminder before one falls due
  • Files: attach to a trip, place, day entry or reservation. 50 MB each, 500 MB for video, with trash and restore
  • PDF export: cover page, place photos, day notes, bookings and costs, with an optional page break per day

👥 Collaboration

  • Real-time sync (WebSocket): edits land live for everyone who has that trip open
  • Members: add by email or username, hand ownership to someone else, or add guests who have no login at all
  • Permissions: an admin maps each of 16 trip actions to admin, trip owner, trip member or everybody
  • Invite links: one reusable link per trip with an optional expiry. Admins can also issue signup invites with a use limit that drop the new account straight into a trip
  • Public share: a read-only trip page anyone can open without an account
  • Collab: group chat with replies, reactions and link previews, shared notes with attachments, polls, and a What's Next list of upcoming activities. Each of the four switches on its own

📔 Journal, Atlas and Vacay

  • Journey (off by default): dated entries with story, mood, weather and tags, photos and video from uploads or a linked Immich or Synology Photos library, map views, co-authors, and a public share link
  • Atlas: mark countries and sub-national regions visited on geoBoundaries outlines, plus a bucket list, travel stats and a consecutive-years-travelled count
  • Vacay: leave calendar with half days, public holidays from date.nager.at, school holiday overlays for 16 European countries, carry-over between years, and joint or read-only sharing of a plan
  • Collections (off by default): a place library outside any trip, with labels, ratings, a custom image per place, copy-into-a-trip, and sharing by invite

🧩 Plugins

  • Third-party plugins: install from the TREK registry or sideload a zip, switch on per instance, with their own pages under /plugins/<id>
  • Sandboxed: one child process per plugin, 63 grantable permissions, an admin-edited outbound host allowlist, memory and RPC caps, and daily caps on AI and notification calls
  • Trust: registry downloads are pinned by sha256 and checked against the author's minisign key. A sideloaded zip is marked unverified, and TREK_PLUGINS_ENABLED=false turns the whole system off
  • Extension points: map markers and layers, place details, day schedules, PDF sections, Atlas layers, journal rows, trip warnings, calendar sources, route profiles and notification channels. Plugin pages run in an opaque-origin iframe
  • SDK: trek-plugin-sdk on npm, with a manifest validator, a mock host, and a dev-link mode that runs a local build against real data

🤖 AI and MCP

  • MCP server (off by default): OAuth 2.1 with mandatory PKCE and dynamic client registration. 199 tools, 30 resources, 4 prompts
  • Scopes: 29 scopes in 14 groups, each one tickable on the consent screen. Tokens are bound to the /mcp resource, and every tool call lands in the audit log
  • Reach: create trips and days, edit places, packing lists, to-dos, costs, reservations, collections and journeys, mark countries visited, all inside the scopes the token holds
  • Prompts: trip-summary, plus packing-list and budget-overview when those addons are on
  • Addon-aware: seven addon gates decide which tools and resources a session sees. Flipping one drops live sessions so the surface re-registers
  • Booking extraction (off by default): read a confirmation with a local Ollama server, any OpenAI-compatible endpoint, or Anthropic, configured instance-wide or per user

📱 Mobile and offline

  • Installable: iOS and Android straight from the browser, no App Store. Runs standalone without browser chrome, with the status bar tinted per theme
  • Phone layouts: a separate shell below 768px with its own tokens, a bottom bar and safe-area insets. Which items sit in that bar is up to the user
  • Offline reads: the app shell and every route chunk are precached, and trips, places and file blobs live in a per-user IndexedDB store, so a trip opens with no network
  • Offline writes: mutations queue and replay with an X-Idempotency-Key, so a reconnect cannot double-apply. A stale edit is parked for a keep-mine or keep-theirs choice
  • Offline maps: pre-download a trip's raster tiles, and clear them again
  • API responses are never cached by the service worker. They vary per session, so they always come from the network

⚙️ Admin, accounts and security

  • Login methods: password login, password registration, OIDC login, OIDC registration and passkey login each switch on and off on their own. OIDC_ONLY leaves SSO as the only way in
  • SSO (OIDC): one provider by discovery, with PKCE and id_token verification (Authentik, Keycloak, Google and the like)
  • 2FA: TOTP with ten single-use backup codes, and an admin can require it instance-wide
  • Passkeys: WebAuthn login by fingerprint, face, PIN or security key, off until an admin enables it. A passkey also satisfies the 2FA requirement
  • Hardening: per-IP limits on login, password reset and 2FA attempts, a password policy, secrets encrypted at rest and masked on read, and an SSRF guard on every URL you configure
  • Admin panel: users and invites, the permission matrix, packing templates, categories, addons, plugins, API keys, MCP tokens and OAuth sessions, backups, storage, audit log, and GitHub releases
  • Backups: manual or scheduled hourly, daily, weekly or monthly, with retention in days. The zip carries the at-rest key, so a restore can decrypt its own secrets
  • Storage: pluggable storage backends per content category — keep everything on local disk, or add S3-compatible backends and replicate any category to them, configured entirely from the admin panel
  • Notifications: a per-user matrix of events against in-app, email (SMTP), webhook and ntfy, plus any channel a plugin registers
  • Appearance: light, dark or follow the OS, seven colour schemes plus a custom accent, transparency, compact density, reduce motion, and text size per tier
  • 23 languages: en, de, es, fr, it, nl, hu, ru, zh, zh-TW, pl, cs, ar (right to left), br, id, tr, ja, ko, uk, gr, sv, vi, ca
  • In-app help: the wiki ships inside the image and is served from disk at /help, so the docs match the version you are running

Atlas · dashboard · trip planner Mobile PWA · dashboard, day plan, map, costs Collections · journey journal · costs

AI usage

We use LLM-assisted coding tools across parts of this codebase. Nothing ships that a maintainer has not read and understood: every change goes through a pull request, is reviewed and tested, and has a human who can answer for it. "The AI wrote that" is not an answer any of us would accept from ourselves.

See How we use AI in TREK for the details.


Get started in 30 seconds

ENCRYPTION_KEY=$(openssl rand -hex 32) docker run -d -p 3000:3000 \
  -e ENCRYPTION_KEY=$ENCRYPTION_KEY \
  -v ./data:/app/data -v ./uploads:/app/uploads mauriceboe/trek

Open http://localhost:3000. On first boot TREK seeds an admin account — if you set ADMIN_EMAIL/ADMIN_PASSWORD those are used, otherwise the credentials are printed to the container log (docker logs trek).


Tech stack

Node.js NestJS SQLite React Vite TypeScript Tailwind Leaflet Docker

Real-time sync via WebSocket (ws). Backend on NestJS 11. State with Zustand. Auth via JWT + OAuth 2.1 + OIDC + Passkeys (WebAuthn) + TOTP MFA. Weather via Open-Meteo (no key required). Maps with Leaflet and Mapbox GL.


Docker Compose (production)

The repository ships a ready-to-use docker-compose.yml with secure defaults and every option documented inline. Download it, then:

docker compose up -d

See Install with Docker Compose for the full walkthrough.


Helm (Kubernetes)

helm repo add trek https://chart.liketrek.com
helm repo update
helm install trek trek/trek

See charts/README.md for values.


Install as App (PWA)

TREK works as a Progressive Web App — no App Store needed.

  1. Open TREK in the browser (HTTPS required)
  2. iOS: Share ▸ Add to Home Screen
  3. Android: Menu ▸ Install app (or Add to Home Screen)

TREK then launches fullscreen with its own icon, just like a native app.


Updating

See Updating — Docker Compose, Docker run, Helm, Portainer, Unraid and Proxmox, plus the encryption-key note.


Reverse Proxy

For production, put TREK behind a TLS-terminating reverse proxy. TREK uses WebSockets for real-time sync, so the proxy must support WebSocket upgrades on /ws.

If you use the MCP addon, the proxy must also pass the Mcp-Session-Id header through in both directions on /mcp — Nginx and Caddy do this by default, but a proxy that strips it makes every tool call open a new session instead of reusing one. See the Reverse Proxy wiki page for details.

Nginx
server {
    listen 80;
    server_name trek.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name trek.yourdomain.com;

    ssl_certificate     /etc/ssl/fullchain.pem;
    ssl_certificate_key /etc/ssl/privkey.pem;

    # 500 MB covers backup-restore uploads (capped at 500 MB server-side).
    client_max_body_size 500m;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /ws {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 86400;
    }

    # Only needed if you use the MCP addon. Responses are Server-Sent Events,
    # so buffering must be off or tool results arrive late.
    location /mcp {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
        proxy_read_timeout 3600s;
    }
}
Caddy
trek.yourdomain.com {
    reverse_proxy localhost:3000
}

Caddy handles TLS and WebSockets automatically.


Environment variables

Every variable, its default and what it does: see Environment Variables.

Star History

Star History Chart

Data sources

The Atlas map's country and sub-national (province/county) boundaries come from geoBoundaries (Runfola et al., 2020), licensed CC BY 4.0. See NOTICE.md for full third-party attributions.


License

TREK is AGPL v3. Self-host freely for personal or internal company use. If you modify and offer TREK as a network service to third parties, your modifications must be open-sourced under the same licence.

Install TREK on Unraid in a few clicks.

Find TREK in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for TREK Review the template variables and paths Click Install

Download Statistics

968,905
Total Downloads
346,572
This Month
346,572
Avg / Month

Total Downloads Over Time

Loading chart...

Related apps

Details

Repository
mauriceboe/trek
Last Updated2026-09-03
First Seen2026-06-24

Runtime arguments

Web UI
http://[IP]:[PORT:3000]
Network
bridge
Privileged
false

Template configuration

Web UI PortPorttcp

Port for the web interface

Target
3000
Default
3000
Value
3000
DataPathrw

Database and app data

Target
/app/data
Default
/mnt/user/appdata/trek/data
Value
/mnt/user/appdata/trek/data
UploadsPathrw

Uploaded files (photos, documents)

Target
/app/uploads
Default
/mnt/user/appdata/trek/uploads
Value
/mnt/user/appdata/trek/uploads
ENCRYPTION_KEYVariable

At-rest encryption key for stored secrets (API keys, MFA, SMTP, OIDC). Recommended: generate with: openssl rand -hex 32. If unset, falls back to data/.jwt_secret (existing installs) or auto-generates a key (fresh installs).

PORTVariable

Internal server port (must match the container port mapping above).

Default
3000
Value
3000
NODE_ENVVariable

Node environment (production / development).

Default
production
Value
production
TZVariable

Timezone for logs, reminders and scheduled tasks (e.g. Europe/Berlin).

Default
UTC
Value
UTC
LOG_LEVELVariable

Log verbosity: info = concise user actions, debug = verbose admin-level details.

Default
info
Value
info
TREK_WIKI_DIRVariable

Where the in-app Help pages (/help) read their content from. Leave empty: the container ships the wiki at /app/wiki and finds it automatically. Only set this to serve your own docs from a mounted path. If the path does not exist, Help falls back to fetching the public GitHub wiki (needs outbound network, and tracks the latest release rather than your version).

DEFAULT_LANGUAGEVariable

Default language shown on the login page for users with no saved preference. Browser/OS language is auto-detected first; this is the fallback when no match is found. Supported: de, en, es, fr, hu, nl, br, cs, pl, ru, zh, zh-TW, it, ar.

Default
en
Value
en
ALLOWED_ORIGINSVariable

Comma-separated origins allowed for CORS and used as base URL in email notification links (e.g. https://trek.example.com).

APP_URLVariable

Public base URL of this instance (e.g. https://trek.example.com). Required when OIDC is enabled — must match the redirect URI registered with your IdP. Also used as base URL for email notification links.

FORCE_HTTPSVariable

Optional. When true: HTTPS redirect, HSTS header, CSP upgrade-insecure-requests, and secure cookies. Only useful behind a TLS-terminating proxy. Requires TRUST_PROXY.

Default
false
Value
false
HSTS_INCLUDE_SUBDOMAINSVariable

When true: adds includeSubDomains 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.

Default
false
Value
false
COOKIE_SECUREVariable

Auto-derived (true in production or when FORCE_HTTPS=true). Set to false to force session cookies over plain HTTP. Not recommended for production.

Default
true
Value
true
TRUST_PROXYVariable

Trusted proxy hops for X-Forwarded-For/X-Forwarded-Proto. Defaults to 1 in production; off in development unless set. Required for FORCE_HTTPS.

Default
1
Value
1
ALLOW_INTERNAL_NETWORKVariable

Allow outbound requests to private/RFC-1918 IP addresses. Set to true if Immich or other integrated services are hosted on your local network.

Default
false
Value
false
SESSION_DURATIONVariable

How long a login session stays valid when 'Remember me' is unchecked (the default): trek_session JWT exp + a browser-session cookie cleared when the browser closes. Accepts 1h, 12h, 7d, 30d, 90d. Defaults to 24h.

Default
24h
Value
24h
SESSION_DURATION_REMEMBERVariable

Session length when 'Remember me' is ticked at login: a longer-lived JWT + persistent cookie that survives browser restarts. Same format as SESSION_DURATION. Defaults to 30d.

Default
30d
Value
30d
ADMIN_EMAILVariable

Email for the first admin account created on initial boot. Has no effect once any user exists.

Default
admin@trek.local
Value
admin@trek.local
ADMIN_PASSWORDVariable

Password for the first admin account created on initial boot. If omitted a random password is generated and printed to the server log. Has no effect once any user exists.

OIDC_ISSUERVariable

OpenID Connect provider URL (e.g. https://auth.example.com).

OIDC_CLIENT_IDVariable

OIDC client ID registered with your identity provider.

OIDC_CLIENT_SECRETVariable

OIDC client secret registered with your identity provider.

OIDC_DISPLAY_NAMEVariable

Label shown on the SSO login button.

Default
SSO
Value
SSO
OIDC_ONLYVariable

Set to true to force SSO-only mode. Disables password login and password registration — overrides the granular toggles in Admin &gt; Settings and cannot be changed at runtime. First SSO login becomes admin.

Default
false
Value
false
OIDC_ADMIN_CLAIMVariable

OIDC claim used to identify admin users (e.g. groups).

OIDC_ADMIN_VALUEVariable

Value of the OIDC claim that grants admin role (e.g. app-trek-admins).

OIDC_SCOPEVariable

Space-separated OIDC scopes to request. Fully overrides the default — always include openid email profile plus any extra scopes you need (e.g. add groups when using OIDC_ADMIN_CLAIM).

Default
openid email profile
Value
openid email profile
OIDC_DISCOVERY_URLVariable

Override the auto-constructed OIDC discovery endpoint. Useful for providers with a non-standard path (e.g. Authentik).

DEMO_MODEVariable

Enable demo mode (resets all data hourly). Not intended for regular use.

Default
false
Value
false
MCP_RATE_LIMITVariable

Max MCP API requests per user per minute.

Default
300
Value
300
MCP_MAX_SESSION_PER_USERVariable

Max concurrent MCP sessions per user.

Default
20
Value
20
UNSPLASH_ACCESS_KEYVariable

Optional Unsplash Access Key for trip-cover and place-image search. Without one, TREK uses Unsplash's unauthenticated endpoint, which some datacenter/VPS IPs are blocked from. Get a free key at unsplash.com/developers. Can also be set per-admin in Admin &gt; Settings; this env var overrides that.