This guide covers running tileserver-rs in Docker containers for development and production deployments.
docker run -p 8080:8080 \
-v $(pwd)/data:/data:ro \
-v $(pwd)/config.toml:/app/config.toml:ro \
ghcr.io/vinayakkulkarni/tileserver-rs:latest
Official images are published to GitHub Container Registry:
| Tag | Description |
|---|---|
latest | Latest stable release |
vX.Y.Z | Specific version (e.g., v0.2.1) |
main | Latest build from main branch |
# Pull latest stable
docker pull ghcr.io/vinayakkulkarni/tileserver-rs:latest
# Pull specific version
docker pull ghcr.io/vinayakkulkarni/tileserver-rs:v0.2.1
my-tileserver/
├── config.toml
├── data/
│ ├── tiles/
│ │ └── world.pmtiles
│ ├── styles/
│ │ └── bright/
│ │ ├── style.json
│ │ ├── sprite.json
│ │ └── sprite.png
│ └── fonts/
│ └── Noto Sans Regular/
│ ├── 0-255.pbf
│ └── ...
└── compose.yml
# config.toml
fonts = "/data/fonts"
[server]
host = "0.0.0.0"
port = 8080
[[sources]]
id = "world"
type = "pmtiles"
path = "/data/tiles/world.pmtiles"
name = "World Map"
[[styles]]
id = "bright"
path = "/data/styles/bright/style.json"
docker run -d \
--name tileserver \
-p 8080:8080 \
-v $(pwd)/data:/data:ro \
-v $(pwd)/config.toml:/app/config.toml:ro \
-e RUST_LOG=info \
ghcr.io/vinayakkulkarni/tileserver-rs:latest
# compose.yml
services:
tileserver:
image: ghcr.io/vinayakkulkarni/tileserver-rs:latest
ports:
- "8080:8080"
volumes:
- ./data:/data:ro
- ./config.toml:/app/config.toml:ro
environment:
- RUST_LOG=debug
restart: unless-stopped
docker compose up -d
docker compose logs -f
# compose.prod.yml
services:
tileserver:
image: ghcr.io/vinayakkulkarni/tileserver-rs:latest
ports:
- "8080:8080"
volumes:
- ./data:/data:ro
- ./config.toml:/app/config.toml:ro
environment:
- RUST_LOG=info
restart: unless-stopped
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '0.5'
memory: 256M
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
docker compose -f compose.prod.yml up -d
# compose.yml
services:
tileserver:
image: ghcr.io/vinayakkulkarni/tileserver-rs:latest
volumes:
- ./data:/data:ro
- ./config.toml:/app/config.toml:ro
environment:
- RUST_LOG=info
labels:
- "traefik.enable=true"
- "traefik.http.routers.tiles.rule=Host(`tiles.example.com`)"
- "traefik.http.routers.tiles.tls=true"
- "traefik.http.routers.tiles.tls.certresolver=letsencrypt"
- "traefik.http.services.tiles.loadbalancer.server.port=8080"
networks:
- traefik
traefik:
image: traefik:v3.0
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik:/etc/traefik
networks:
- traefik
networks:
traefik:
external: true
# compose.yml
services:
tileserver:
image: ghcr.io/vinayakkulkarni/tileserver-rs:latest
volumes:
- ./data:/data:ro
- ./config.toml:/app/config.toml:ro
environment:
- RUST_LOG=info
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- tileserver
# nginx.conf
events {
worker_connections 1024;
}
http {
proxy_cache_path /tmp/tiles levels=1:2 keys_zone=tiles:10m max_size=1g inactive=7d;
upstream tileserver {
server tileserver:8080;
keepalive 32;
}
server {
listen 80;
server_name tiles.example.com;
# Vector tiles - cache for 7 days
location ~ ^/data/.*\.(pbf|mvt)$ {
proxy_pass http://tileserver;
proxy_cache tiles;
proxy_cache_valid 200 7d;
proxy_cache_valid 204 1h;
add_header X-Cache-Status $upstream_cache_status;
}
# Static images - cache for 1 hour
location ~ ^/styles/.*/static/ {
proxy_pass http://tileserver;
proxy_cache tiles;
proxy_cache_valid 200 1h;
}
# Everything else
location / {
proxy_pass http://tileserver;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
# Clone repository
git clone https://github.com/vinayakkulkarni/tileserver-rs.git
cd tileserver-rs
# Build for your platform
docker build -t tileserver-rs:local .
# Build with specific target
docker build --platform linux/amd64 -t tileserver-rs:amd64 .
docker build --platform linux/arm64 -t tileserver-rs:arm64 .
# Create builder
docker buildx create --name multiplatform --use
# Build for multiple platforms
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t myregistry/tileserver-rs:latest \
--push .
# Dockerfile.custom
FROM ghcr.io/vinayakkulkarni/tileserver-rs:latest
# Add your tiles and config
COPY data/ /data/
COPY config.toml /app/config.toml
# Set environment
ENV RUST_LOG=info
docker build -f Dockerfile.custom -t my-tileserver:latest .
docker run -p 8080:8080 my-tileserver:latest
| Variable | Description | Default |
|---|---|---|
RUST_LOG | Log level (error, warn, info, debug, trace) | info |
CONFIG_PATH | Path to config file inside container | /app/config.toml |
environment:
- RUST_LOG=debug # More verbose logging
- RUST_LOG=tileserver_rs=debug,tower_http=info # Selective logging
| Host Path | Container Path | Description |
|---|---|---|
./data | /data | Tile files, styles, fonts |
./config.toml | /app/config.toml | Configuration file |
:ro) in production to prevent accidental modifications.The /health endpoint returns OK when the server is ready:
# Check health
curl http://localhost:8080/health
# Docker health check
docker inspect --format='{{.State.Health.Status}}' tileserver
tileserver-rs is memory-efficient, but large tile files may need more memory:
| Tile File Size | Recommended Memory |
|---|---|
| < 1 GB | 256 MB |
| 1-10 GB | 512 MB - 1 GB |
| > 10 GB | 2 GB+ |
For high-throughput scenarios:
| Use Case | Recommended CPUs |
|---|---|
| Development | 0.5 |
| Light production | 1 |
| High traffic | 2-4 |
| Static image rendering | 4+ |
Run multiple instances behind a load balancer:
# compose.yml
services:
tileserver:
image: ghcr.io/vinayakkulkarni/tileserver-rs:latest
volumes:
- ./data:/data:ro
- ./config.toml:/app/config.toml:ro
deploy:
replicas: 3
nginx:
image: nginx:alpine
ports:
- "80:80"
depends_on:
- tileserver
# Initialize swarm
docker swarm init
# Deploy stack
docker stack deploy -c compose.prod.yml tileserver
# Scale
docker service scale tileserver_tileserver=5
# Follow logs
docker compose logs -f tileserver
# Last 100 lines
docker compose logs --tail 100 tileserver
# Filter by time
docker compose logs --since 1h tileserver
environment:
# Minimal logging
- RUST_LOG=warn
# Standard logging
- RUST_LOG=info
# Debug logging
- RUST_LOG=debug
# Trace logging (very verbose)
- RUST_LOG=trace
# Component-specific
- RUST_LOG=tileserver_rs::sources=debug,tileserver_rs::render=trace
# Check logs
docker logs tileserver
# Common issues:
# - Config file not found: Check volume mounts
# - Permission denied: Check file permissions
# - Port in use: Change port mapping
# Check resource usage
docker stats tileserver
# Check container limits
docker inspect tileserver | jq '.[0].HostConfig.Memory'
# Verify mounts
docker exec tileserver ls -la /data
docker exec tileserver cat /app/config.toml
# Check paths in config match container paths