A portable, low-power web map server that “hitches a ride” on your personal hotspot.
UNVT Hitchhiker is a lightweight UNVT Portable-style setup for serving static web maps (HTML/CSS/JS + PMTiles) from small devices such as Raspberry Pi Zero-class hardware.
This repository is intentionally simple:
- A shell installer served via GitHub Pages
- A matching uninstaller
- A static document root under
/var/www/hitchhiker
- Status
- Supported Environment
- Install
- Terrain / DEM Requirements
- Uninstall
- What Gets Installed
- Web Server (Caddy)
- PMTiles Extraction & Upload
- Offline Style and Assets
- Verification & Quick Tests
- MapLibre GL JS and pmtiles.js
- Adding Your PMTiles
- Web Interface Features
- Architecture & Philosophy
- Relationship to UNVT Portable
- Acknowledgments
- License
This project is under active construction. The installer aims to be:
- Practical on a fresh Raspberry Pi OS Lite installation
- Idempotent (safe to re-run)
- Conservative (does not aggressively overwrite existing Caddy configs)
- OS: Raspberry Pi OS (Debian-based). Target: Raspberry Pi OS Lite (32-bit)
- Internet access during installation (to download packages and JS assets)
- Privileges: root is required (writes to
/var,/etc, usessystemctl)
Use sudo. Inspect first if desired with curl ... | less.
- Local HTTP only:
curl -fsSL https://unvt.github.io/hitchhiker/install.sh | sudo shFor internet exposure, use your own reverse proxy or tunneling setup (not bundled in Hitchhiker).
If you enable terrain (3D elevation) in the shipped style, follow these requirements so MapLibre can consume the DEM correctly:
- PMTiles file location: the default style expects a DEM PMTiles named
mapterhorn-sl.pmtilesserved from/pmtiles/on the device. The installer will attempt to download example files into/var/www/hitchhiker/pmtiles/if they are available on the configured tunnel host. - Encoding: DEM tiles must use the terrarium encoding (the style sets
encoding: "terrarium"). - Tile size: the style is configured for 512px tiles (
"tileSize": 512); your PMTiles must contain 512px terrarium tiles or MapLibre will misinterpret elevation values. - Max zoom: the style limits DEM requests to
maxzoom: 13to avoid requesting very high-resolution elevation tiles. Only increase this if your PMTiles include higher zoom levels and you accept the storage and bandwidth costs.
Ensure your PMTiles generation/extraction workflow produces 512px terrarium PNG tiles up to the desired zoom. If you require reproducible installer behavior, pin vendor versions as noted in the "MapLibre GL JS and pmtiles.js (How We Fetch "Latest")" section.
Uninstall removes the Hitchhiker document root and the Hitchhiker Caddy site snippet. If you have PMTiles data you want to keep, back it up first.
curl -fsSL https://unvt.github.io/hitchhiker/uninstall.sh | sudo shDocument root:
/var/www/hitchhiker
├── index.html
├── vendor/
│ ├── maplibre/
│ │ ├── maplibre-gl-csp.js
│ │ ├── maplibre-gl-csp-worker.js
│ │ └── maplibre-gl.css
│ └── pmtiles/
│ └── pmtiles.js
└── pmtiles/
└── (your .pmtiles files)
Server management:
/home/hitchhiker/
├── Justfile # Tasks for verification and logs
Why /var/www/hitchhiker?
- It matches common Debian conventions (and avoids inventing a new top-level under
/var).
Why not /var/www directly?
- On Debian/Raspberry Pi OS,
/var/wwwis commonly shared by other web content (often/var/www/html). Using a dedicated subdirectory avoids accidental conflicts. - It makes uninstall safer: removing
/var/www/hitchhikeris predictable, whereas “uninstalling” from/var/wwwrisks deleting unrelated files.
If you still want the absolute simplest layout (/var/www as the site root), you can do it, but Hitchhiker’s installer/uninstaller should then be more conservative (delete only the specific files it created, and never remove /var/www itself).
The installer installs Caddy (if missing) and configures Hitchhiker as an HTTP site on port 80.
Note on GeoLocation API:
- Most browsers require a “secure context” for
navigator.geolocation(HTTPS orlocalhost). - If you need geolocation in the browser while keeping Hitchhiker simple, common options are:
- Use an SSH tunnel and access via
http://localhost:PORT(secure-context exception for localhost). - Temporarily allow an insecure origin in your browser for development/testing.
- Switch Hitchhiker to HTTPS with a locally-trusted certificate (more setup; not the default).
- Use an SSH tunnel and access via
Hitchhiker keeps TLS disabled by default to avoid accidental ACME requests for non-public names. The installer sets a sensible default hostname of hitchhiker for local reference, but Caddy will only attempt automatic HTTPS when you explicitly provide a public hostname via the HITCHHIKER_HOST environment variable.
Examples:
- Download and run the installer interactively (recommended) and enable TLS by setting
HITCHHIKER_HOSTwhen invoking the script withsudo:
curl -fsSL -o install.sh https://unvt.github.io/hitchhiker/install.sh
HITCHHIKER_HOST=example.com sudo sh install.sh- Or, download first and inspect before running (safer):
curl -fsSL -o install.sh https://unvt.github.io/hitchhiker/install.sh
less install.sh # verify contents
HITCHHIKER_HOST=example.com sudo sh install.shImportant notes when enabling TLS:
example.commust resolve to the device's public IP address (DNS A/AAAA record), and port 80/443 must be reachable for ACME (HTTP-01) validation.- If the device is behind NAT or a carrier grade NAT, automatic certificate issuance will likely fail unless you configure port forwarding or use a publicly routable host.
- If you only need local HTTPS for development, consider creating a locally-trusted certificate or use a hostname mapped in
/etc/hostsand import a local CA into your devices — automatic ACME issuance requires a publicly routable name. - If you change
HITCHHIKER_HOSTlater, re-run the installer to update the Caddy snippet (it is idempotent).
If you do not set HITCHHIKER_HOST, Hitchhiker remains an HTTP site on :80 and the built-in default hostname hitchhiker is only useful as a local label (no certificates are requested).
Keep GeoLocation/HTTPS guidance concise:
- Browsers typically allow Geolocation only on HTTPS or localhost. Easiest workaround: SSH tunnel (or access via localhost).
- Only set
HITCHHIKER_HOSTwhen you intend to serve public HTTPS and the hostname is DNS-resolvable to the device. - For local self-signed HTTPS (development), you can run
HITCHHIKER_HOST=hitchhiker.local HITCHHIKER_SELF_SIGN=1 sudo sh install.shand manually trust the cert in your browser.
If you want a small, device-friendly PMTiles file that covers Sierra Leone, perform the extraction on a more powerful machine (your macOS "mother ship") and upload the resulting files to a tunnel host so the Pi can download them during install.
Recommended bounding box (safe margin):
-13.5,6.5,-9.9,10.3
Example workflow (macOS, pmtiles binary installed):
- Create an
extracts/directory in the repository and runpmtiles extractagainst a planet build (adjust source URLs as needed):
pmtiles extract https://r2-public.protomaps.com/protomaps-sample-datasets/planet.pmtiles \
extracts/protomaps-sl.pmtiles --bbox=-13.5,6.5,-9.9,10.3 --maxzoom=12
pmtiles extract https://download.mapterhorn.com/planet.pmtiles \
extracts/mapterhorn-sl.pmtiles --bbox=-13.5,6.5,-9.9,10.3 --maxzoom=12- Upload the extracted files to your tunnel host. Use
rsyncwith--progressto preserve/verify transfer and show progress:
rsync -av --progress extracts/protomaps-sl.pmtiles pod@pod.local:/home/pod/x-24b/data/
rsync -av --progress extracts/mapterhorn-sl.pmtiles pod@pod.local:/home/pod/x-24b/data/After upload, example public URLs might be:
- https://tunnel.optgeo.org/protomaps-sl.pmtiles
- https://tunnel.optgeo.org/mapterhorn-sl.pmtiles
- https://tunnel.optgeo.org/freetown_2025-10-22_nearest.pmtiles (optional high-resolution imagery layer)
- During
install.sh, the installer will attempt to download those files into/var/www/hitchhiker/pmtiles/if they exist at the example URLs. This avoids heavy extraction on the Pi and keeps your microSD usage low. If a file is already present locally and the remote copy is not newer, the download is skipped (usingcurl -ztimestamp comparison).
Notes:
--maxzoom=12is a reasonable compromise for country-level extracts; each additional zoom level roughly doubles the file size. Reduce--maxzoomto save space.- Adjust the source URLs if you maintain your own mirror or use a different daily-build endpoint.
- The repository
.gitignoreis configured to excludeextracts/*.pmtilesto avoid checking large binary files into Git.
The installer also attempts to install a local style and assets so the device can render maps offline. During install.sh the installer will:
- download pre-extracted PMTiles into
/var/www/hitchhiker/pmtiles/(if found at the example tunnel URLs), - install a local Protomaps "light" style with terrain and optional high-resolution imagery layers, and
- leave sprites/glyphs references in the style.json pointing to local paths so the device does not require network access at runtime.
The included style/protomaps-light-style.json is an offline-friendly style that references:
protomaps-sl.pmtiles(vector basemap)mapterhorn-sl.pmtiles(DEM raster with multidirectional hillshading)freetown_2025-10-22_nearest.pmtiles(optional: high-resolution imagery layer, fades in at zoom 13-15 while hillshade fades out)
You can customize or replace the style before running the installer.
After running install.sh, verify the site and assets with these best-effort checks (run on the device, or from a host that can reach it):
# Replace hitchhiker with your device hostname or IP if different
curl -fsS http://hitchhiker/ | sed -n '1,20p'
curl -fsI http://hitchhiker/vendor/pmtiles/pmtiles.js
curl -fsI http://hitchhiker/pmtiles/protomaps-sl.pmtiles
curl -fsI http://hitchhiker/pmtiles/mapterhorn-sl.pmtiles
curl -fsI http://hitchhiker/pmtiles/maxar-2020-freetown.pmtiles
curl -fsI http://hitchhiker/pmtiles/freetown_2025-10-22_nearest.pmtiles
# If Caddy appears down, restart and view logs:
sudo systemctl restart caddy
sudo journalctl -u caddy --no-pager -n 50Open a browser to http://hitchhiker/ to confirm the map loads.
Caddy configuration strategy (conservative + uninstallable):
- A site snippet is written to
/etc/caddy/Caddyfile.d/hitchhiker.caddy - The main
/etc/caddy/Caddyfileis updated only to add animport Caddyfile.d/*.caddyline if it is missing
This keeps Hitchhiker configuration isolated and easy to remove. If you already have a custom Caddy configuration on :80, Hitchhiker will avoid making conflicting changes. In that case, you may need to manually point your existing :80 site to /var/www/hitchhiker.
Hitchhiker installs MapLibre GL JS and pmtiles.js as static vendor assets so the device can serve everything locally.
Current approach:
- Download from UNPKG using the
@latesttag - Place files under
/var/www/hitchhiker/vendor/...
Files fetched:
https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl-csp.jshttps://unpkg.com/maplibre-gl@latest/dist/maplibre-gl-csp-worker.jshttps://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.csshttps://unpkg.com/pmtiles@latest/dist/pmtiles.js
Trade-offs:
- Pros: simple, no build step, always pulls a current stable release
- Cons: not perfectly reproducible over time (because “latest” moves)
If you need reproducibility, pin versions inside install.sh (e.g. maplibre-gl@5.15.0, pmtiles@4.3.2).
Copy your .pmtiles into:
/var/www/hitchhiker/pmtiles/
The default index.html is a minimal template intended to be edited for your own style and data.
The Hitchhiker web interface provides an intuitive layer control system:
Five independent layer toggles (checkbox-based), displayed as concise labels with descriptive tooltips:
- basemap - Vector basemap derived from OpenStreetMap (Protomaps)
- hillshade - Multi-directional hillshading for terrain visualization (Mapterhorn)
- terrain - 3D terrain elevation with DEM (Mapterhorn)
- drone - High-resolution 4cm aerial imagery (Freetown 2025-10-22)
- satellite - Satellite imagery 30cm resolution (Freetown 2020-02-03, Maxar)
- Toggle button (▲/▼) in the top-left corner collapses the control panel
- Useful for mobile devices or to maximize map viewing area
- Panel slides up smoothly when minimized
- Button remains visible even when panel is minimized (fixed positioning)
Layer visibility is encoded in the URL hash fragment for easy sharing:
Format: #layers=<layer1>,<layer2>,...
This format is compatible with MapLibre's #map= fragment, allowing both to coexist:
#map=7/8.5/-11.5&layers=basemap,hillshade,terrain
Layer names:
basemap- Basemaphillshade- Hillshadeterrain- Terrain 3Ddrone- Drone imagerysatellite- Satellite imagery
Examples:
# Default (all layers enabled) - no parameter needed
http://hitchhiker/
# Basemap only
http://hitchhiker/#layers=basemap
# Basemap + Hillshade
http://hitchhiker/#layers=basemap,hillshade
# Imagery only
http://hitchhiker/#layers=drone,satellite
# Basemap + Terrain + Drone imagery
http://hitchhiker/#layers=basemap,hillshade,terrain,drone
# Combined with map position
http://hitchhiker/#map=14/8.484/-13.231&layers=basemap,drone
Remote Access:
- LAN hostname:
http://hitchhiker/ - Cloudflare tunnel:
https://hitchhiker.optgeo.org/(if configured)
Behavior:
- Checkbox state changes update the URL hash in real-time (no page reload)
- URLs can be shared to reproduce exact layer visibility states
- Missing or invalid layer names are ignored
- No
layersparameter = all layers enabled (default) - Empty layers parameter (
#layers=) = all layers disabled - Compatible with MapLibre's
#map=fragment for preserving map position
Extensibility:
When adding new layers to the map style, update three locations in /var/www/hitchhiker/index.html:
- Add to
layerNamesobject (JavaScript) - Add checkbox control to HTML
- Add visibility logic to
applyVisibility()function
- Locate button is available in the top-left corner (part of MapLibre's standard controls)
- Click to request the browser to find and zoom to your current location
- Requires HTTPS or localhost secure context (see GeoLocation API note in main documentation)
- Uses non-blocking geolocation request (does not track movement continuously)
Background and design notes start here (skip if you only need install/run).
UNVT Hitchhiker represents a pragmatic step forward in distributed geospatial data serving, combining three key principles:
1. Offline-first and local autonomy
- Web maps do not require a central server or constant internet connectivity.
- All essential data (vector tiles, raster terrain, imagery, stylesheets, glyphs, sprites) are bundled and served locally.
- A device can operate independently from the internet after installation, supporting disconnected or intermittent connectivity scenarios.
2. Low-power and low-footprint deployment
- Designed for Raspberry Pi Zero and similar single-board computers (not just cloud VMs).
- No heavy GIS servers, databases, or processing pipelines.
- Minimal dependencies: shell scripts, static web assets, and a lightweight HTTP server (Caddy).
3. Forward deployment (edge hosting)
- Web maps can be served from the field, car, boat, or disaster site without relying on infrastructure.
- Data moves toward the edge rather than centralizing in a single cloud endpoint.
- Supports peer-to-peer or broadcast scenarios where a single device shares data with multiple clients via a personal hotspot.
Why "Hitchhiker"? Hitchhiker implies traveling light and moving fast. A single small device carries everything needed and can deploy its own map server anywhere with a power supply and optional internet access for setup. The map "hitches a ride" on your hotspot, making it available to any client on the network.
This approach complements the UNVT Portable framework by enabling truly offline, low-power, and field-operable web map infrastructure.
UNVT Hitchhiker is a valid UNVT Portable-style implementation focused on:
- Static-by-default web maps
- Personal connectivity (hotspot) instead of infrastructure ownership
- Low-power operation
It intentionally does not:
- Run its own access point
- Provide heavyweight GIS backends
- Target high-availability production deployments
This project is built on the shoulders of many open-source and open-data initiatives:
-
Protomaps Basemaps - Vector basemap tiles derived from OpenStreetMap data. © OpenStreetMap contributors. Map data licensed under ODbL.
-
Mapterhorn - Open terrain tiles project by Leichter als Luft GmbH. Made possible through support from the NGI0 Core Fund, established by NLnet with financial support from the European Commission's Next Generation Internet programme. Terrain data sources are documented at mapterhorn.com/attribution. Code: BSD-3 License.
-
OpenAerialMap - Freetown high-resolution imagery provided via OpenAerialMap:
- 2025-10-22 Freetown Urban Imagery (4cm resolution) - Provided by DroneTM. Captured with DJI Mini 4 Pro. Licensed under CC-BY 4.0.
- 2020-02-03 Maxar Freetown Mosaic (30cm resolution) - Satellite imagery from Maxar. Licensed under CC-BY-NC 4.0.
Both available through OpenAerialMap. Made with ♥ by HOT partners and community.
- MapLibre GL JS - Open-source map rendering library (BSD-3 License)
- PMTiles - Cloud-optimized tile archive format by Protomaps (BSD-3 License)
- Caddy - Modern web server with automatic HTTPS (Apache 2.0 License)
- just - Command runner for task automation (CC0 License)
- tunnel.optgeo.org - Tile hosting and distribution infrastructure provided by optgeo for example deployments and testing
Built on Debian-based Raspberry Pi OS for low-power, forward-deployed mapping infrastructure.
CC0 1.0 Universal (Public Domain Dedication)