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 serving
src/config/- Configuration managementargs/- CLI argument parsingfile/- Config file parsing
martin-ui/- React-based web interface
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
Key Design Decisions
Note
🧠 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.
Rust for Performance and Safety
Why Rust: Martin is written in Rust to balance high performance with strong safety guarantees. (click to expand)
- 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 Framework
Why Actix-Web: It offers a fast, production-ready async HTTP stack. (click to expand)
- High-performance async request handling
- Mature middleware ecosystem
- Built-in compression and caching headers
- Easy Prometheus metrics integration
Async/Await Throughout
Why async/await: Allows Martin to handle many concurrent requests efficiently. (click to expand)
- Handles thousands of concurrent connections
- Avoids blocking database queries
- Enables efficient file and network I/O
- Keeps thread usage low under load
Crate Separation
Why multiple crates: The codebase is split into crates with clear responsibilities. (click to expand)
- 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
PostgreSQL Connection Pooling
Why connection pooling: Reuse database connections instead of reconnecting per request. (click to expand)
- Uses
deadpool-postgres - Avoids per-request connection overhead
- Configurable pool sizing
- Automatic connection health checks
In-Memory Tile Caching
Why caching: Avoid regenerating frequently requested tiles. (click to expand)
- 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
Why auto-discovery: Martin tries to work out of the box with minimal configuration. (click to expand)
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
Why multiple formats: 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.
On-the-Fly Resource Generation
Why generate resources dynamically: Sprites, fonts, and styles are created on demand. (click to expand)
- No pre-processing step required
- Simpler deployments (just provide source files)
- URL-based customization
- Less storage overhead
Modular Configuration
Why layered configuration: Martin supports multiple configuration sources. (click to expand)
- 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
Deployment Patterns
Martin supports multiple deployment patterns:
- Single binary with embedded WebUI
- Direct PostgreSQL connection
- Local file serving
- Suitable for small to medium deployments
Performance Characteristics
- Bottleneck: Complex geometry queries on large tables
- Optimization: Spatial indexes (GIST), connection pooling, query tuning
- Increase CPU for faster tile generation
- Increase memory for larger tile cache
- Faster disk I/O for file sources
- 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
Extensibility Points
Adding New Tile Sources
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
Adding New Resource Types
To add new resource endpoints:
- Implement resource generator in
martin-core - Add HTTP handler in
martin/src/srv/ - Add configuration support
- Update catalog/discovery
Custom Authentication/Authorization
Martin doesn’t include built-in auth, but supports:
- Reverse proxy authentication (recommended)
- Custom Actix-Web middleware
- Token-based access control via proxy
Monitoring and Observability
Martin exposes Prometheus metrics via /metrics:
- HTTP request counters and histograms
- Tile generation time histograms
- Cache hit/miss rates
- Database connection pool stats
- Error rates by type
Related Documentation
- Development Guide - Contributing to Martin
- Configuration File - Detailed configuration options
- API Documentation - HTTP API reference
- Sources Documentation - Tile source configuration