Three Repos, One Decision
You decided to self-host geocoding. Great. Now you’ve got three GitHub tabs open, all of them claiming to do the same thing, all of them with completely different deploy stories. One spins up in an evening, one needs Java and Elasticsearch, and the third looks like a small data engineering team’s hobby project.
Here’s the thing — they’re not actually competing on the same axis. Nominatim, Photon, and Pelias all geocode, but they’re optimized for radically different workloads. Picking the wrong one isn’t a disaster, but it’s the difference between a working setup in a weekend and a working setup in a month. Let’s cut through it.
The 30-Second Verdict
If you stop reading after this paragraph, take this:
- Nominatim — accurate, well-maintained, the safe default. Pick this unless you have a specific reason not to.
- Photon — fast typeahead and fuzzy search on top of OSM data. Pick this if you’re building autocomplete UX.
- Pelias — multi-source (OSM + WhosOnFirst + OpenAddresses + GeoNames), Elasticsearch-based. Pick this only if you genuinely need that data breadth and have ops budget.
The combo move that a lot of teams actually make: run Nominatim for the API and Photon alongside it for the autocomplete UI. We’ll get to that.
How They Actually Differ Under the Hood
These three projects make different bets about what “geocoding” means architecturally.
Nominatim is built on PostgreSQL + PostGIS. The OSM data goes into a relational schema, search is done with trigram indexes plus a custom tokenizer plus the structured place table. It’s the reference implementation — the geocoder behind openstreetmap.org. Maintained by people who care about correctness more than benchmarks.
Photon is a Java application backed by OpenSearch. It doesn’t import OSM directly — it imports a Nominatim database dump and re-indexes it for fuzzy search. Komoot built it because they wanted “as you type” search that worked well with typos and partial input. The result is fast, forgiving, and lightweight to run if you grab their prebuilt index.
Pelias is a Node.js stack with multiple importers. It pulls from OpenStreetMap, WhosOnFirst (a global gazetteer), OpenAddresses (open address data from various government sources), and GeoNames. All of it gets normalized and dumped into Elasticsearch indices. The architecture is a fleet of microservices: api, placeholder, libpostal, interpolation, plus the importers. It’s the most ambitious of the three, and the most complex.
Install Effort Compared
Be honest with yourself about which install you actually want to live with.
Nominatim with the mediagis/nominatim Docker image is one Compose file and a regional PBF. Hours-to-days for the import depending on size. The runtime is a single container plus its Postgres data volume. Boring in the best way.
Photon is even simpler at runtime. You can download a prebuilt index from the Komoot mirror, drop the JAR or container next to it, and you’re up in minutes. The catch: that prebuilt index is the entire planet by default (~80 GB). If you want a regional index, you build it yourself from a Nominatim database dump, which means you need a Nominatim install first.
Pelias is a docker-compose file with around a dozen services and an importer pipeline. Each importer (osm, openaddresses, whosonfirst, geonames, polylines) has its own config and runs separately. The official quickstart works, but if you want a non-default region setup, prepare for an afternoon of YAML.
If you already followed the Nominatim install guide, you have the hard part done — and you can layer Photon on top of it with very little additional effort.
Search Quality: Where Each One Shines
Here’s the rough capability map. Cells are general impressions, not benchmarks.
| Capability | Nominatim | Photon | Pelias |
|---|---|---|---|
| Forward geocode (full address) | Excellent | Good | Good |
| Reverse geocode | Excellent | Limited | Good |
| Fuzzy / typo tolerance | Weak | Excellent | Good |
| Autocomplete latency | Slow-ish | Fast | Medium |
| Structured search (street, city, …) | Yes | Limited | Yes |
| Multi-source data | OSM only | OSM only | OSM + others |
| API surface | REST | REST | REST |
| Docker quickstart | One container | One container | Many containers |
The big practical implications:
- Type “1600 Pennsy” — Nominatim is unhappy with the partial match. Photon will give you a ranked list of suggestions immediately. Pelias is somewhere between.
- Reverse geocode a coordinate in the middle of a forest — Nominatim returns the nearest meaningful address with a confidence score. Photon doesn’t really do reverse well. Pelias does but the result quality depends on which sources covered that area.
- Find the address
123 Main Stin 50 cities — All three handle this. Nominatim wants you to use structured search to disambiguate. Pelias’s confidence scoring tends to be the most usable here because of the multi-source data.
Resource Footprint
This is where the projects diverge hard.
Nominatim wants disk and RAM for Postgres. A regional extract (say North America) imports to ~100 GB and runs comfortably on a box with 16 GB of RAM. The full planet wants 700 GB+ of disk and at least 64 GB of RAM to import in a reasonable timeframe.
Photon is the lightest at runtime. The prebuilt planet index is around 80 GB on disk. Java heap of 4–8 GB is plenty for query workloads. CPU usage is low except under sustained autocomplete load.
Pelias is the heaviest. Multiple Elasticsearch nodes, multiple importer processes, the libpostal service eating its own RAM allocation. Plan for 32 GB of RAM minimum for a useful planet-scale install, more if you want decent throughput. Disk usage adds up fast across the indices.
When Each Wins
Pick Nominatim if…
- You want a general-purpose geocode + reverse geocode API
- You’re doing batch jobs (backfilling addresses, enriching datasets)
- You already run Postgres and like the idea of one more database role
- You want one well-trodden tool with good docs
- You don’t need typo-tolerant typeahead
This covers the vast majority of self-hosting scenarios. Honestly, if you’re not sure, this is the default.
Pick Photon if…
- You’re building search-as-you-type UX in a web or mobile app
- Sub-100ms response time is a real requirement
- Users will mistype names and you need that to still work
- Reverse geocoding is a “nice to have” rather than a core need
The classic combo: run Nominatim for the structured API, run Photon for the autocomplete bar. Photon literally builds its index from a Nominatim database, so they share a data source and stay in sync naturally.
Pick Pelias if…
- Multi-source data is a hard requirement (OSM coverage isn’t enough on its own)
- You need POI data from sources OSM doesn’t cover well
- You have an actual ops team or you genuinely enjoy running Elasticsearch clusters
- You’re geocoding for a specific country with great open address data
Fair warning: Pelias is the hardest to operate of the three. Several of its dependencies (placeholder, libpostal) are non-trivial pieces of software. It’s the right tool when you’ve outgrown Nominatim’s data, not the right tool because it sounds more impressive.
The Combo Move
A lot of teams don’t actually pick exclusively. The most common production pattern I see:
- Nominatim for the REST API (forward geocode, reverse geocode, structured search). This is what your backend services hit.
- Photon for the autocomplete in the web UI. Fast typeahead, forgiving of typos, paired with a debounce on the frontend.
Both run on the same OSM data. Photon’s index gets built off the Nominatim Postgres dump, so they stay consistent. Total ops cost is one Postgres + one Elasticsearch + two app containers. Easily fits on a single home lab box if you’re modest about scope.
Pelias, by contrast, doesn’t really compose with the others. It owns its own data pipeline and indices. You’d run it instead of, not alongside.
Watch Outs
A few things you’ll find out the hard way:
- Nominatim’s fuzzy matching is genuinely weak. If your users type “san fransicso” it will not gracefully recover. Photon will. Pelias will partially.
- Photon doesn’t update incrementally. Updates require rebuilding the index. Plan for periodic full rebuilds rather than live sync.
- Pelias’s docker-compose looks tidy but is brittle. Importer ordering matters. Failed imports leave the indices in weird states.
- All three are sensitive to disk speed. NVMe makes a real difference, especially during import.
- None of them are CDN-friendly out of the box. If you expose any of these to the public internet, you need rate limiting and probably caching at the edge.
My Pick (and Why)
For most home lab and small-shop scenarios: Nominatim alone, optionally with Photon for autocomplete UX. Two containers, both well-understood, and the data flow between them is a straight pipeline.
I reach for Pelias only when there’s a clear reason — usually “OSM coverage in this country is bad and I have a better address dataset” or “I need confidence scoring across multiple sources for de-duplication.” Pure curiosity is not a good reason. The ops cost is real.
If you’re standing at the start of this and you’re not sure, start with Nominatim. You can always layer Photon on top later. Going from Nominatim → Pelias is more like “rebuild from scratch” than “migrate,” so don’t overthink the initial pick.
Wrapping Up
Three projects, three architectures, three different right answers depending on your workload. The framing question isn’t “which is best” — it’s “what does your geocoding actually need to do.” Forward-only on full addresses? Nominatim. Real-time typeahead with typos? Photon. Multi-source POI database with confidence scoring? Pelias.
If you’re new to self-hosting any of them, start with Nominatim. The path from there to the other two is well-documented; the reverse path is not.
Related posts
- Nominatim: Self-Hosted Geocoding — install walkthrough
- Reverse Geocoding for Home Assistant — privacy-friendly device tracking
- Nominatim Hardware Sizing — RAM and disk math, planet vs region
- Full Self-Hosted Maps Stack — Nominatim + PostGIS + tiles