TREK
Overview
TREK is an open-source, self-hosted planner for real trips. You build a day-by-day itinerary, save places onto an interactive map, record accommodation and transport, keep tickets and confirmations together as documents, and track what the trip costs, including splitting expenses across everyone travelling.
Everyone you invite to a trip works on the same plan at the same time. Notes, polls and chat update live over a WebSocket, so a group can settle an itinerary without emailing spreadsheets around.
This SecureAnyCloud image builds TREK 3.4.1 from the official upstream source release (git tag v3.4.1, commit a0994658) using upstream's own workspace build, then wraps it as an appliance. A single Node.js 24 process serves both the API and the React client and is bound to loopback only. nginx on port 80 is the only public listener, with the WebSocket upgrade explicitly configured. TREK's real-time features fail silently behind a proxy that does not pass the Upgrade and Connection headers.
Two security decisions differ from a stock install:
- Public sign-up ships closed. Upstream enables it by default: registration is refused only when the
password_registrationsetting is exactlyfalse, and an absent setting means enabled. On a stock instance, anyone who reaches the address can create an account. This image closes sign-up at first boot. Invite links still work, and they are the supported way to add people. You can re-open public sign-up from Admin > Settings at any time. - Every secret is generated per virtual machine at first boot and never baked into the image. These are the at-rest encryption key, the session signing secret, and a one-time administrator password written to a root-only credentials file. A bootstrap marker holds the application and nginx closed until those exist, so a fresh instance is never reachable with an empty user table.
TREK is licensed under the AGPL-3.0, so the exact upstream source this image was built from ships with it, checksummed, together with the license and a written offer.
What you get:
- TREK 3.4.1 built from the official
v3.4.1source release, pinned by commit and verified to declare version 3.4.1 - Node.js 24 (matching upstream's own build and runtime), with SQLite via
better-sqlite3in WAL mode - The application bound to
127.0.0.1:3000, with nginx on:80as the sole public listener - Explicit WebSocket proxying on
/ws(UpgradeandConnectionheaders, 24 hour read timeout), so live collaboration works out of the box - A 500 MB request body limit in nginx, so backup restore uploads are not rejected
- A per-VM at-rest encryption key, session signing secret and administrator password, generated at first boot
- Public sign-up closed by default, with invite links and OIDC single sign-on still available
- A pre-start guard that refuses to run the application on an empty, dummy or known-default encryption key
- Booking auto-import enabled (the kitinerary extractor reads PDF and calendar confirmations)
- SMTP, OIDC and Unsplash credentials shipped empty for you to supply
- The complete AGPL-3.0 corresponding source at
/opt/trek/SOURCE/, checksummed, with the license and a written offer - Security updates through unattended-upgrades
- 24/7 SecureAnyCloud support
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.
Standard_B2s (2 vCPU / 4 GiB RAM) is the recommended size and is comfortable for a household or small group.
Configure the network security group (NSG) to allow inbound 22/tcp from your management network and 80/tcp for the web interface. Do not open 3000/tcp. Nothing listens on it externally, by design.
TREK renders map tiles, searches for places and fetches trip cover images from public third-party services. The VM therefore needs outbound HTTPS (443) for the map and place search to work. No API key is required for the default map tiles.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for TREK by SecureAnyCloud, and select Create.
On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80).
Select Review + create, then Create.
Step 2 - Deploy from the Azure CLI
Accept the Marketplace terms, then create the VM and open port 80:
az vm image terms accept --urn sac1647283583153:trek-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name trek \
--image sac1647283583153:trek-ubuntu-24-04:default:latest \
--size Standard_B2s \
--storage-sku StandardSSD_LRS \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name trek --port 80 --priority 1010Restrict 22/tcp to your own management range instead of leaving it open to the internet:
az network nsg rule create --resource-group <your-rg> --nsg-name trekNSG \
--name allow-ssh-mgmt --priority 1000 --access Allow --protocol Tcp \
--destination-port-ranges 22 --source-address-prefixes <your-mgmt-cidr>Step 3 - Confirm first boot completed
At first boot, a oneshot service generates this VM's own secrets, creates the administrator account and closes public sign-up. Only then does it write the marker that allows the application and nginx to start.
Wait about a minute after the VM reports running, then check the services:
sudo systemctl is-active trek.service nginx.service trek-firstboot.serviceAll three report active. The bootstrap service is a oneshot that stays active (exited) after it succeeds, so it reads active rather than inactive.
Confirm what is listening. The application is on loopback only, and nginx is the public listener:
sudo ss -tlnp | grep -E ':(80|3000)\b' | awk '{print $1, $4}'The bootstrap service writes a full account of what it did to the journal:
sudo journalctl -u trek-firstboot.service --no-pager | grep -oE 'trek-firstboot: .*' | tail -12Step 4 - Retrieve the administrator password
The per-VM credentials are written to a root-only file. No password is ever printed to the journal. Read the file:
sudo cat /root/trek-credentials.txtIt contains the instance URL, the administrator e-mail and a one-time password.
The file is 0600 root:root. The database and both secret files are 0600, owned by the unprivileged trek service account. Verify the permissions:
sudo stat -c '%n %a %U:%G' /root/trek-credentials.txt /etc/trek/trek.env \
/opt/trek/server/data/travel.db /opt/trek/server/data/.jwt_secretTREK forces a password change at first sign-in. The password in the file is deliberately a one-time value. Sign in with it and choose your own password immediately.
Step 5 - Check that the application is healthy
An unauthenticated health endpoint and the public app-config endpoint confirm the version and the security posture:
curl -s http://127.0.0.1/api/health; echocurl -s http://127.0.0.1/api/auth/app-config \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(json.dumps({k:d.get(k) for k in ('version','has_users','password_registration','demo_mode')}, indent=2))"The output shows version as 3.4.1, has_users as true (the administrator exists), password_registration as false (public sign-up is closed) and demo_mode as false.
Verify that a passer-by cannot register:
curl -s -o /dev/null -w 'POST /api/auth/register -> HTTP %{http_code}\n' \
-X POST http://127.0.0.1/api/auth/register -H 'Content-Type: application/json' \
--data '{"username":"passerby","email":"passer@by.invalid","password":"Passerby-Pw-2026"}' 2>/dev/nullThe request returns 403 because password registration is disabled.
Sign-in and registration share a rate limit of 10 attempts per 15 minutes per client address. If you see 429 instead of 403, wait for the window to clear. A 429 is a rate limit, not a refusal.
Step 6 - Sign in
Browse to http://<TREK_URL>/. The address is recorded as trek.url in the credentials file. Sign in with the e-mail and one-time password from Step 4.
TREK requires you to set a new password immediately. On first sign-in, you also see a one-time thank-you note from TREK's author with links to support the project. This is genuine upstream behaviour. Dismiss it and it does not return.
You land on My Trips, which shows a hero card for your next trip, an Atlas of countries visited, and a New Trip button.
Step 7 - Build a day-by-day itinerary
Select New Trip and give the trip a title and start and end dates. TREK generates one day per date.
Open the trip and use Add Place/Activity to save places. Each place can carry an address, a time, a duration, a cost and notes. Drag a saved place onto a day to schedule it. Each day rolls up its own subtotal, and the trip shows a running total.
The tabs across the top cover the rest of the trip: Transports for flights and trains, Book for reservations, Lists for packing and to-dos, Costs for the budget, Files for documents, and Collab for shared notes, polls and chat.
The map, place search and cover-image search call public third-party services, so they need outbound HTTPS from the VM. The default tiles need no API key. To point the map at your own tile server, use Settings > Map. To add an Unsplash key for cover images, use Admin > Settings.
Step 8 - Track and split trip costs
Open Costs to record expenses against the trip. TREK groups them by day and category, and once you record who paid, it settles up who owes whom.
Step 9 - Add the people you are travelling with
Public sign-up is closed on this image, so you add collaborators deliberately instead of letting anyone register:
- Invite links. Admin > Users and Invites creates a link with a use count and an expiry. Invited people register through that link even while public sign-up is closed. This is the recommended route.
- Guests. A trip can include accountless participants, who never sign in. You assign items to them and split costs with them.
- Re-open public sign-up. If you want open registration, for example on a private network, turn it back on in Admin > Settings.
Confirm that the invite endpoint is live. An unknown token is correctly rejected:
curl -s -o /dev/null -w 'unknown invite token -> HTTP %{http_code}\n' \
http://127.0.0.1/api/auth/invite/not-a-real-token 2>/dev/nullStep 10 - Verify real-time collaboration over WebSocket
TREK's shared notes, polls, chat and live presence run over a WebSocket at /ws. nginx is configured to upgrade that path. Without the Upgrade and Connection headers, the live features fail silently while every ordinary HTTP request still returns 200, so it is worth knowing how to check.
When no session token is supplied, the handshake completes and TREK then closes the socket with application code 4001. That is the expected answer for an unauthenticated probe:
cd /opt/trek && node -e ' const WebSocket = require("ws"); const ws = new WebSocket("ws://127.0.0.1/ws"); let upgraded = false; const t = setTimeout(() => { console.log("timed out, upgraded=" + upgraded); process.exit(0); }, 10000); ws.on("upgrade", () => { upgraded = true; }); ws.on("close", (c) => { clearTimeout(t); console.log("HTTP upgrade ok=" + upgraded + ", close code=" + c); process.exit(0); }); ws.on("error", () => { clearTimeout(t); console.log("upgrade failed, upgraded=" + upgraded); process.exit(0); });'The output HTTP upgrade ok=true, close code=4001 means the proxy and the real-time layer are both working.
Step 11 - Import bookings from confirmations
The image ships the kitinerary extractor, so TREK can read a PDF or calendar booking confirmation and turn it into a reservation. In a trip, open Book and upload the confirmation.
Confirm that the extractor is present:
grep -E '^KITINERARY_EXTRACTOR_PATH=' /etc/trek/trek.envStep 12 - Terminate TLS for production
The image serves plain HTTP on port 80, because no certificate can be issued for an address that does not exist until you create the VM. Put TLS in front of TREK before you use it for real work. TREK marks its session cookie Secure when it knows the connection is HTTPS. Installable progressive web app support and OIDC single sign-on also require HTTPS.
So that browser sign-in works out of the box over plain HTTP, the image ships COOKIE_SECURE=false. Once you have HTTPS in front, remove that line so the session cookie is marked Secure again:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain> -m <your-email> --agree-tos --no-eff-email
# Then harden the cookie and tell TREK its public URL:
sudo sed -i '/^COOKIE_SECURE=/d' /etc/trek/trek.env
sudo sed -i 's@^APP_URL=.*@APP_URL=https://<your-domain>@' /etc/trek/trek.env
sudo sed -i 's@^ALLOWED_ORIGINS=.*@ALLOWED_ORIGINS=https://<your-domain>@' /etc/trek/trek.env echo 'FORCE_HTTPS=true' | sudo tee -a /etc/trek/trek.env
sudo systemctl restart trek.service nginx.serviceTRUST_PROXY=1 is already set. It lets TREK see the original protocol behind the proxy.
Step 13 - Configure e-mail notifications
SMTP ships empty, and trip reminders and notification e-mails need it. Add your SMTP settings and restart the service:
sudo tee -a /etc/trek/trek.env >/dev/null <<'ENVEOF' SMTP_HOST=<your-domain> SMTP_PORT=587 SMTP_USER=<your-email> SMTP_PASS=<your-token> SMTP_FROM=<your-email> ENVEOF
sudo systemctl restart trek.serviceStep 14 - Back up your data
Everything TREK stores lives in two directories plus two secret files. Back up the encryption key with the data. Without it, stored secrets such as API keys and one-time-password seeds cannot be decrypted.
sudo ls -1 /opt/trek/server/data /opt/trek/server/uploadsTREK also has its own backup feature in Admin > Backups, which produces a restorable archive of the database and uploads. nginx is configured with a 500 MB body limit, so restoring a large archive through the browser works.
Step 15 - The AGPL-3.0 source on this image
TREK is licensed under the GNU Affero General Public License v3.0. Because people interact with it over a network, section 13 requires that the corresponding source be available to them. The complete, unmodified upstream source that this image was built from ships on the image, with its checksum and a written offer. Verify it:
ls -lh /opt/trek/SOURCE/ && cd /opt/trek/SOURCE && sha256sum -c trek-3.4.1.tar.gz.sha256sudo head -20 /opt/trek/SOURCE/WRITTEN-OFFER.txtIf you run TREK as a network service, that archive is what you make available to your users. The TREK source in this image is unmodified. SecureAnyCloud applies deployment configuration only.
Maintenance
Service control and logs
sudo systemctl status trek.service --no-pager | head -12sudo journalctl -u trek.service -n 20 --no-pager | tail -12TREK also writes an application log to /opt/trek/server/data/logs/.
Configuration
Configuration lives in /etc/trek/trek.env (0640 root:trek). It is a systemd EnvironmentFile, so keep values unquoted and on one line, and restart trek.service after you edit it. LOG_LEVEL=debug gives verbose admin-level detail.
Upgrading TREK
This image pins 3.4.1. Take a backup first (Admin > Backups), then follow upstream's release notes. SecureAnyCloud publishes refreshed images as upstream releases land, so the simplest upgrade path is a new VM from a newer image with your backup restored into it.
Rotating the encryption key
Upstream's own script supports key rotation. Read /opt/trek/wiki/Encryption-Key-Rotation.md on the image first, and back up before you start.
In-app help
The in-app help at /help is served from /opt/trek/wiki on the image, so it always matches the version you are running rather than the latest upstream docs.
Troubleshooting
Sign-in appears to succeed and then immediately fails, or the app says "Access token required". The session cookie is being dropped. This happens when TREK believes the connection is HTTPS but it is not, or the reverse. Over plain HTTP, COOKIE_SECURE=false must be present in /etc/trek/trek.env. Behind TLS, it must be absent and TRUST_PROXY=1 must be present.
429 Too many attempts. Sign-in, registration and invite lookups allow 10 attempts per 15 minutes per client address. Wait for the window to clear, or restart trek.service, because the counter is held in memory.
The map is blank or place search finds nothing. Both need outbound HTTPS from the VM. Check your NSG and any egress firewall. Map tiles need no API key. If your network blocks the public tile hosts, point Settings > Map at a tile server you can reach.
The application will not start. The pre-start guard refuses to run TREK when the bootstrap marker is missing or the encryption key is unset, a dummy value or a known default. The journal records the reason:
sudo journalctl -u trek.service -n 15 --no-pager | tail -8Locked out of the administrator account. Upstream ships a recovery script on the image:
sudo -u trek RESET_ADMIN_EMAIL=<your-email> RESET_ADMIN_PASSWORD=<new-password> \
node /opt/trek/server/reset-admin.jsSupport. SecureAnyCloud support is available 24/7 at sales@secureanycloud.com.
TREK is free software licensed under the GNU Affero General Public License v3.0. SecureAnyCloud is not affiliated with, endorsed by, or sponsored by the TREK project. The TREK name is used only to identify the unmodified open-source software distributed in this image. The corresponding source ships at /opt/trek/SOURCE/.
Deploy on Azure
Launch TREK 3.4.1 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com