Cloud-Native Web Mapping: PMTiles, Martin & PostGIS
How PMTiles on Cloudflare R2, Rust-powered microservices, and PostGIS optimization are replacing legacy GeoServer stacks and cutting infrastructure costs by up to 90%.
Modern cloud-native GIS architectures replace monolithic Java map servers with serverless PMTiles on object storage, Rust-powered Martin tile services, and optimized PostGIS ST_AsMVT vector tile pipelines. This slashes infrastructure costs by up to 90% while delivering sub-50ms tile response times. See this architecture live in our free GeoEditor tool or consult our dedicated technology engineering for PostGIS, GeoServer Modernization, and Mapbox & MapLibre Web GIS.
For almost two decades, Java-based mapping servers like GeoServer and MapServerwere the backbone of web mapping infrastructure. They worked. But "it works" and "it scales economically" are very different things.
The core problem is architectural: every map pan, zoom, and tile request must travel to a centralized application server, spin up JVM threads, negotiate a database connection, execute spatial queries, serialize geometries, and return a payload in real time. Under heavy production load, that pipeline becomes an expensive bottleneck.
Why Monolithic GIS Servers Are Failing Modern Applications
GeoServer deployments require deliberate JVM memory configuration. Its official production guidance documents heap sizing, garbage collection, and related Java settings that add state and capacity planning to an otherwise elastic cloud deployment.
| JVM Parameter | Function | Scalability Impact |
|---|---|---|
| -Xms | Initial heap size on startup | Wastes idle cloud resources if set too high |
| -Xmx | Maximum memory allocation pool | Limits concurrent tile rendering threads |
| -XX:MaxPermSize | Class bytecode storage (PermGen) | Exhaustion causes catastrophic server crashes |
Cloud-Native Geospatial Formats: The Foundation
The paradigm shift does not just replace one server with a faster one. It removes the server entirely for the majority of use cases, leveraging a class of cloud-native file formats built around HTTP range requests.
Requires local file downloads or running database middleware to extract geometry.
Clients query only specific byte ranges directly from standard S3/R2 storage with zero compute.
The OGC Cloud Optimized GeoTIFF standard established this methodology for raster data. The GeoParquet specification brings interoperable geospatial types to columnar analytics, while FlatGeobuf documents its HTTP range-request and spatial-indexing model.
PMTiles: The Gold Standard for Static Map Delivery
For spatial data with update cycles measured in days, weeks, or months, PMTiles is the premier static delivery mechanism. The PMTiles v3 specification defines a single-file tile archive whose directories and tile payloads can be retrieved with HTTP range requests, completely bypassing tile servers.
PMTiles v3 replaces older Z/X/Y directory structures with 64-bit Hilbert Curve ordering. Geographically adjacent tiles map to contiguous byte sequences, maximizing CDN cache locality and shrinking initial directory requests to 16 KB.
Infrastructure Economics: Escaping the Egress Trap
PMTiles’ technical brilliance only matters if you can serve it economically at scale. Traditional hyperscale cloud providers charge punishing egress fees on high-traffic range queries.
Cloudflare R2 provides zero-egress fee object storage. In production case studies like Sakay, migrating map basemaps to PMTiles on Cloudflare R2 resulted in documented 90% cost reductions.
Dynamic Data: Rust-Powered Vector Tile Microservices (Martin)
When data updates in real time, static PMTiles are paired with dynamic tile servers. Martin, written in Rust and maintained under the MapLibre organization, generates vector tiles directly from PostGIS functions with zero JVM overhead.
# Martin configuration (config.yaml)
postgres:
connection_string: 'postgresql://postgres:secret@db.internal:5432/spatial_db'
auto_publish:
tables:
from_schema: 'public'
functions:
from_schema: 'public'
pmtiles:
sources:
basemap: '/data/planet.pmtiles'PostGIS Optimization: The ST_AsMVT Pipeline & Dynamic LOD
PostGIS aggregates binary Mapbox Vector Tiles using ST_AsMVT() and transforms planar geometry into screen-space tile coordinates with ST_AsMVTGeom().
However, querying full-resolution geometries at low zoom levels produces multi-megabyte MVT payloads that choke network bandwidth and exhaust client WebGL memory. Production pipelines implement Zoom-Dependent Level of Detail (LOD) using ST_SimplifyPreserveTopology() with dynamic pixel tolerance:
-- Martin Custom Function: Generates optimized vector tiles with dynamic LOD
CREATE OR REPLACE FUNCTION public.get_parcels_tile(
z integer, x integer, y integer
) RETURNS bytea AS $$
DECLARE
tile_bbox geometry;
pixel_tolerance float8;
result bytea;
BEGIN
-- Generate EPSG:3857 bounding box for current tile
tile_bbox := ST_TileEnvelope(z, x, y);
-- Calculate meters per pixel at this zoom level (Web Mercator)
pixel_tolerance := (40075016.68 / (256 * (2 ^ z))) * 0.5;
WITH mvt_data AS (
SELECT
ST_AsMVTGeom(
-- Simplify vertex density to pixel resolution; preserve topology
ST_SimplifyPreserveTopology(geom, pixel_tolerance),
tile_bbox,
4096, 64, true
) AS mvt_geom,
id,
name,
-- Omit granular property attributes at coarse zooms to minimize byte weight
CASE WHEN z >= 14 THEN parcel_type ELSE NULL END AS parcel_type,
CASE WHEN z >= 14 THEN assessed_value ELSE NULL END AS assessed_value
FROM parcels
WHERE geom && tile_bbox
-- Filter out micro-polygons smaller than 4 screen pixels at current zoom
AND (z >= 14 OR ST_Area(geom) > (pixel_tolerance * pixel_tolerance * 4))
)
SELECT ST_AsMVT(mvt_data.*, 'parcels', 4096, 'mvt_geom')
INTO result
FROM mvt_data
WHERE mvt_geom IS NOT NULL;
RETURN result;
END;
$$ LANGUAGE plpgsql STABLE PARALLEL SAFE;Architecture Comparison: Legacy vs. Cloud-Native
Modern web mapping replaces bloated monolithic servers with lightweight, single-purpose primitives:
| Metric / Feature | GeoServer (Java / WMS) | Martin (Rust / MVT) | PMTiles on Cloudflare R2 |
|---|---|---|---|
| Runtime Footprint | 2–4 GB (JVM Heap) | 20–30 MB (Native Rust) | 0 MB (Serverless Object Storage) |
| P99 Tile Latency | 180–450 ms | 8–25 ms | 15–35 ms (Edge CDN) |
| GC Stalls / Pauses | Frequent (JVM Stop-the-World) | Zero (Rust zero-cost memory) | Zero (Static Byte Ranges) |
| Data Freshness | Real-time | Real-time (PostGIS SQL) | Static / Periodic Pipeline Build |
| Cost per 10M Requests | $180–$400/mo (EC2 Clusters) | $25–$50/mo (Small VM) | $3.60/mo (R2 Class B Ops) |
Multi-Tiered Caching: The Final Performance Layer
Vector tile URLs are completely deterministic. Modern pipelines deploy multi-tier caching across CDN edges, Nginx reverse proxies, and Martin's internal memory cache.
Frequently Asked Questions
What is PMTiles v3 and how does it eliminate web mapping tile servers?
PMTiles v3 is a single-file archive format for pyramids of tiled data (vector, raster, or elevation). It uses a 127-byte header and a 64-bit Hilbert Curve addressing directory. Web clients like MapLibre GL JS issue HTTP Range requests directly to cloud object storage (e.g., Cloudflare R2 or AWS S3), fetching only the necessary 16KB directory sector and the exact tile byte slice without requiring an active tile application server.
Why is Cloudflare R2 favored over Amazon S3 for hosting PMTiles archives?
Web maps generate massive volumes of HTTP Range requests as users pan and zoom across viewports. Traditional hyperscale cloud providers charge $0.09 per GB in outbound data egress fees, which causes serverless mapping bills to escalate unpredictably. Cloudflare R2 provides S3-compatible object storage with $0 egress fees, enabling high-traffic vector tile streaming at a fraction of the cost.
How does Martin (Rust) outperform GeoServer or pg_tileserv for dynamic vector tiles?
Martin is compiled to native machine code in Rust with an asynchronous Tokio runtime, operating with a memory footprint of just 20-30 MB and zero JVM garbage collection stalls. Compared to legacy Java-based GeoServer stacks (which require 2-4 GB heap space and suffer GC pauses), Martin achieves sub-10ms P99 tile generation latencies and seamlessly multiplexes PostGIS functions with static PMTiles archives.
How do you prevent MVT tile size bloat in PostGIS queries?
To avoid generating multi-megabyte MVT tiles that crash WebGL contexts at low zoom levels, use zoom-scaled geometry simplification via ST_SimplifyPreserveTopology(geom, tolerance) where tolerance is scaled to pixel dimensions, filter out minor polygons using ST_Area(geom) < zoom_threshold, and omit granular attributes that are only needed at detailed inspection zooms.
Ready to Modernize Your Spatial Architecture?
Migrate from Legacy GIS Servers to Cloud-Native
Infryne TechWorks builds modern geospatial infrastructure for teams moving off bloated GIS servers: PMTiles deployment, Martin configuration, and PostGIS optimization.
Primary Sources & Datasets
Continue Reading
More engineering research from Infryne TechWorks
A practical guide to WebGL rendering, PMTiles, custom GLSL layers, feature state, performance tuning, Martin, and production-ready Next.js setup.
A field guide to PostGIS storefront architecture, ST_DWithin indexing, and the decisions that pull spatial systems back from the edge.