roel-de-vries pitched seventeen changes to Journey. Four were already built or rest on a misunderstanding, one cannot work as described, and three would take away more than they give; the rest are here, plus two things the list did not ask for. **The entry you write on the move now has a name.** Quick capture hid the title field outright and offered a note instead, so every entry caught on a phone arrived nameless and a day's timeline read as a column of identical placeholders. One line of input; it is the single change in this branch that users will feel most. **The map stops yanking.** Stepping through the timeline called flyTo with a zoom floor of 12, so reading a journey from a country view snapped to street level on the first scroll and stayed there — every marker filling the screen, and no sense of where anything was. Both renderers pan now and leave the frame the reader chose. The initial fitBounds still decides how close a journey opens. **The phone timeline is photos.** A 64px thumbnail beside three lines of text spent the card on a story nobody reads at that size and truncated the title after a few characters. The picture is the card now, with the name and place on it, the country's flag and the date in the corners, and the day carried as a colour rather than as a number nobody was reading. Above the carousel, a segment per day: it says how long the journey is, how far in you are, and takes you to day nine without swiping past days three to eight. Everything else, briefly: - A plus in each day header, starting a draft on that day instead of on today. - A journey opens on today when today is part of it, and at the beginning when it is not — a journey that ended last spring is something you read. - Suggestions can be waved away one at a time. The row survives so the trip sync does not offer the same place again; journey settings brings them all back. - Mood, weather and the pros/cons list can be switched off per journey. Nothing written is erased — the form stops asking, the values stay. - The weather fills itself in at the desk the way it already did on the phone. The date picks the source, so writing up last Tuesday gets last Tuesday's. - Enter opens the next pro or con, below the one you are in. - The verdict moved behind the story's fold, and the fold itself came in from nine lines to four. - The external picker lost the count it was printing twice and some padding it could not afford inside the editor. - The phone can reorder entries within a day at last; the desktop arrows finally have translated labels. - Marker labels are off where a carousel already names the entry, and on where they are the only name a marker has. - Search over a journey's titles, stories, places and tags, accent-blind. - An entry knows which country it happened in, resolved once from its coordinates against the bundled boundaries. Not built, and why: the photos-for-markers idea needs clustering first or thirty markers become soup, and the active entry's photo is already on screen in the card below. Removing mood, weather and the verdict outright would take them from everyone who uses them, so they became switches. `capture` on the file picker takes the photo library away on a phone, which is why the camera is a second input; it is hidden on desktop now instead. The suggestion strip in the new-entry flow would be a third surface over the same data — tapping a suggestion already opens the editor filled in. The one gap the list missed and this branch does not close: Journey is the only domain with no offline path. That belongs in its own change.
A self-hosted, real-time collaborative travel planner — with maps, budgets, packing lists, a journal, and AI built in.
What you get
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
|
🧳 Bookings and money
|
👥 Collaboration
📔 Journal, Atlas and Vacay
|
🧩 Plugins
🤖 AI and MCP
|
📱 Mobile and offline
|
⚙️ Admin, accounts and security
|
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
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.
- Open TREK in the browser (HTTPS required)
- iOS: Share ▸ Add to Home Screen
- 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
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.



