5.0 KiB
AdventureLog Setup Summary
What It Is
AdventureLog is a self-hosted travel logging app with a SvelteKit frontend and Django/PostGIS backend. Live at: https://go.jgitta.com
Infrastructure
| Component | Detail |
|---|---|
| Host | siklos (192.168.88.27) |
| Portainer Stack | #47 — adventurelog (endpoint 3) |
| Frontend container | adventurelog-frontend — port 8015 (SvelteKit SSR) |
| Backend container | adventurelog-backend — port 8016 (Django/nginx → gunicorn :8000) |
| Database container | adventurelog-db (PostGIS 16-3.5) |
| Data volumes | /srv/docker/adventurelog/postgres_data/ and /srv/docker/adventurelog/media/ |
| Reverse proxy | Caddy VM (192.168.88.110) — go.jgitta.com block in /etc/caddy/sites/infrastructure.caddy |
Caddy Routing (Corrected 2026-07-02 — now matches official docs)
AdventureLog authenticates through the SvelteKit frontend: on login the frontend keeps the
Django session server-side and does NOT hand the browser a Django sessionid cookie (it even
clears csrftoken in the browser). Therefore all /api/* and /auth/* traffic must go through
the frontend, which proxies to Django server-to-server with the correct session + CSRF token.
Only static-type paths go straight to Django.
This is the official AdventureLog Caddy design (https://adventurelog.app/docs/install/caddy.html), adapted for our remote Caddy VM (proxying to LAN ports 8015/8016) plus our standard security headers:
go.jgitta.com {
import headers_base
encode zstd gzip
header {
X-Frame-Options "SAMEORIGIN"
Permissions-Policy "geolocation=(self), microphone=(), camera=()"
}
# Backend (Django): media, admin, static files, legacy account pages
@backend path /media* /admin* /static* /accounts*
handle @backend {
reverse_proxy http://192.168.88.27:8016 { import proxy_timeouts }
}
# Everything else (pages, /api/*, /auth/*) -> SvelteKit frontend,
# which proxies API/auth to Django server-side with session + CSRF.
handle {
reverse_proxy http://192.168.88.27:8015 { import proxy_timeouts }
}
}
geolocation=(self) (inline, not the web_secure snippet) is required so the "Use My Location"
button works.
History — why the old split routing was removed
Earlier the config split /api/* by HTTP method: GET/HEAD/OPTIONS went directly to Django and
only writes went through the frontend. That was a workaround for a geocode 401 seen during initial
setup. After a frontend container auto-update (:latest) on 2026-07-02, the frontend stopped
exposing a Django session to the browser, so the "GET direct to Django" path could no longer
authenticate — every authenticated read (including geocoding / map search) returned
401 "Authentication credentials were not provided." Reverting to the official all-/api-through-
frontend routing fixed it. Lesson: don't hand-split /api; let the frontend proxy it.
Portainer Environment Variables
All env vars are stored in Portainer's environment section (not hardcoded in compose). Key ones:
| Variable | Value |
|---|---|
| PUBLIC_SERVER_URL | http://server:8000 |
| ORIGIN | https://go.jgitta.com |
| PUBLIC_URL | https://go.jgitta.com |
| CSRF_TRUSTED_ORIGINS | https://go.jgitta.com |
| FRONTEND_URL | https://go.jgitta.com |
| FRONTEND_PORT | 8015 |
| BACKEND_PORT | 8016 |
| BODY_SIZE_LIMIT | Infinity |
| POSTGRES_DB | adventurelog |
| POSTGRES_USER | adventure |
| DEBUG | False |
| DISABLE_REGISTRATION | False |
Secrets (DB password, SECRET_KEY, admin credentials) are in Portainer only — not in this file.
Note: DJANGO_ADMIN_PASSWORD is only the bootstrap password used at first container start; the
live admin password has since been changed in-app.
Optional/recommended (not currently set): ENABLE_RATE_LIMITS=True (docs recommend for production).
Bugs Fixed During Setup
- CSRF errors on login —
CSRF_TRUSTED_ORIGINSmust match the public URL exactly. Set in env. - GET /api/ returning DRF HTML instead of JSON* — old Caddy did a broken URI rewrite; removed.
- Geocode search 401 — see "Caddy Routing" history above. Final fix: route all
/api/*through the SvelteKit frontend (official design), not directly to Django. - Category creation 500 — AdventureLog returns 500 (not 400) on duplicate category slug (unhandled IntegrityError). Workaround: delete the duplicate via Django shell.
- "Use My Location" blocked — set
Permissions-Policy: geolocation=(self)on the site block.
Admin Access
- URL: https://go.jgitta.com/admin
- Username: admin
- Django shell:
docker exec -it adventurelog-backend python manage.py shell
Known Limitations / Not Configured
- Images run on
:latestwith Watchtower auto-update — an update on 2026-07-02 changed frontend auth behavior and broke the old routing. Consider pinning image versions. - No Google Maps API key — geocoding uses OpenStreetMap/Nominatim (works fine).
- No Strava integration.
- B2/Kopia backup not configured for this stack.