A Dockerized OpenStreetMap tile server based on the Switch2OSM guide for Ubuntu 24.04.
This setup provides a complete tile server stack including:
- PostgreSQL with PostGIS - Spatial database for OSM data
- osm2pgsql - Tool to import OSM data into PostgreSQL
- Mapnik - Map rendering engine
- mod_tile - Apache module for serving tiles
- renderd - Tile rendering daemon
- OpenStreetMap Carto - Default OSM map stylesheet
- Docker
- Docker Compose
- At least 4GB RAM (8GB+ recommended for larger regions)
- Sufficient disk space for your region (varies greatly by size)
git clone https://github.com/gridcell/osm-tile-server-docker-setup.git
cd osm-tile-server-docker-setupUse the management script to download and import regions:
./manage.sh download <region-url> --import [cache-size-mb] [threads]Example - Download and Import Canada:
./manage.sh download https://download.geofabrik.de/north-america/canada-latest.osm.pbf --import
# or with custom cache and thread settings
./manage.sh download https://download.geofabrik.de/north-america/canada-latest.osm.pbf --import --cache 4096 --threads 8Recommended test regions:
- Canada: ~2.5GB (large country)
- Austria: ~600MB (medium country)
- Japan: ~1.2GB (island nation)
- Germany: ~3.5GB (large country)
Browse all available regions at: https://download.geofabrik.de/
Once import is complete, access your tile server at:
http://localhost/osm_tiles/{z}/{x}/{y}.png
Example tile URL:
http://localhost/osm_tiles/0/0/0.png
If you prefer to do steps manually:
./manage.sh startCreate a data directory and download your region:
mkdir -p osm-data
cd osm-data
wget https://download.geofabrik.de/europe/monaco-latest.osm.pbf
cd .../manage.sh import /data/monaco-latest.osm.pbfThis setup uses OpenStreetMap Carto with flex mode (osm2pgsql flex output), which is the current standard. Key components that are automatically configured:
-
Mapnik XML with Network PostgreSQL Connection - The Dockerfile automatically configures the mapnik.xml file with host, port, user, and password parameters for connecting to PostgreSQL over the network (not Unix sockets)
-
SQL Functions - Required flex mode functions (
carto_path_type, etc.) are automatically installed fromfunctions.sqlon first startup -
External Shapefiles - Water polygons, ice sheets, and other external data are automatically downloaded and imported on first startup
-
Flex Mode Import - Uses
osm2pgsql --output flexwith theopenstreetmap-carto-flex.luastyle file
Copy .env.example to .env and adjust values:
cp .env.example .envKey settings:
OSM_IMPORT_CACHE- Memory cache for import (default: 2048MB)THREADS- Number of parallel threads (default: 4)- PostgreSQL tuning parameters (adjust based on your RAM)
| RAM Available | OSM_IMPORT_CACHE | SHARED_BUFFERS | MAINTENANCE_WORK_MEM |
|---|---|---|---|
| 4GB | 1024 | 512MB | 1GB |
| 8GB | 2048 | 1GB | 4GB |
| 16GB | 4096 | 2GB | 10GB |
| 32GB+ | 8192 | 4GB | 16GB |
# All services
./manage.sh logs
# Just tile server (use docker-compose directly for specific service)
docker-compose logs -f tile-server
# Just database (use docker-compose directly for specific service)
docker-compose logs -f postgres./manage.sh status./manage.sh restart./manage.sh stop./manage.sh clean-allDuring import, you can check progress in the logs:
./manage.sh logs
# Or for just the tile server:
docker-compose logs -f tile-serverTo pre-render tiles for a specific area and zoom levels:
docker-compose exec tile-server render_list \
-m default \
-a \
-z 0 \
-Z 10 \
-x <min_x> \
-X <max_x> \
-y <min_y> \
-Y <max_y># Get a single tile
curl http://localhost/osm_tiles/0/0/0.png -o test-tile.pngCreate a simple HTML file to test your tiles with Leaflet:
<!DOCTYPE html>
<html>
<head>
<title>OSM Tile Server Test</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
#map { height: 600px; }
</style>
</head>
<body>
<div id="map"></div>
<script>
// Set coordinates to center of your region
var map = L.map('map').setView([49.6116, 6.1319], 13); // Luxembourg
L.tileLayer('http://localhost/osm_tiles/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap contributors'
}).addTo(map);
</script>
</body>
</html>For detailed troubleshooting information, see TROUBLESHOOTING.md.
Check logs:
./manage.sh logsReduce OSM_IMPORT_CACHE in your environment or docker-compose.yml:
docker-compose exec tile-server bash -c "export OSM_IMPORT_CACHE=1024 && /usr/local/bin/import-osm.sh /data/your-file.osm.pbf"- Check if data was imported:
docker-compose exec postgres psql -U renderaccount -d gis -c "SELECT COUNT(*) FROM planet_osm_point;"- Check renderd status:
docker-compose exec tile-server ps aux | grep renderd- Check Apache status:
docker-compose exec tile-server apache2ctl status- Test if tiles are being served:
curl http://localhost/osm_tiles/0/0/0.png -o test-tile.png
file test-tile.png # Should show: PNG image dataEnsure PostgreSQL is ready:
docker-compose exec postgres pg_isreadyThe SQL functions required by flex mode need to be installed. This should happen automatically on container start, but you can install them manually:
docker-compose exec tile-server bash -c "export PGPASSWORD=renderpassword && psql -h postgres -U renderaccount -d gis -f /home/renderer/src/openstreetmap-carto/functions.sql"
docker-compose restart tile-serverExternal shapefiles need to be downloaded. This should happen automatically on container start, but you can download them manually:
docker-compose exec tile-server bash -c "cd /home/renderer/src/openstreetmap-carto && export PGHOST=postgres PGPORT=5432 PGDATABASE=gis PGUSER=renderaccount PGPASSWORD=renderpassword && python3 scripts/get-external-data.py"
docker-compose restart tile-serverThis indicates a PostgreSQL connection issue. The fix is already implemented in the Dockerfile which configures mapnik.xml with network connection parameters. If you still see this:
- Rebuild the containers:
./manage.sh stop
./manage.sh rebuild- Verify the mapnik.xml has correct connection parameters:
docker-compose exec tile-server grep -A 5 "Parameter name=\"host\"" /home/renderer/src/openstreetmap-carto/mapnik.xml | head -10You should see host, port, dbname, user, and password parameters.
osm-server/
├── docker-compose.yml # Main Docker Compose configuration
├── Dockerfile # Tile server container definition
├── README.md # Main documentation (this file)
├── TROUBLESHOOTING.md # Detailed troubleshooting guide
├── QUICK_REFERENCE.md # Quick command reference
├── .env.example # Environment variables template
├── configs/
│ ├── renderd.conf # Renderd configuration
│ └── apache-tile.conf # Apache virtual host configuration
├── scripts/
│ ├── entrypoint.sh # Container startup script
│ ├── import-osm.sh # OSM data import script
│ ├── init-db.sh # Database initialization script
│ └── setup-carto.sh # Mapnik configuration script (legacy)
├── manage.sh # Management command wrapper
└── test-map.html # Interactive test page
- Use SSDs - Tile rendering is I/O intensive
- Increase RAM - More RAM = faster imports and rendering
- Pre-render tiles - Render common zoom levels in advance
- Tune PostgreSQL - Adjust settings based on your hardware
- Use smaller regions - Start with a city or small country
- Geofabrik: https://download.geofabrik.de/ (regional extracts, updated daily)
- Planet OSM: https://planet.openstreetmap.org/pbf/ (full planet file, ~70GB)
- BBBike: https://extract.bbbike.org/ (custom extracts)
To update your map data:
- Download the latest extract
- Stop the tile server
- Clear the tile cache
- Re-import the data
./manage.sh clean-tiles
./manage.sh download <your-region-url>This setup is based on open-source software:
- OpenStreetMap data: ODbL
- Software components: Various open-source licenses
Based on the Switch2OSM tutorial by the OpenStreetMap community.