00 Introduction
GeoServer is a powerful, open-source publishing platform for spatial data. Out of the box, it provides reasonable defaults, but production workloads require deliberate tuning. This guide covers the critical levers you can pull to optimize rendering speed, cache hit rates, database throughput, and resource utilization.
ab (Apache Bench) or vegeta for consistent load testing.
01 JVM & Memory Management
Java memory configuration is the single most impactful setting for GeoServer performance. Undersized heaps cause frequent garbage collection pauses, while oversized heaps increase GC cycle duration.
export CATALINA_OPTS="-Xms2g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication -XX:+AlwaysPreTouch"
- -Xms / -Xmx: Set initial and max heap to the same value to prevent runtime resizing pauses. Start with 2:1 ratio of physical RAM to heap size.
- G1GC: Garbage-First collector offers better predictable latency for medium-to-large heaps.
- String Deduplication: Reduces memory footprint of string-heavy WMS/WFS responses.
02 Database & Data Stores
Most GeoServer bottlenecks originate at the data store level. Ensure your spatial database is indexed, query-optimized, and connection-pooled.
GEOMETRY or GEOGRAPHY columns. Enable postgis.enable_outdb_rasters for large raster datasets.
- Create
SP-GISTorBRINindexes on geometry columns - Run
VACUUM ANALYZEregularly to update planner statistics - Use HikariCP connection pooling (default in modern GeoServer) with
maxLifetime=1800000 - Disable feature info queries (
FeatureInfoFormats) when not neededβthey are expensive
03 GeoWebCache & Tile Serving
Pre-rendering tiles via GeoWebCache (GWC) drastically reduces database load and improves map load times. Configure layer caching appropriately for your use case.
<!-- Enable disk-quota cleanup thread -->
<init-param>
<param-name>disk-quota-manager-thread-interval</param-name>
<param-value>30</param-value>
</init-param>
- Use
image/webporimage/jpegfor raster tiles (better compression than PNG) - Set appropriate
Tile Size(256x256 is standard; 512x512 reduces HTTP requests) - Enable
BlobStore(S3/MinIO) for distributed tile storage and CDN integration
04 Threading & Web Container
GeoServer runs on Tomcat by default. Thread pool sizing determines concurrent request handling capacity. Too few threads cause queuing; too many cause context-switching overhead.
<Connector
protocol="HTTP/1.1"
port="8080"
maxThreads="200"
minSpareThreads="50"
acceptCount="100"
/>
For high-concurrency environments, consider switching to Jetty or Undertow for better async I/O handling. Adjust org.geotools.shapefile.cache.size in web.xml if serving large vector layers.
05 Monitoring & Profiling
You cannot optimize what you cannot measure. Implement observability to track request latency, GC frequency, cache hit ratios, and database query times.
- Prometheus + Grafana: Export JMX metrics via
jmx_prometheus_javaagent - Logging: Set
org.geotoolsandorg.geoservertoINFOin production. UseDEBUGonly for troubleshooting. - Thread Dumps: Capture during load tests using
jstack <pid>to identify blocking operations
06 Quick Optimization Checklist
Need Expert Performance Tuning?
Our engineering team specializes in high-throughput GeoServer architectures. Get a custom audit, infrastructure review, or managed deployment.
Request Architecture Review β