aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/deploy/README.md
diff options
context:
space:
mode:
authorgodosa <godosa@godosa.eu>2026-10-07 00:14:38 +0200
committergodosa <godosa@godosa.eu>2026-10-07 00:14:38 +0200
commit3443c1c65e9f1753e1e656b35d08416c1fa298f2 (patch)
tree4e43236f460145a4d75d1b4616dcb7aa6ef08f51 /deploy/README.md
downloadworldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.tar.gz
worldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.zip
worldmap-viewer: initial public history
Diffstat (limited to 'deploy/README.md')
-rw-r--r--deploy/README.md101
1 files changed, 101 insertions, 0 deletions
diff --git a/deploy/README.md b/deploy/README.md
new file mode 100644
index 0000000..4ce6a0a
--- /dev/null
+++ b/deploy/README.md
@@ -0,0 +1,101 @@
+# Public map server (container)
+
+A read-only map for anyone, on a small shared machine. Worlds are **built elsewhere** (a build needs far more memory
+and time than serving) and copied over; the container holds only code.
+
+## 1. Build the image
+
+From the folder that holds `worldgen/` and `worldmap-viewer/`:
+
+```sh
+podman build -f worldmap-viewer/deploy/Containerfile --ignorefile worldmap-viewer/deploy/.containerignore \
+ -t worldmap-viewer .
+```
+
+`requirements.lock` pins the libraries the worlds were built and checked with (same numbers → the same tiles as on
+the build machine).
+
+## 2. Copy a world
+
+```sh
+python deploy.py bundle WORLD_DIR server:/srv/data/map/WORLD_NAME [--res N] # --dry-run lists it first
+```
+
+Copies `config/`, `places/regions.json` and `out/r<N>/`, leaving out:
+
+- pins, exports, lore notes and build inputs, which a public server never serves;
+- refined areas and tiles of earlier builds.
+
+It uses `rsync -a`, which keeps modification times. The build fingerprint depends on them; without them, the
+server would rebuild every refined area. Nothing is deleted on the server: it prunes what it no longer uses at
+start, and keeps the tiles it rendered.
+
+Then, on the server, let the eras' serve caches share their identical blocks again (a copy loses that; ≈ half
+their size on btrfs or reflink XFS):
+
+```sh
+podman exec worldmap-viewer python deploy.py dedupe /data/WORLD_NAME
+```
+
+Data disk: btrfs with `compress=zstd:1`, normal copy-on-write (no `chattr +C`/`nodatacow`: that turns off both
+compression and block sharing). Back up nothing under `out/`: it can all be copied again.
+
+## 3. Run it
+
+Settings come from `WORLDMAP_<OPTION>` variables (every `serve.py` option; a command-line flag wins). The image
+sets `WORLDMAP_PUBLIC=1`, `WORLDMAP_BIND=0.0.0.0`, `WORLDMAP_PORT=8765`.
+
+| Variable | Meaning |
+|---|---|
+| `WORLDMAP_WORLD` | world folder(s) in the container, comma separated (the first opens by default) |
+| `WORLDMAP_RES` | build resolution to serve |
+| `WORLDMAP_ALLOW_HOST` | the public name(s) the proxy passes on, e.g. `maps.example.org` |
+| `WORLDMAP_WORKERS` | render processes (≈ one core each while rendering) |
+| `WORLDMAP_MAX_RENDERS` | tiles rendering or waiting at once; more → 503 + `Retry-After` (default 4 × workers) |
+| `WORLDMAP_TILE_CACHE_GB` | cap on saved tiles; the least recently used go first |
+| `WORLDMAP_TILE_KEEP_Z` | relief + mesh tiles to this zoom are never deleted nor counted (a shipped prerender) |
+
+Example Quadlet (`/etc/containers/systemd/worldmap-viewer.container`). This sizing is for a 4-core, 16 GB machine shared
+with other services:
+
+```ini
+[Unit]
+Description=Public world map
+
+[Container]
+Image=localhost/worldmap-viewer:latest
+ContainerName=worldmap-viewer
+Volume=/srv/data/map:/data:Z
+Environment=WORLDMAP_WORLD=/data/world-a,/data/world-b,/data/world-c
+Environment=WORLDMAP_ALLOW_HOST=maps.example.org
+Environment=WORLDMAP_WORKERS=4
+Environment=WORLDMAP_TILE_CACHE_GB=140
+Environment=WORLDMAP_TILE_KEEP_Z=9
+PublishPort=127.0.0.1:8765:8765
+HealthCmd=python -c "import os, urllib.request as u; u.urlopen('http://127.0.0.1:' + os.environ['WORLDMAP_PORT'] + '/api/worlds', timeout=8)"
+HealthStartPeriod=10m
+
+[Service]
+MemoryHigh=7G
+MemoryMax=8G
+CPUWeight=50
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
+```
+
+Keep `%` out of Quadlet lines: systemd expands `%s`, `%h` and the like (write `%%` for a literal `%`). The `HealthCmd`
+above builds its URL without one.
+
+Leave `WORLDMAP_RES` unset when the worlds differ: each then serves its final build (world-a r5, the others r4).
+A set `WORLDMAP_RES` applies to every world.
+
+The image runs as uid 8765. `/srv/data/map` must be writable by that user as the container maps it: the server
+writes tiles and serve caches there. `:U` would do this, but it re-chowns every file at each start, which is slow
+with millions of tiles. Set the ownership once instead.
+
+The first start after a new copy can take minutes, while the server loads or builds its serve caches. Ship them
+built (they are part of `out/r<N>/`) and it starts in about half a minute.
+
+Single-disk machines: set a smaller `WORLDMAP_TILE_CACHE_GB` (e.g. 20).