Planetiler & Tilemaker Workflows Jump to heading

Both of these tools do something a GeoJSON-based pipeline cannot: they read an OSM extract and emit a finished tile archive in a single pass, never materialising the enormous text intermediate that dominates the alternative. That is the reason to reach for them, and the reason their schema model looks so different from a generator that just consumes features.

Where the schema decision happens in a single-pass tile build A PBF extract is read once. Each element passes through a profile, which is code or a Lua function that decides whether the element belongs in the output, which layer it goes to, which attributes it carries and at which zoom levels it appears. Elements the profile accepts are written to a temporary feature store on disk. The store is then read back per tile to produce the archive. The profile is the only place any cartographic decision is expressed. One pass, and the profile makes every decision PBF extract read exactly once profile layer, attrs, zooms feature store temporary, on disk tile archive written per tile Because the profile runs per element during the single read, it must be fast and must not need to look anything up elsewhere.
Everything a GeoJSON pipeline spreads across export, ranking and generation lives in one function here.

The Problem This Topic Solves Jump to heading

You need a complete base map from an OSM extract, and the GeoJSON route’s intermediate files are the dominant cost — in disk, in time, or in both. Single-pass tools remove that intermediate entirely, which for a continent or the planet is the difference between a feasible build and an infeasible one.

The failure scenario is subtler than a crash. A team adopts a single-pass tool, copies an example profile, and gets a working map quickly. Six months later nobody can change what appears at zoom 10, because the cartographic rules are spread through a profile that was never read carefully and the tool rebuilds everything on every change. The trap is not the tool; it is treating the profile as configuration when it is the most important code in the pipeline.

Prerequisites Jump to heading

Understand the tile model from OSM Vector Tiles & Rendering Pipelines and the generalization vocabulary from Cartographic Generalization of OSM Data. Know how OSM elements become geometry, from Node, Way & Relation Data Model — a profile sees elements, not ready-made features.

The Two Tools, Honestly Compared Jump to heading

Planetiler is a Java application built for throughput. Its profile is Java code implementing an interface: a method called per element, returning the layers and attributes that element contributes. It uses memory-mapped node storage and a highly optimised feature store, and it will build a planet-wide tile set on a single large machine in hours rather than days. The cost of that speed is that the schema is a compiled artefact — changing a zoom threshold means rebuilding the profile.

Tilemaker is a C++ application whose profile is a Lua script plus a JSON layer configuration. The Lua functions are called per node and per way, and the JSON declares the layers with their zoom ranges. It is slower than Planetiler on very large inputs but far quicker to iterate on, because changing the schema is editing a script. For a regional map with a custom schema, that iteration speed usually matters more than raw throughput.

Planetiler and Tilemaker compared on the properties that decide between them A grid of five properties against the two tools. The schema lives in compiled Java for Planetiler and in a Lua script plus JSON for Tilemaker. Iteration requires a rebuild for Planetiler and only a file edit for Tilemaker. Throughput is built for planet scale for Planetiler and is comfortable at regional scale for Tilemaker. Memory demand is high for Planetiler and moderate for Tilemaker. The natural fit is a complete base map for Planetiler and a custom regional schema for Tilemaker. Two tools, and iteration speed is the real difference Planetiler Tilemaker Schema lives in compiled Java Lua plus JSON Changing it needs a rebuild a file edit Throughput planet scale regional scale Memory demand high moderate Natural fit complete base map custom schema Neither produces better tiles than the other; they differ in how quickly a cartographer can change what the tiles contain.
Choose on who will be editing the schema and how often, because that is the cost you pay every week.

Node Location Storage: the Shared Constraint Jump to heading

Both tools face the same fundamental problem as any OSM parser: building way geometry requires the coordinates of every referenced node, and there are far more nodes than features. The strategies are the ones discussed in Memory-Efficient Chunk Processing, applied at tile-building scale.

Planetiler defaults to a memory-mapped array indexed by node identifier, which is fast and demands either a lot of RAM or a fast local disk with generous page cache. Tilemaker offers a choice between an in-memory store and an on-disk one, with the on-disk option making a large region feasible on a modest machine at a throughput cost.

The practical rule is the same for both: node storage is the thing that decides whether your build fits on the machine you have. Estimate it first, before choosing zoom ranges or layers, because it is the constraint that does not negotiate.

Validation and Error-Handling Matrix Jump to heading

Condition Root cause Detection Remediation
Build exhausts memory Node store sized for a smaller region Failure during the first read pass Switch to on-disk node storage or add RAM
A layer is empty Profile never matches its condition Layer declared but no features written Log match counts per layer during the profile run
Relations missing from output Profile does not handle relation members Multipolygons absent, ways present Implement the relation handling the tool provides
Build succeeds, map is blank Layer names differ from the style Metadata declares unexpected layer names Align profile layer names with the style
Attributes inconsistent Profile writes different types per branch Client reports mixed attribute types Coerce every attribute to one type in the profile
Very slow build Profile does expensive work per element Throughput far below the tool’s baseline Move lookups out of the per-element path
Zoom ranges ignored Range declared in the wrong place Features appear outside their intended zooms Declare ranges where the tool expects them

Performance and Scale Jump to heading

Three things dominate a single-pass build.

The profile’s per-element cost. It runs hundreds of millions of times. A regular expression compiled inside it, a hash lookup against a large table, or anything touching the filesystem turns a two-hour build into a twelve-hour one. Precompute everything the profile needs into a small immutable structure before the run starts.

Node storage. As above: it is the constraint that decides feasibility rather than speed.

The feature store’s disk. Both tools write an intermediate feature store to disk and read it back per tile. That store is written once and read in a spatially sorted order, so sequential throughput matters more than latency, but there must be room for it — typically a multiple of the extract size.

Neither tool parallelises the profile across machines, so scaling is vertical. For very large builds that means one big machine for a few hours rather than a cluster, which is usually simpler and cheaper.

Where the wall-clock time goes in a typical single-pass build Five phases of a single-pass build with their approximate share of total runtime. Reading the extract and running the profile over every element takes the largest share. Writing and sorting the intermediate feature store takes the next largest. Rendering tiles from the sorted store takes a moderate share. Writing the archive takes a small share. Computing metadata takes a negligible share. A note observes that profile cost lands entirely in the first phase, which is why per-element work dominates. The first phase is where a slow profile shows up Read plus profile about 46% Sort feature store about 28% Render tiles about 18% Write archive about 7% Metadata about 1% Profile cost lands entirely in the first bar, so a profile twice as slow makes the whole build roughly half again as long.
Optimising the later phases is optimising a quarter of the build; optimising the profile is optimising half of it.

What a Profile Cannot Easily Do Jump to heading

Both tools trade flexibility for the single pass, and it is worth naming what that costs before adopting one.

It cannot look anything up. The profile sees one element at a time, with no index of what came before and no way to query a database without destroying throughput. A rule such as “render this building only if it is inside a named settlement” needs the containment computed elsewhere and attached to the element beforehand, which usually means a preprocessing pass — at which point part of the advantage of the single pass has been given back.

It cannot aggregate across features. Merging a block of adjacent buildings into one built-up polygon, as described in Merging Adjacent OSM Polygons for Low Zoom, needs all the members present at once. The tools provide limited coalescing of identical adjacent geometry at render time, but genuine cartographic aggregation is a preprocessing step.

It cannot easily be tested in isolation. A profile function depends on the host’s tag-access API, so unit-testing it means either running the tool over a small fixture extract or building a harness that fakes those functions. Running against a single-city extract is usually the pragmatic answer, and it is fast enough to sit in a change workflow.

It cannot update incrementally. A schema change means a full rebuild. That is the operational fact that makes the invalidation work in Serving & Invalidating OSM Tiles a separate concern rather than something the generator handles.

None of these is a reason to avoid single-pass tools for a base map, where the schema is stable and the input is large. They are reasons to keep thematic layers whose rules need context in a separate, GeoJSON-based pipeline, and to combine the two archives at serving time rather than forcing every layer through one tool.

Failure Modes and Gotchas Jump to heading

  • The profile is code, not configuration. It deserves tests, review and a changelog, because it is where every cartographic decision lives.
  • Per-element work is multiplied by hundreds of millions. Anything not constant-time in the profile is a build-time problem.
  • Relations need explicit handling. A profile that only implements the node and way callbacks silently drops every multipolygon.
  • Attribute types must be consistent. A branch returning a number where another returns a string produces attributes clients cannot use reliably.
  • Layer names are a contract with the style. Renaming a layer in the profile silently blanks part of the map.
  • Rebuilds are all-or-nothing. Neither tool updates a tile set incrementally, so a schema change means a full rebuild — which is why invalidation is handled separately.

Integration Points Jump to heading

Upstream, the input is a verified extract from OSM Extract Providers & Automated Downloads; no GeoJSON stage is involved. Downstream, the archive is served and invalidated exactly as a Tippecanoe-produced one is, per Serving & Invalidating OSM Tiles — the tools differ in how tiles are produced, not in what they produce.

The two guides below take each tool in turn: Running Planetiler on a Regional Extract and Writing a Tilemaker Lua Profile for OSM Tags.

Guides in This Topic Jump to heading

Frequently Asked Questions Jump to heading

Should I use a single-pass tool or a GeoJSON pipeline?

It depends on the size of the input and on who edits the schema. For a continent or the planet, the GeoJSON intermediate is often simply too large to materialise, and a single-pass tool is the only practical option. For a city or a small region with a schema a cartographer iterates on frequently, the GeoJSON route keeps each stage independently inspectable, which is worth a lot during development. Many teams use both: single-pass for the base map, GeoJSON for thematic overlays.

Why is my build so much slower than the tool's published numbers?

Almost always because the profile does expensive work per element. It runs once for every node, way and relation in the extract — hundreds of millions of times on a country file — so compiling a regular expression, allocating a collection or consulting a large lookup inside it multiplies that cost by the element count. Precompute everything into an immutable structure before the run and keep the per-element path to constant-time operations.

Why are my multipolygons missing?

Because the profile implements handling for nodes and ways but not for relations, which is the default state of a profile copied from a minimal example. Areas mapped as multipolygon relations — most large lakes, forests and complex buildings — arrive as relations and are silently dropped if nothing handles them. The symptom is a map that looks complete until you notice every large water body is absent.

Can these tools update an existing tile set incrementally?

Not on their own. Both are batch builders that produce a complete archive from an extract, so a change means a full rebuild. Keeping a tile set current after a diff is a separate concern handled by computing which tiles changed and re-rendering only those, which is why invalidation is treated as its own topic rather than as a feature of the generator.

How much disk does a single-pass build need?

Enough for the node store and the intermediate feature store on top of the output archive, which together are typically several times the extract size. The feature store is written once and read back in spatial order, so sequential throughput matters more than random latency. Size it before starting: both tools fail late, well into a build, when the disk fills.

Up one level: OSM Vector Tiles & Rendering Pipelines.