Why Monolithic GIS Servers Are Failing Modern Applications #
For almost two decades, Java-based mapping servers like GeoServer and MapServer were the unchallenged backbone of web mapping infrastructure. GeoServer was originally created in 2001 to act as a universal spatial translator—consuming raw PostGIS data and publishing it over OGC standards like WMS and WFS. It worked. But "it works" and "it scales" are very different things.
The core problem is architectural: every map pan, every zoom level change, every tile request must travel all the way to a centralized application server, spin up JVM threads, negotiate a database connection, execute spatial queries, serialize geometries, and return a payload—all in real time. Under high concurrent load, this pipeline becomes a catastrophic bottleneck.
OutOfMemoryError—taking
your entire mapping infrastructure offline instantly.
The JVM Tuning Nightmare
GeoServer infrastructure teams live in a perpetual arms race against the Java Virtual Machine. Every production deployment requires meticulous heap allocation, garbage collection tuning, and I/O disk cache management for JAI (Java Advanced Imaging) libraries. This inherently ties the compute node to expensive local block storage—directly undermining the elasticity that modern cloud architecture promises.
| 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 |
| -XX:NewRatio | Old vs new generation heap ratio | Must be tuned per-workload for GC stability |
The deepest issue? For map data that doesn't change minute-to-minute—municipal boundaries, road networks, flood zones—running a dynamic compute layer is pure architectural waste. That realization is the seed of the modern cloud-native geospatial movement.
Cloud-Native Geospatial Formats: The Foundation #
The paradigm shift doesn't just replace one server with a faster one. It removes the server entirely for the majority of use cases, leveraging a new class of cloud-native file formats engineered around one key HTTP primitive: Range Requests.
Instead of querying a middleware layer, these formats allow clients to request only the exact bytes they need from a static file sitting in object storage—no running process required.
Cloud Optimized GeoTIFF (COG) established this methodology for raster data, using internal image pyramids to let clients fetch only the pixels needed for the current viewport. GeoParquet brings the same efficiency to vector analytics via columnar storage with native spatial indexing. FlatGeobuf enables bounding-box-filtered vector streaming directly over HTTP. Together, they prove that intelligent file structure can replace entire tiers of middleware.
PMTiles: The Gold Standard for Static Map Delivery #
For spatial data with update cycles measured in days, weeks, or months—parcel boundaries, road networks, flood zones—PMTiles by Protomaps is the definitive answer. It's a single-file binary archive that stores an entire multi-scale tile pyramid, served directly to browsers via HTTP Range Requests with zero server-side compute.
"PMTiles resolves the MBTiles friction by arranging tiles into an internally structured, read-only contiguous byte sequence navigable using standard HTTP headers—no running database engine required."
PMTiles v3: A Quantum Leap in Efficiency
The v3 specification wasn't just an incremental improvement—it was an architectural rethink of how tiles are addressed, stored, and fetched.
-
01Hilbert Curve Spatial AddressingInstead of Z/X/Y coordinates, v3 uses a 64-bit Tile ID derived from a Hilbert curve—a fractal space-filling mathematical construct. Geographically adjacent tiles become sequentially adjacent bytes, dramatically improving CDN cache hit ratios on any given viewport.
-
02Run-Length Encoding for RedundancyThe physical world is full of redundant empty space. Ocean tiles, desert tiles—millions of identical blobs. PMTiles v3's 4-field entry record (TileId, RunLength, Offset, Length) deduplicates these by pointing multiple consecutive IDs at the same byte offset, massively reducing archive footprint.
-
0397% HTTP Overhead ReductionThe initial request overhead dropped from 512 KB in v2 to just 16 KB in v3. Compressed directories now use roughly 10% of their previous space. Metadata limits were removed entirely, enabling rich analytical statistics in the archive itself.
| Feature | PMTiles v2 | PMTiles v3 | Architectural Impact |
|---|---|---|---|
| Tile Addressing | 17-byte Z/X/Y | 64-bit Hilbert Curve ID | Maximizes spatial locality for sequential byte reads |
| Initial HTTP Overhead | 512 KB | 16 KB | 97% reduction → dramatically faster first paint |
| Deduplication | Multiple entries → same offset | Run-Length Encoding | Substantially compresses sparse physical features |
| Metadata Limit | ~300 KB hard cap | Unlimited JSON | Enables rich analytical statistics and layer schemas |
| Directory Size | Standard entries | Compressed (10% of v2) | Smaller network payload during heavy panning/zooming |
Infrastructure Economics: Escaping the Egress Trap #
PMTiles' technical brilliance only matters if you can serve it economically at scale. This is where the choice of object storage provider becomes the most important infrastructure decision you'll make.
Traditional hyperscale providers—AWS S3 and Google Cloud Storage—have negligible storage costs but impose severe egress fees on data leaving their networks. A high-traffic mapping application executing millions of HTTP GET Range Requests per day can generate billing that rapidly bankrupts a project.
Case Study: Sakay — 90% Cost Reduction in Production
The Sakay mapping application's engineering team executed a three-phase migration that became a widely cited proof point for the PMTiles/Cloudflare approach:
-
01Custom Basemap GenerationDownloaded raw OpenStreetMap
.osm.pbfextracts for the Philippines, processed with Tilemaker on WSL2 into semantic layers (admin boundaries, highway networks), output as an.mbtilesintermediate. -
02MBTiles → PMTiles ConversionUsed the
go-pmtilesCLI to convert the SQLite-based archive into a single optimized.pmtilesfile, ready for edge deployment. -
03Cloudflare R2 + Worker DeploymentTransferred the archive via
rcloneto a Cloudflare R2 bucket, deployed a lightweight Cloudflare Worker to intercept HTTP requests and proxy byte-range fetches—enabling Range Request support across Cloudflare's global edge network.
Dynamic Data: Rust-Powered Vector Tile Microservices #
PMTiles is the apex for static and periodically updated data. But not everything is static. Live parcel boundary edits, real-time flood sensor telemetry, continuous vehicle tracking, dynamic spatial aggregations—these require a live database connection and on-demand tile generation.
The engineering objective here shifts from eliminating the server to stripping it to its absolute minimum viable footprint: accept HTTP request, translate to optimized SQL, fetch the binary MVT payload from PostgreSQL, stream it back. Nothing more.
pg_tileserv: The Pioneer (Now Unmaintained)
Crunchy Data's pg_tileserv proved the Go microservice model was viable. It pioneered automated discovery—scanning PostGIS catalogs on startup to expose geometry columns as RESTful tile endpoints without configuration. It also enabled parameterized PL/pgSQL tile sources, passing dynamic URL parameters directly into the SQL execution layer.
But as of 2025, pg_tileserv is officially unmaintained. It supports only a single database source simultaneously, lacks asset generation capabilities, and hits performance ceilings under high concurrent workloads. It's a historical landmark, not a production choice.
Martin: The Undisputed Performance Leader
Martin, backed by the MapLibre project and engineered entirely in Rust,
is the current gold standard for dynamic vector tile serving. It uses the
actix-web framework—one of the fastest HTTP server frameworks in existence—combined
with Rust's zero-cost abstractions, strict memory safety, and the moka async
cache crate. Independent benchmarks consistently show Martin outperforming pg_tileserv,
Tegola, and every Java-based alternative across latency and throughput.
-
01Multi-Source AggregationConnects to multiple Postgres databases simultaneously. Natively serves static PMTiles and MBTiles from disk or over HTTP. Composes disparate sources into composite vector tile layers for the client.
-
02On-the-Fly Asset GenerationNatively generates map styles, multi-resolution sprites, and font glyphs—consolidating the entire asset chain of a modern MapLibre GL JS application into a single binary deployment.
-
03Native Serverless ReadinessRust's instantaneous startup and tiny binary footprint make Martin ideal for AWS Lambda, completely avoiding the catastrophic cold-start penalties that make Java applications unviable in serverless environments.
-
04martin-cp Bulk GenerationThe companion
martin-cputility queries live PostGIS instances and copies tile ranges directly to portable.mbtilesarchives, bridging the gap between dynamic compute and static storage optimization.
| Feature | GeoServer | pg_tileserv | Martin |
|---|---|---|---|
| Language | Java | Go | Rust |
| Memory Footprint | Massive (GBs of JVM Heap) | Low | Ultra-Low (near-zero bloat) |
| Maintenance Status | Active (legacy focus) | Unmaintained (2025) | Highly Active (MapLibre-backed) |
| Database Support | Expansive via JDBC plugins | Single PostgreSQL only | Multiple simultaneous databases |
| Static Format Serving | Minimal (requires extensions) | None natively | Native PMTiles & MBTiles |
| Serverless Viability | Unviable (cold-start penalties) | Yes (thin binary) | Highly Optimized (AWS Lambda native) |
PostGIS Optimization: Pushing the Database to its Limits #
A lean Martin microservice only shifts the bottleneck—from middleware to database. The true
performance ceiling of your vector tile stack is PostGIS.
The ST_AsMVT() and ST_AsMVTGeom() functions, introduced in
PostGIS 2.5, moved the entire MVT encoding pipeline inside the database kernel. No more
WKB transport to Mapnik middleware.
The ST_AsMVT Pipeline
-- Optimized CTE for vector tile generation
WITH mvt_geom AS (
SELECT
ST_AsMVTGeom(
geom,
ST_TileEnvelope(z, x, y), -- tile bounding box
4096, -- grid extent
64, -- buffer pixels
true -- clip geometries
) AS geom,
id, name, category
FROM parcels
WHERE geom && ST_TileEnvelope(z, x, y) -- bounding box pre-filter (uses spatial index)
)
SELECT ST_AsMVT(mvt_geom.*, 'parcels') -- aggregate into Protocol Buffer binary
FROM mvt_geom
WHERE mvt_geom.geom IS NOT NULL;
SQL / PostGIS
ST_AsMVTGeom reprojects to Web Mercator (EPSG:3857), clips against the tile
bounding box, and snaps vertices to a 4096-unit grid. ST_AsMVT then delta-encodes
coordinates for maximum compression and serializes to the Protocol Buffer binary format.
CARTO's Optimization Findings: 87% Faster Polygons
CARTO's engineering team profiled ST_AsMVTGeom deeply and found that the
function was performing redundant polygon validity checks before grid snapping—computationally
prohibitive and mathematically unnecessary. By disabling deep validity tests and applying
vertex simplification before coordinate translation:
| Geometry Type | Feature Count | Standard PostGIS | Optimized PostGIS | Improvement |
|---|---|---|---|---|
| Complex Polygons | 5,145 | 5,299 ms | 687 ms | 87% Faster |
| Line Strings | 49,846 | 350 ms | 290 ms | 17% Faster |
| Points | 489,446 | 2,574 ms | 1,864 ms | 28% Faster |
Spatial Indexing Strategy
Without the right indexes, none of the above matters. Three rules govern PostGIS indexing for vector tile workloads:
Every tile query must use the && (bounding box overlap) operator
before any expensive geometric operations. This forces the query planner to use the spatial
index and discard irrelevant rows before touching ST_Intersects or
ST_Contains.
ST_Subdivide: Fixing the "Giant Polygon" Problem
A single highly detailed polygon—a national boundary, a vast forest footprint—can contain hundreds of thousands of vertices. Its bounding box covers enormous areas, triggering thousands of false-positive index reads on every tile request.
The solution is Geometry Subdivision during the ETL pipeline:
-- ETL step: subdivide large polygons before loading
INSERT INTO parcels_subdivided (geom, id, name)
SELECT ST_Subdivide(geom, 256), id, name -- max 256 vertices per chunk
FROM parcels_raw
WHERE ST_NPoints(geom) > 256;
SQL / PostGIS
This fractures massive geometries into small, index-friendly chunks. The database then retrieves only the specific fragment physically intersecting the client's current viewport— radically reducing I/O load and serialization overhead.
Multi-Tiered Caching: The Final Performance Layer #
Even a perfectly optimized PostGIS database shouldn't be generating every tile from scratch
on every request. Vector tile URLs are deterministic (/{z}/{x}/{y}.pbf)—
making them ideal cache targets. Modern architectures deploy caching in two layers.
Deploying Nginx immediately in front of Martin provides a high-throughput caching tier resilient to traffic spikes. Repeated tile requests are served from disk cache without ever touching the Rust runtime or Postgres connection pool. Nginx's disk-based cache is memory-efficient, making horizontal scaling trivial.
Layer 2: Redis / Martin's Internal moka Cache
For applications requiring precise cache invalidation—live fleet tracking, incident response
maps, real-time edits—Redis provides TTL control per key. Martin's internally integrated
moka async cache framework provides similar benefits without the operational
overhead of managing an external Redis cluster, making it the preferred choice for lean teams.
The Zoom-Level Caching Paradox
One of the subtler challenges in vector tile architecture is the non-linear cost relationship between zoom level and tile generation complexity:
| Zoom Level | Geographic Extent | Generation Cost | Cache Strategy |
|---|---|---|---|
| z=0–5 (Low) | Continental / Global | Extremely High | Mandatory: pre-bake with long TTL |
| z=6–12 (Mid) | Regional / State | Moderate | Standard Nginx/Redis caching |
| z>13 (High) | City Block / Local | Extremely Low | Bypass cache: fetch direct from PostGIS |
Putting It Together: The Modern Geospatial Stack #
The modern cloud-native geospatial architecture is not a single technology—it's a deliberate composition of purpose-built tools across the entire pipeline:
-
→Static / Periodic Data → PMTiles on Cloudflare R2Eliminate the server entirely. Zero egress fees. Global CDN edge delivery. Automated OSM-to-PMTiles pipelines for freshness.
-
→Dynamic / Live Data → Martin on PostGISUltra-lean Rust microservice. Multi-source aggregation. Native serverless deployment. No JVM, no heap tuning, no cold-start hell.
-
→Database Layer → Optimized PostGISST_AsMVT pipelines with proper
&&predicates, GiST/SP-GiST indexes, and ST_Subdivide preprocessing. CARTO-style validity optimization where needed. -
→Delivery → Zoom-Aware Multi-Tiered CachingNginx proxy cache at the edge. Redis or moka for dynamic TTL invalidation. Pre-baked low-zoom tiles. Direct PostGIS for high-zoom locality.
The era of provisioning multi-gigabyte Java application servers to serve tiles that haven't changed since last Tuesday is over. The geospatial engineering community has found a better way—and it's cheaper, faster, and dramatically more scalable.
Ready to Migrate Your Mapping Stack?
InfryneTechWorks builds cloud-native geospatial infrastructure for organizations moving off legacy GIS servers. PMTiles deployment, Martin configuration, PostGIS optimization, and CDN architecture—we've done it.
Talk to Our Engineers More Articles