1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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).
|