Architecture
This document provides a high-level overview of Martin's architecture, describing its major components, how they interact, and the rationale behind key design decisions.
Overview
Martin is a blazing fast tile server written in Rust that generates and serves vector tiles on the fly from multiple data sources. It is designed to handle heavy traffic and optimize for speed while maintaining a clean separation of concerns.
graph TB
Client[Map Client<br/>MapLibre, Leaflet, etc.]
subgraph Martin["Martin Tile Server"]
CLI[CLI Entry Point<br/>martin binary]
Server[HTTP Server<br/>Actix-Web]
subgraph Sources["Tile Sources"]
PG[PostgreSQL<br/>Tables & Functions]
MBT[MBTiles Files]
PMT[PMTiles Files<br/>Local & Remote]
COG[Cloud Optimized<br/>GeoTIFF]
end
subgraph Resources["Supporting Resources"]
Sprites[Sprite Generation<br/>SVG to PNG]
Fonts[Font Glyphs<br/>PBF Format]
Styles[MapLibre Styles<br/>JSON]
end
Catalog[Tile Catalog<br/>Source Registry]
Cache[Tile/Resources Cache<br/>Moka]
end
subgraph Storage["Data Storage"]
DB[(PostgreSQL<br/>PostGIS)]
Files[File System<br/>MBTiles/PMTiles]
S3[Object Storage<br/>S3/Azure/GCP]
end
Client -->|HTTP Requests| Server
Server --> Catalog
Catalog --> Sources
Server --> Resources
Server --> Cache
PG --> DB
MBT --> Files
PMT --> Files
PMT --> S3
COG --> Files
COG --> S3
Cache -.->|Cached Tiles/Resources| Client
Core Components
Martin's architecture is organized into four main Rust crates, each with distinct responsibilities:
Purpose: The main tile server binary and HTTP service layer.
Location: /martin
Key Responsibilities:
- HTTP server using Actix-Web framework
- Request routing and endpoint handling
- Configuration parsing (CLI args, env vars, config files)
- Tile source discovery and initialization
- Serving the Web UI for tile inspection
Main Modules:
src/bin/martin.rs- Server entry pointsrc/bin/martin-cp.rs- Bulk tile copying toolsrc/srv/- HTTP service handlersserver.rs- Main server setup and routingtiles/- Tile serving endpointsfonts.rs- Font glyph servingsprites.rs- Sprite servingstyles.rs- Style servingsrc/config/- Configuration managementargs/- CLI argument parsingfile/- Config file parsingmartin-ui/- React-based web interface
Purpose: Core abstractions and implementations for tile sources and supporting resources.
Location: /martin-core
Key Responsibilities:
- Abstract tile source traits and implementations
- PostgreSQL connection pooling and query execution
- MBTiles and PMTiles reading
- Cloud Optimized GeoTIFF (COG) tile extraction
- Sprite, font, and style resource generation
- Tile format handling (MVT protocol buffers)
Main Modules:
src/tiles/- Tile source implementationspostgres/- PostGIS table and function sourcesmbtiles/- MBTiles file sourcepmtiles/- PMTiles file sourcecog/- Cloud Optimized GeoTIFF sourcecatalog.rs- Source registry and managementsrc/resources/- Supporting resourcessprites/- SVG sprite generationfonts/- Font glyph generationstyles/- MapLibre style handling
Purpose: MBTiles format support and manipulation tools.
Location: /mbtiles
Key Responsibilities:
- SQLite-based MBTiles reading and writing
- Metadata management
- Tile compression (gzip, brotli)
- Validation and integrity checking
- Diff/patch operations between MBTiles files
- Schema management
Main Modules:
src/lib.rs- Core MBTiles librarysrc/bin/mbtiles.rs- CLI toolsql/- SQL schema and migrations
Purpose: Low-level tile manipulation and conversion utilities.
Location: /martin-tile-utils
Key Responsibilities:
- Tile coordinate system conversions
- Tile encoding/decoding
- Tile format utilities
- Bounding box calculations
Data Flow
sequenceDiagram
participant Client
participant Server as HTTP Server
participant Catalog
participant Cache
participant Source as Tile Source
participant DB as Data Store
Client->>Server: GET /source_id/z/x/y
Server->>Catalog: Resolve source_id
Catalog-->>Server: Source reference
Server->>Cache: Check cache
alt Tile in cache
Cache-->>Server: Cached tile
Server-->>Client: 200 OK (tile data)
else Tile not in cache
Server->>Source: Get tile(z, x, y)
Source->>DB: Query data
DB-->>Source: Raw data
Source->>Source: Generate MVT
Source-->>Server: Tile data
Server->>Cache: Store tile
Server-->>Client: 200 OK (tile data)
end
sequenceDiagram
participant CLI
participant Config as Config Parser
participant Discovery as Source Discovery
participant Sources as Tile Sources
participant Server as HTTP Server
CLI->>Config: Parse args & config file
Config->>Discovery: Initialize sources
alt PostgreSQL Source
Discovery->>Discovery: Connect to database
Discovery->>Discovery: Query tables & functions
Discovery->>Sources: Register table sources
Discovery->>Sources: Register function sources
end
alt File Sources
Discovery->>Discovery: Scan MBTiles files
Discovery->>Discovery: Scan PMTiles files
Discovery->>Sources: Register file sources
end
Sources-->>Server: Source catalog
Server->>Server: Setup routes
Server->>Server: Start HTTP listener
sequenceDiagram
participant Client
participant Server
participant Sprite as Sprite Generator
participant Font as Font Generator
participant FS as File System
Client->>Server: GET /sprite/sprite_id
Server->>Sprite: Generate sprite
Sprite->>FS: Read SVG files
FS-->>Sprite: SVG data
Sprite->>Sprite: Render to PNG
Sprite->>Sprite: Generate JSON index
Sprite-->>Server: Sprite sheet
Server-->>Client: PNG/JSON response
Client->>Server: GET /font/fontstack/range
Server->>Font: Generate glyphs
Font->>FS: Read font files
FS-->>Font: Font data
Font->>Font: Rasterize glyphs
Font->>Font: Encode as PBF
Font-->>Server: Glyph data
Server-->>Client: PBF response
Key Design Decisions
🧠This section provides background and context, not required knowledge. You don’t need to understand or remember all of this to use or contribute to Martin. Read it when you’re curious why certain choices were made.
Why Rust
Martin is written in Rust to balance high performance with strong safety guarantees.
- Near-C performance without manual memory management
- Memory safety (no null pointers or buffer overflows)
- Safe concurrency without data races
- Zero-cost abstractions that compile to efficient code
Actix-Web as the chosen web framework
It offers a fast, production-ready async HTTP stack.
- High-performance async request handling
- Mature middleware ecosystem
- Built-in compression and caching headers
- Easy Prometheus metrics integration
Async-first architecture
Allows Martin to handle many concurrent requests efficiently.
- Handles hundreds of thousdands of concurrent connections
- Avoids blocking database queries
- Enables efficient file and network I/O
- Keeps thread usage low under load
Specifc crates splitting
The codebase is split into crates with clear responsibilities
- martin-core — reusable core logic and tile sources
- mbtiles — standalone MBTiles tooling
- martin — HTTP server, configuration, and runtime wiring
- martin-tile-utils — shared low-level tile utilities
This makes it easier to:
- Embed Martin as a library in other Rust projects
- Use MBTiles tools independently
- Maintain clear API boundaries and versioning
Why connection pooling
Reuse database connections instead of reconnecting per request
- Uses
deadpool-postgres - Avoids per-request connection overhead
- Configurable pool sizing
- Automatic connection health checks
In-Memory Tile Caching
Avoid regenerating frequently requested tiles.
- Fast LRU cache with optional TTLs
- Automatic eviction of least-used entries
- Configurable memory limits (default: 512 MB)
- Thread-safe concurrent access
- Significant performance improvements for repeated requests
Automatic Source Discovery
Martin tries to work out of the box with minimal configuration. It can currently automatically detect:
- PostgreSQL tables with geometry columns
- PostgreSQL functions that return MVT
- MBTiles and PMTiles files in configured directories
This keeps common setups close to zero-config.
Multi-Protocol Tile Support
Different workloads benefit from different storage models. This lets operators pick the best format for their use case:
Dynamic tiles generated from live data. Best for frequently changing datasets.
Pre-generated tile archives. Simple and fast for static datasets.
Single-file, cloud-native archives optimized for HTTP range requests.
Direct tile serving from Cloud Optimized GeoTIFFs.
Why generate resources (sprites, fonts) dynamically
Sprites, fonts, and styles are created on demand.
- No pre-processing step required
- Simpler deployments (just provide source files)
- URL-based customization
- Less storage overhead
Why layered configuration
Martin supports multiple configuration sources.
- CLI flags for quick testing and overrides
- Environment variables for containerized deployments
- Config files for larger or more complex setups
- Clear precedence between configuration layers
Component Interactions
graph TB
Martin[Martin Server]
Pool[Connection Pool<br/>deadpool-postgres]
subgraph PostgreSQL
Tables[Tables with<br/>Geometry Columns]
Functions[MVT Functions]
PostGIS[PostGIS Extension]
end
Martin --> Pool
Pool --> Tables
Pool --> Functions
Tables --> PostGIS
Functions --> PostGIS
How it works:
- Martin connects to PostgreSQL using connection string
- Queries
geometry_columnsview to discover tables - Queries
pg_procto discover MVT-returning functions - Maintains connection pool for efficient query execution
- Generates tile SQL queries with bbox parameters
- Returns results as MVT tiles
graph TB
Martin[Martin Server]
subgraph "Local Files"
MBT[MBTiles<br/>SQLite]
PMT[PMTiles<br/>Binary Format]
COG[GeoTIFF<br/>Cloud Optimized]
end
subgraph "Remote Files"
S3MBT[S3/Azure/GCP<br/>MBTiles]
S3PMT[S3/Azure/GCP<br/>PMTiles]
S3COG[S3/Azure/GCP<br/>GeoTIFF]
end
Martin --> MBT
Martin --> PMT
Martin --> COG
Martin --> S3MBT
Martin --> S3PMT
Martin --> S3COG
How it works:
- Martin scans configured directories for tile files
- Opens MBTiles with SQLite (using
sqlx) - Opens PMTiles with custom parser (HTTP range requests for remote)
- Opens COG with TIFF parser (HTTP range requests for remote)
- Uses
object_storecrate for S3/Azure/GCP access - Serves tiles directly from file format
Deployment Patterns
Martin supports multiple deployment patterns:
- Single binary with embedded WebUI
- Direct PostgreSQL connection
- Local file serving
- Suitable for small to medium deployments
- Docker image with all dependencies
- Configuration via environment variables
- Health check endpoints
- Suitable for Kubernetes and container orchestrators
- Lambda adapter for Actix-Web
- Cold start optimization
- Stateless operation
- Suitable for sporadic traffic
- NGINX or Apache fronting Martin
- Additional caching layer
- SSL termination
- Load balancing across multiple Martin instances
Performance Characteristics
- Bottleneck: Complex geometry queries on large tables
- Optimization: Spatial indexes (GIST), connection pooling, query tuning
- Bottleneck: MVT encoding CPU time
- Optimization: Tile caching, pre-generated MBTiles for static data
- Bottleneck: High tile request rate
- Optimization: Async I/O, connection keep-alive, compression
- Bottleneck: Large tile cache size
- Optimization: LRU eviction, configurable cache size, streaming responses
- Increase CPU for faster tile generation
- Increase memory for larger tile cache
- Faster disk I/O for file sources
- Run multiple Martin instances behind load balancer
- Each instance maintains its own cache
- Shared PostgreSQL database with connection pooling
- CDN for tile distribution
Security considerations
- All tile coordinates validated (z/x/y bounds)
- SQL injection prevention through parameterized queries
- Path traversal prevention for file sources
- URL parsing with strict validation
- Read-only database user recommended
- Connection string security (avoid logging)
- SSL/TLS support for PostgreSQL connections
- Certificate validation for secure connections
- CORS configuration for cross-origin requests
- Rate limiting (via reverse proxy)
- Authentication/authorization (via reverse proxy)
- HTTPS termination (via reverse proxy)
Extensibility Points
To add a new tile source type:
- Implement the
Sourcetrait inmartin-core - Add configuration parsing in
martin - Register source in the catalog
- Add integration tests
Example source types that could be added:
- Direct GeoJSON file serving
- Vector tile rendering from raster data
- Integration with other spatial databases
To add new resource endpoints:
- Implement resource generator in
martin-core - Add HTTP handler in
martin/src/srv/ - Add configuration support
- Update catalog/discovery
Martin doesn't include built-in auth, but supports:
- Reverse proxy authentication (recommended)
- Custom Actix-Web middleware
- Token-based access control via proxy
Operations considerations
These are the current monitoring and observability options:
Martin exposes Prometheus metrics via /_/metrics:
- HTTP request counters and histograms
If you need more in-depth observability, we would be happy to review PRs that add additional metrics; the current implementation is intentionally minimal.
We allow health checks via /health and /catalog endpoints:
/health- Basic health check (HTTP 200)/catalog- Source availability check
- logging via
env_logger - Configurable log levels
- Request/response logging
Related Documentation
- Development Guide - Contributing to Martin
- Configuration File - Detailed configuration options
- API Documentation - HTTP API reference
- Sources Documentation - Tile source configuration