niroku — a new implementation of UNVT Portable with JICA, for 2026 (Raspberry Pi OS trixie)
niroku is a new implementation of UNVT Portable. It is co‑developed with JICA Quick Mapping Project and targeted for 2026 use. niroku sets up an offline local web map server on Raspberry Pi OS (trixie). It uses Caddy (reverse proxy) and Martin (PMTiles tile server). It is designed for field operations where power and connectivity can be unstable.
The simplest way to install niroku:
curl -fsSL https://unvt.github.io/niroku/install.sh | sudo -E bash -
Or using wget:
wget -qO- https://unvt.github.io/niroku/install.sh | sudo -E bash -
If you prefer to review the script before running:
# Download the script
curl -fsSL https://unvt.github.io/niroku/install.sh -o install.sh
# Review the script
less install.sh
# Make it executable
chmod +x install.sh
# Run the installer
sudo ./install.sh
niroku follows the proven x-24b architecture:
Web Browser ←→ Caddy (Reverse Proxy) ←→ Martin (PMTiles Server)
niroku is the next iteration of UNVT Portable. Compared to earlier versions, it:
What you get:
The niroku installer will:
aria2
, btop
, gdal-bin
, git
, jq
, ruby
, tmux
, vim
curl
, wget
, hostapd
, dnsmasq
, qrencode
, build tools, and related packagesvite@latest
, maplibre-gl@latest
, pmtiles@latest
)/opt/niroku
with data subdirectorymartin.yml
(PMTiles paths, web UI) and Caddyfile
(reverse proxy, CORS)/tmp/niroku_install.log
for troubleshootingAfter installation completes:
Access the web interface:
# Find your Raspberry Pi's IP address
hostname -I
# Access via web browser at:
# http://[YOUR_IP_ADDRESS]:8080
# Martin tile server at:
# http://[YOUR_IP_ADDRESS]:8080/martin
/opt/niroku/data
http://[YOUR_IP]:8080/martin/[filename]/{z}/{x}/{y}
Manage services:
# Check service status
systemctl status martin
systemctl status caddy-niroku
# View logs
journalctl -u martin -f
journalctl -u caddy-niroku -f
# Restart services
systemctl restart martin caddy-niroku
qrencode
toolUse installed tools:
# Check installed versions
node --version # Node.js LTS
npm --version
vite --version
docker --version # Docker Engine
cloudflared --version # Cloudflare Tunnel
tippecanoe --version # Vector tile tool
pmtiles --version # PMTiles CLI
martin --version # Tile server
caddy version # Web server
# Example: Convert GeoJSON to PMTiles
tippecanoe -o output.pmtiles input.geojson
# Example: Inspect PMTiles metadata
pmtiles show /opt/niroku/data/yourfile.pmtiles
For non-interactive installations (e.g., automated deployments), you can use these environment variables:
# Skip OS compatibility check
export NIROKU_FORCE_OS=1
# Keep existing installation (default is to overwrite)
export NIROKU_KEEP_EXISTING=1
# Example: Full non-interactive install (overwrites existing by default)
sudo NIROKU_FORCE_OS=1 ./install.sh
# Example: Non-interactive install that keeps existing installation
sudo NIROKU_FORCE_OS=1 NIROKU_KEEP_EXISTING=1 ./install.sh
⚠️ Important Security Notes:
set -e
to exit on errors# Ensure you're using sudo
sudo ./install.sh
The installer creates a detailed log file for troubleshooting:
# View installation log
sudo cat /tmp/niroku_install.log
# View uninstallation log
sudo cat /tmp/niroku_uninstall.log
# Check Martin status
sudo systemctl status martin
# Check Caddy status
sudo systemctl status caddy-niroku
# View logs
sudo journalctl -u martin -n 50
sudo journalctl -u caddy-niroku -n 50
If you see errors about missing repository files (e.g., legacy cloudflared repository), the installer will automatically clean them up. If issues persist:
# Manually remove problematic repository files
sudo rm -f /etc/apt/sources.list.d/cloudflared.list
sudo rm -f /etc/apt/keyrings/cloudflare-main.gpg
sudo apt-get update
Martin does not provide prebuilt binaries for armv6l architecture (Raspberry Pi Zero W). If you need Martin on Pi Zero, you must build it from source:
# Prerequisites: Rust toolchain and build dependencies
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source ~/.cargo/env
# Install build dependencies
sudo apt-get install -y cmake protobuf-compiler libsqlite3-dev libssl-dev build-essential
# Disable /tmp tmpfs to avoid "No space left" errors
sudo systemctl mask tmp.mount
sudo reboot
# Build Martin (takes several hours on Pi Zero)
cargo install martin --locked --jobs 1
# Install the binary
sudo install -m 0755 ~/.cargo/bin/martin /usr/local/bin/martin
# Verify installation
martin --version
Note: Building Martin on Pi Zero W with --jobs 1
can take 4-6 hours. Consider building on a faster machine with cross-compilation, or use Pi Zero for Caddy-only setups.
niroku can cache some npm packages during installation so devices with limited or no internet access can still install required frontend packages.
@latest
tag for certainty: vite@latest
, maplibre-gl@latest
, pmtiles@latest
.# On a machine with internet access (after running install.sh):
sudo tar -C /usr/lib -czf /tmp/npm-global-cache.tar.gz node_modules
sudo chown $USER:$USER /tmp/npm-global-cache.tar.gz
# Copy the archive to the offline device (e.g., via USB or scp)
sudo tar -C /usr/lib -xzf /tmp/npm-global-cache.tar.gz
sudo npm rebuild -g || true
Note: Global package paths vary by distribution and Node.js packaging. On Debian-based NodeSource installs, global modules typically live under /usr/lib/node_modules
.
For automated testing we use expect
to wrap scp
and ssh
so the test harness can authenticate to test devices using the niroku
user with password niroku
. Example expect
one-liners used in testing:
expect -c 'set timeout 30; spawn scp -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15 ./install.sh niroku@m333.local:/var/tmp/install.sh; expect -re {password:}; send "niroku\r"; expect eof'
expect -c 'set timeout 1200; spawn ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15 niroku@m333.local "sudo /var/tmp/install.sh"; expect -re {password:}; send "niroku\r"; expect eof'
Use these only for temporary testing harnesses. Do not hard-code credentials or expose them in production documentation.
# Check what's using port 8080
sudo lsof -i :8080
# Stop conflicting service if needed
sudo systemctl stop [service-name]
# Or edit the Caddyfile to use a different port
sudo nano /opt/niroku/Caddyfile
sudo systemctl restart caddy-niroku
# Ensure files are in the correct directory
ls -la /opt/niroku/data/
# Check file permissions
sudo chmod 644 /opt/niroku/data/*.pmtiles
# Restart Martin
sudo systemctl restart martin
The installer automatically falls back to /var/tmp
if /tmp
is not writable. If you encounter issues:
# Check /tmp permissions
ls -ld /tmp
# Should be: drwxrwxrwt (permissions 1777)
sudo chmod 1777 /tmp
If Docker fails to install or the GPG key conflicts occur:
# Remove existing Docker GPG key
sudo rm -f /etc/apt/keyrings/docker.gpg
# Re-run the installer
sudo ./install.sh
To remove niroku and all installed components:
curl -fsSL https://unvt.github.io/niroku/uninstall.sh | sudo -E bash -
# Download the uninstall script
curl -fsSL https://unvt.github.io/niroku/uninstall.sh -o uninstall.sh
# Review the script
less uninstall.sh
# Make it executable
chmod +x uninstall.sh
# Run the uninstaller
sudo ./uninstall.sh
The uninstaller will:
/usr/local/bin/martin
This release contains a small but important refactor to make the installer and uninstaller more maintainable and more robust in the field.
Highlights
refactor: add a reusable download helper try_download()
to centralize robust curl downloads and candidate URL attempts
refactor: add create_systemd_service()
helper to centralize systemd unit creation, enable and start logic
refactor: make uninstall.sh
symmetric with install.sh
by adding remove_*
helpers (e.g. remove_martin
, remove_caddy
, remove_go_pmtiles
) so each install step has a clear uninstall counterpart
feature: lightweight post-install smoke checks to verify service status and basic HTTP responses
Why this matters
Reduces duplicated code paths and makes future changes safe and easier to audit
Better logging and clearer failure points when used in automated test harnesses
Compatibility notes
Raspberry Pi 4/400 (arm64): Full support — prebuilt binaries are used for Martin and other components
Raspberry Pi Zero W (armv6l): Limited support — Martin prebuilt binaries are not available and must be built from source on-device (see “Building Martin on Raspberry Pi Zero (armv6l)” in Troubleshooting). Docker CE is also not officially supported on armv6l.
Testing
m333
(Raspberry Pi 400 / aarch64) as part of this release verification. Logs are available on the device at /tmp/niroku_install.log
and /tmp/niroku_uninstall.log
.If you depend on automatic deployments or CI, consider running the installer in a VM or test device first and reviewing /tmp/niroku_install.log
after the run.
/usr/local/bin/pmtiles
/opt/niroku
)aria2
, btop
, gdal-bin
, jq
, ruby
, tmux
, vim
)/tmp/niroku_uninstall.log
The script will ask for confirmation before removing anything and show you exactly what will be removed.
This project is part of the UNVT (United Nations Vector Tile Toolkit) ecosystem.
This project is released under the CC0 1.0 Universal license, dedicating it to the public domain. See LICENSE for details.
niroku is developed as part of the UNVT project to support disaster response and field operations worldwide, with special focus on JICA Knowledge Co-creation Programs.