dobrovolskiy.com
RU
← Notes2026-08-30 · by Dmitriy Dobrovolskiy

A self-hosted OpenStreetMap stack for Russia and the CIS on a 2 GB VPS: PMTiles, Photon and a metered API

How geo.ryta.online replaces Google/Yandex map APIs: 13 Geofabrik extracts into PostGIS with osm2pgsql, a ~10 GB PMTiles vector basemap served as a static file, an off-server Photon geocoder index, and a Node/Express gateway with scoped keys, quotas and paid plans.

geo.ryta.online is a complete self-hosted geo backend for Russia plus twelve CIS neighbours: OSM extracts merged and imported into PostGIS, a ~10 GB PMTiles vector basemap served with HTTP range requests, a Photon geocoder with Russian and English autocomplete and reverse geocoding, and a small SaaS gateway with API keys, daily and monthly quotas and T-Bank billing. The trick that makes it fit on a 2 GB RAM VPS is building heavy off-server and serving light.

Why

The RYTA marketplace puts every cargo, truck and machine on a map and geocodes addresses all day. Third-party map APIs in the region are either paid per request, rate-limited, or politically fragile. Owning the stack for the whole CIS was cheaper than any of them — provided it ran on the hardware we already had: a VPS with 2 GB of RAM.

Build heavy, serve light

Everything expensive happens on a build machine; the VPS only serves files and answers small queries.

Basemap. Thirteen Geofabrik PBF extracts (Russia and twelve neighbours) are merged with osmium, imported into PostGIS with osm2pgsql — the architecture notes contain the sizing math for osm2pgsql cache versus flat-nodes and the disk multipliers — and rendered to a vector basemap with Planetiler/tippecanoe. The output is a single ~10 GB PMTiles file. PMTiles is served as a static file with HTTP range requests; no tile server process, no cache to warm, and MapLibre GL reads it directly.

Geocoding. A Photon index is built off-server from a Nominatim import and copied over: about 5.5 GB on disk and ~2 GB of heap for OpenSearch, which is the one component that actually needs memory. It gives forward search with Russian and English autocomplete and reverse geocoding.

Live tiles when needed. For layers that change, pg-map renders MVT tiles straight from PostGIS with ST_AsMVT; there is also a Martin tile-server config and a kepler.gl v3 setup over pg_featureserv that needs no Mapbox token.

The gateway: keys, quotas, money

A Node/Express gateway fronts everything as a small SaaS. Two key scopes — rgk_* for geocoding and rmk_* for maps — with daily and monthly quotas that answer HTTP 429 on overrun, a usage dashboard, an admin, and a keyless public demo. Sign-up is passwordless: the user proves a phone number by calling an 8-800 number and the Rostelecom virtual PBX confirms it through a signed webhook. Free, Starter and Business plans are paid through T-Bank.

Editing the map yourself

edit-osm is a Flask tool: draw a bounding box on your own map, cut it out with osmium, edit it in JOSM through Remote Control, merge the result back and rebuild the basemap. Custom places therefore appear both on the tiles and in search. There is also an experiment with a full OSM API database (cgimap) for a private editing workflow.

Tooling and deployment

geo-demo is a MapLibre test bench showing Photon timing statistics, per-result OSM tags, click-to-reverse-geocode and a vector-tile feature inspector. Two deployment targets — one VPS reusing an existing Photon, one main server with a private registry — are driven by dry-run-capable PowerShell scripts and Caddy host blocks. About 7 600 lines of my own code excluding the vendored pieces.

What I would do differently

Decide the tile schema before the first Planetiler run; changing attributes later means re-rendering 10 GB. And put the quota accounting in from the first day — it was added when the demo started getting real traffic, which is the wrong moment.

FAQ

Why PMTiles instead of a tile server?

A PMTiles archive is one static file that MapLibre reads with HTTP range requests. There is no tile server process, no rendering on request and nothing to cache — which is what lets a 2 GB VPS serve a whole-region basemap.

How good is the geocoding?

Photon over a Nominatim import handles Russian and English forward search with autocomplete and reverse geocoding across Russia and twelve CIS countries. Custom places added through the JOSM round-trip appear in search too.

Can you set this up for my project?

Yes. The pipeline is scripted; adapting it to your region, adding layers or wiring the gateway into your billing are hourly sessions on a shared screen.

Want something like this?

I build it live on a Zoom call, you watch the screen, the timer stops when you say stop. First 15 minutes are free.

$50 / hourBook an hour

More projects

geo.ryta.online: self-hosted maps APISelf-hosted OpenStreetMap stack for Russia/CIS: vector tiles, Photon geocoder and a metered API gateway with paid plans