selfhostable maps
Find a file
2026-04-03 21:59:56 +02:00
backend reuse previous build artifacts 2026-04-03 21:59:56 +02:00
docs draft 2026-03-30 09:22:16 +02:00
mobile combine martin calls 2026-04-03 21:11:38 +02:00
.gitignore draft 2026-03-30 09:22:16 +02:00
README.md photon dockerfile 2026-03-30 16:26:57 +02:00

Privacy Maps

A privacy-first Google Maps alternative. No tracking, no accounts, no third-party API calls. All services are self-hosted.

Stack: Flutter (mobile) · Rust/Actix-web (backend) · PostgreSQL/PostGIS · Martin (tiles) · Photon (geocoding) · OSRM (routing)


Project Structure

maps/
├── docs/           # Specs, architecture, API contracts, data model
├── backend/        # Rust API gateway + Docker Compose for all services
│   ├── scripts/    # Data import scripts
│   └── initdb/     # PostgreSQL init SQL (runs on first startup)
└── mobile/         # Flutter app

Backend Setup

Requirements

  • Docker or Podman + Compose
  • ~50 GB free disk (for OSM data, OSRM graphs, Photon index)
  • ARM64 (Raspberry Pi) or amd64 host

1. First-time setup

cd backend

# Build the custom images (PostGIS arm64 + importer toolchain)
podman compose build

# Start PostgreSQL first — extensions are enabled automatically on first start
podman compose up -d postgres

# Wait until ready
podman compose exec postgres pg_isready -U maps

2. Import data (first time)

Run each script individually rather than update_all.sh on first setup — the download takes ~6 minutes and you only need it once.

# Step 1: Download OSM PBF extract (~1.2 GB for Netherlands, ~6 min)
podman compose run --rm importer /app/scripts/01_download.sh

# Step 2: Import tile data into PostGIS (~10-20 min)
podman compose run --rm importer /app/scripts/02_import_tiles.sh

# Step 3: Import POI data into PostGIS
podman compose run --rm importer /app/scripts/03_import_pois.sh

# Step 4: Download Photon geocoding index (~2.3 GB for Netherlands)
podman compose run --rm importer /app/scripts/04_import_geocoding.sh

# Step 5: Preprocess OSRM routing graphs — run on the HOST, not in the container
# Builds a local ARM64 OSRM image on first run (~30-60 min), then preprocesses
# car/foot/bicycle profiles. Subsequent runs skip the image build.
bash backend/scripts/05_import_routing_host.sh ~/dev/maps/data

# Step 6: Register offline regions in the database
podman compose run --rm importer /app/scripts/06_build_offline_packages.sh

To change the country for step 4, use the full country name:

PHOTON_COUNTRY=germany podman compose run --rm importer /app/scripts/04_import_geocoding.sh

3. Start all services

podman compose up -d --scale importer=0

After startup, restart the services that depend on the imported data:

podman compose restart martin
podman compose restart osrm-driving osrm-walking osrm-cycling

4. Weekly data refresh

Use update_all.sh for scheduled updates. It re-downloads the PBF only if the file has changed on the server (wget -N), then reimports everything. Note: OSRM preprocessing is not included — run it separately on the host if routing data has changed.

podman compose run --rm importer /app/scripts/update_all.sh

# If routing data changed, also run on the host:
bash backend/scripts/05_import_routing_host.sh ~/dev/maps/data
podman compose restart osrm-driving osrm-walking osrm-cycling

To refresh only specific data (e.g. tiles changed but routing didn't):

podman compose run --rm importer /app/scripts/01_download.sh
podman compose run --rm importer /app/scripts/02_import_tiles.sh
podman compose restart martin

Service ports

Service Port Description
backend 8080 Rust API gateway
postgres 5432 PostGIS database
redis 6379 Tile/route cache
martin 3001 Vector tile server
photon 2322 Geocoding (search)
osrm-driving 5000 Car routing
osrm-walking 5001 Walking routing
osrm-cycling 5002 Cycling routing

Mobile App Setup

cd mobile
flutter pub get
dart run build_runner build --delete-conflicting-outputs
flutter run

On first launch, go to Settings and enter your backend URL (e.g. http://your-pi-ip:8080).


Troubleshooting

PostGIS extension error — The postgres container must be recreated to pick up the init scripts:

podman compose down postgres
podman compose up -d postgres

Podman short-name error — All images use full docker.io/ registry paths. If you see this on another service, prefix its image with docker.io/.

Exec format error on Pi — The postgres image is built locally from postgis.Dockerfile using arm64v8/postgres as base. Run podman compose build postgres to rebuild it.

OSRM exec format errorosrm/osrm-backend is amd64-only. The project uses osrm-arm64.Dockerfile to build a native ARM64 image. Run podman compose build osrm-driving (builds once, shared by all three OSRM services) or let 05_import_routing_host.sh build it automatically.


Attribution