Skip to content

Photon Geocoding Service

Photon is a self-hosted, OpenStreetMap-based geocoder. Tomoda runs its own Photon instance both locally and in production as a complement to Google Places.

Why self-host

Google Places is excellent but has two practical downsides:

  • Cost. Per-request pricing on autocomplete and place details adds up fast at scale, especially for low-value queries (typeahead while a user is still thinking).
  • Rate limits and outages. A hard external dependency on a third-party API for every search is fragile.

Photon gives us:

  • A free local fallback / cache layer for forward geocoding.
  • Predictable latency (in-cluster, no internet round-trip).
  • The ability to pre-load multilingual indexes for the markets we care about (English, Japanese, Simplified and Traditional Chinese).

How the backend uses it

The Go service is at backend/internal/services/photon_service.go. The backend calls it for forward geocoding (text → coordinates + place metadata) with a 3-second timeout so that a misbehaving Photon Pod can never wedge a request handler.

The full search flow in the LocationsService (see ../backend/services/locations.md) layers providers: Google Places is the primary, Photon is the fallback when Google fails or returns nothing useful. The two have different strengths — Google for fresh business data, Photon for OSM-curated POIs and address coverage.

Local

The local Photon container is defined in docker-compose.dev.yml:

photon:
  image: rtuszik/photon-docker:2.2.0   # Photon 1.1.0 inside
  ports: ["2322:2322"]
  volumes: [photon_data:/photon/data]
  environment:
    - JAVA_OPTS=-Xmx2g
    - REGION=${PHOTON_REGION:-canada}

The image is pinned to rtuszik/photon-docker:2.2.0:latest is dangerous because the on-disk index format is version-sensitive. When bumping you also have to bump the Photon images in the devops repo. See DevOps → Photon Deployment and DevOps → Photon Indexer.

On first start the container downloads the index for REGION (default canada — the smallest, fastest to download) from the community mirror at r2.koalasec.org. This takes several minutes; the Compose healthcheck has a 300s start_period to wait it out.

Using the multilingual GCS index locally

The community mirror only ships English / German / French / Italian. To test Japanese or Chinese queries against the same index used in production, set the FILE_URL and MD5_URL env vars to point at the GCS-hosted multilingual bundle, then recreate the container:

# Canada multilingual (smallest)
export PHOTON_FILE_URL="https://storage.googleapis.com/development-485000-photon-index/canada/photon-db-canada-multilang-latest.tar.bz2"
export PHOTON_MD5_URL="https://storage.googleapis.com/development-485000-photon-index/canada/photon-db-canada-multilang-latest.tar.bz2.md5"

docker compose -f docker-compose.dev.yml down -v
docker compose -f docker-compose.dev.yml up -d photon

(The compose file documents how to wire those env vars in.)

Production

In the cluster, Photon runs as a Pod in the data namespace. The backend reaches it via the cluster-internal DNS name:

PHOTON_URL=http://photon.platform.svc.cluster.local:2322

(set in k8s/apps/tomoda/base/backend-deployment.yaml in the devops repo.)

The production index lives in gs://development-485000-photon-index/, organized by region (canada/, asia/, …) with multilang variants covering en, ja, zh, zh-Hans, and zh-Hant — matching the four supported frontend locales (en-US, ja-JP, zh-CN, zh-TW).

The index is rebuilt periodically by a photon-indexer CronJob (currently suspended awaiting Nominatim setup — indexes are built manually via scripts/photon-index-local.sh in the devops repo). See DevOps → Photon Multilang Rollout for the rollout history and DevOps → Photon Indexer for the cluster side.

When something's wrong

  • task dev hangs for several minutes on first run. Almost always the Photon container downloading its index. Tail docker logs tomoda-photon to watch progress.
  • Backend logs say photon: timeout. The 3s timeout fired. Check that the Photon Pod is Ready; if not, look at its logs (it may be re-downloading an index after a volume wipe).
  • Geocoding results are missing non-English text. You're probably on the default-region community index, not the multilingual GCS one. See above.

See also