Runs osmsg continuously: Postgres store, REST API, replication worker, and Caddy for HTTPS plus the static leaderboard.
| Service | Image | Role |
|---|---|---|
db |
postgis/postgis:17-3.5-alpine |
Stats store |
api |
ghcr.io/osgeonepal/osmsg-api:latest |
REST API |
worker |
ghcr.io/osgeonepal/osmsg-worker:latest |
Replication worker |
caddy |
caddy:2-alpine |
HTTPS proxy: API + leaderboard |
Frontend and API use two subdomains: OSMSG_FRONTEND_DOMAIN and OSMSG_API_DOMAIN.
Keep growing data off the root disk. Attach a block volume at /mnt/mnt; the three data volumes bind
to it. Create the directories and swap before the first start:
sudo mkdir -p /mnt/mnt/osmsg/{pgdata,cache,data,maintain/work,maintain/out}
sudo fallocate -l 4G /mnt/mnt/swapfile && sudo chmod 600 /mnt/mnt/swapfile
sudo mkswap /mnt/mnt/swapfile && sudo swapon /mnt/mnt/swapfile
echo '/mnt/mnt/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabPoint both subdomains at the server as A records. On Cloudflare, set them DNS-only (grey cloud) so
Caddy's Let's Encrypt challenge reaches the host.
osmsg.<domain> A <server-ip>
api.osmsg.<domain> A <server-ip>
cp infra/.env.example infra/.env && $EDITOR infra/.env # set the two domains + ACME email
sudo mkdir -p /opt/osmsg/infra
sudo cp infra/docker-compose.yml infra/Caddyfile infra/osmsg.service infra/.env /opt/osmsg/infra/
sudo git clone https://github.com/osgeonepal/osmsg-leaderboard.git /opt/osmsg-leaderboard
sudo cp /opt/osmsg/infra/osmsg.service /etc/systemd/system/ && sudo systemctl daemon-reload
sudo systemctl enable --now osmsgOSMSG_EXTRA_ARGS runs every tick. Do not put --last, --days, --update, or --url there: the
worker adds --update and auto-selects granularity from the gap (day to hour to minute). Pinning
--url minute over a large gap crawls tens of thousands of files and fills the disk.
Seed the last published month into Postgres, then let the worker follow live. Use the worker's store params so resume state lines up.
cd /opt/osmsg/infra && docker compose up -d db
docker compose run --rm --entrypoint osmsg worker \
--insert --start 2026-05-01 --end 2026-06-01 \
--name stats --output-dir /var/lib/osmsg --cache-dir /var/cache/osmsg \
--format psql --psql-dsn postgresql://osmsg:osmsg@db:5432/osmsg --all
docker compose up -dManifest max month: .../osmsg-history/resolve/main/manifest.json.
sudo cp infra/osmsg-cache-prune.{service,timer} /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now osmsg-cache-prune.timerMonthly HuggingFace dump (maintain month needs uv + source, so it runs host-side):
curl -LsSf https://astral.sh/uv/install.sh | sudo env UV_INSTALL_DIR=/usr/local/bin sh
sudo git clone https://github.com/osgeonepal/osmsg.git /opt/osmsg-maintain
cd /opt/osmsg-maintain && sudo uv sync --no-dev
sudo cp /opt/osmsg/infra/run-maintain.sh /opt/osmsg-maintain/
echo 'HF_TOKEN=<write-token>' | sudo tee /opt/osmsg-maintain/.env && sudo chmod 600 /opt/osmsg-maintain/.env
sudo cp infra/osmsg-maintain.{service,timer} /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now osmsg-maintain.timerrun-maintain.sh keeps all scratch on the block volume. Publish a month by hand with
sudo systemctl start osmsg-maintain.service (previous month) or pass a month:
sudo -E .../run-maintain.sh 2026-06.
journalctl -u osmsg -f # container logs
cd /opt/osmsg/infra && docker compose pull && docker compose up -d # update imagesRecover a full root disk (cache or images landed on root):
systemctl stop osmsg && cd /opt/osmsg/infra && docker compose down
docker volume rm infra_osmsg-cache infra_osmsg-data # pgdata is never removed
docker image prune -af && docker builder prune -afGET /health
GET /api/v1/stats?start=<ISO8601>&end=<ISO8601>[&hashtag=<tag>][&tags=true|false][&limit=N][&offset=N]
GET /docs/swagger