services: maptool: # "./build", not ".": the compose file's own context ("." would mean # the symlink's directory, /srv/podman/maptool/ — see docs/deployment.adoc) # is one level above where the actual checkout (and its Dockerfile) # lives since the previous commit moved it into build/. Volume paths # below resolve against that same symlink directory too, but a relative # *build context* specifically needs pointing at build/ itself — the # gap this fixes: production's compose up --build failed outright # ("lstat .../maptool/Dockerfile: no such file or directory") until this # was caught, since only the volume-path resolution got dry-run tested # before the previous commit shipped, not this one. build: ./build image: maptool:latest restart: unless-stopped ports: - "4567:4567" environment: # "[::]", not "0.0.0.0": binding the IPv6 wildcard also accepts IPv4 # connections (dual-stack is the Linux default), so this covers both # without a second listener. Puma's bind URL needs the brackets around # a literal IPv6 address (config/puma.rb interpolates this as-is). BIND_HOST: "[::]" RACK_ENV: production # Read from .env (docker compose loads it automatically) — not # committed, see docs/deployment.adoc. SESSION_SECRET: ${SESSION_SECRET:?SESSION_SECRET is required, see docs/deployment.adoc} GITEA_OAUTH_CLIENT_ID: ${GITEA_OAUTH_CLIENT_ID:?GITEA_OAUTH_CLIENT_ID is required, see docs/deployment.adoc} GITEA_OAUTH_CLIENT_SECRET: ${GITEA_OAUTH_CLIENT_SECRET:?GITEA_OAUTH_CLIENT_SECRET is required, see docs/deployment.adoc} MAPTOOL_ALLOWED_GITEA_USER: ${MAPTOOL_ALLOWED_GITEA_USER:?MAPTOOL_ALLOWED_GITEA_USER is required, see docs/deployment.adoc} # Optional: blank disables the LiveTrack webhook endpoint rather than # failing to start, since it's not needed for the app's core function. LIVETRACK_WEBHOOK_SECRET: ${LIVETRACK_WEBHOOK_SECRET:-} # Optional, only read by data/plugins/p4n2gpx/'s own p4n2gpx.rb (not # part of this repo — see docs/deployment.adoc) if that private # plugin is present; blank just means it fails with its own "could # not authenticate" error when run, same non-fatal-to-startup story # as the other optional secrets here. P4N_USER/P4N_PASSWORD alone # don't actually work here — park4night's login form is behind # invisible reCAPTCHA, which no browserless login can satisfy — so # P4N_SESSION_COOKIE (a real logged-in browser's session cookie, # copied out by hand and refreshed whenever it expires) is what # p4n2gpx.rb's make_session actually ends up using. P4N_USER: ${P4N_USER:-} P4N_PASSWORD: ${P4N_PASSWORD:-} P4N_SESSION_COOKIE: ${P4N_SESSION_COOKIE:-} # The kiwix service below, reached by its compose service name (both # are on the same default compose network) — port matches its own # --port=8080 in the command below. Not made conditional on the ZIM # actually being present: GET /api/kiwix pings it and just reports # unavailable if the container isn't up/reachable, same story as # every other optional-local-file feature (offline.pmtiles, etc). # Reached server-side only — production sits behind a reverse proxy # that only forwards to maptool itself, so app.rb's GET /kiwix/* # proxies to this instead of handing the browser this address. KIWIX_URL: "http://kiwix:8080" volumes: # Uploaded GPX files and the offline PMTiles extract — kept outside # the image so a rebuild/redeploy doesn't wipe them (see # docs/development.adoc for how the deploy job treats this # directory). A private plugin (app.rb's PLUGINS_DIRS) also belongs # under here, at data/plugins// — nothing further to mount, # this one bind already covers the whole data/ tree. An offline.pmtiles # build's own scratch work (Dir.mktmpdir, lib/maptool/pmtiles_builder.rb) # happens outside this mount, in the container's own filesystem — a # large country's .osm.pbf plus Planetiler's working set can be # several GB, worth knowing if the host is ever short on space there. # :Z (not :z — this path is only ever mounted into this one # container) relabels the host directory for SELinux, since Podman # on an SELinux-enforcing host otherwise denies the container write # access to a bind-mounted directory regardless of Unix permissions # — harmless/ignored on a host without SELinux. - ./data:/app/data:Z # Fakes a GNSS fix (a loop around Bratislava, see # bin/gnss_simulator.rb/docs/development.adoc) since there's no real # receiver attached here — the "GNSS" control would otherwise just show # "no fix" always. Remove this service (and nothing else) whenever a real # receiver/gpsd setup replaces it. network_mode ties it to maptool's own # network namespace rather than a bridge network, since GpsdClient # connects to a hardcoded 127.0.0.1:2947 — same as gpsd itself normally # being local to the machine it serves. gnss-simulator: image: maptool:latest depends_on: - maptool restart: unless-stopped network_mode: "service:maptool" command: ["ruby", "bin/gnss_simulator.rb"] # Serves an offline Wikipedia ZIM for the popup's "Nearby on Wikipedia" # link (see docs/development.adoc for where to get one and drop it in — # same "not part of the repo, place it under ./data locally" story as # offline.pmtiles/terrain.pmtiles). On the default compose network (not # network_mode: "service:maptool" — unlike gnss-simulator this doesn't # need a hardcoded localhost port, KIWIX_URL above reaches it by this # service's own name), so maptool and kiwix stay independent containers. # Keeps restarting (harmlessly) if data/kiwix/wikipedia.zim isn't there # yet — GET /api/kiwix just reports unavailable until it comes up. # # The volume binds the exact *file* path, not the data/kiwix/ directory — # deliberately, so data/kiwix/wikipedia.zim can be a symlink to wherever # the (large) real ZIM actually lives. A bind mount resolves a symlink at # its *source* path once, at mount time (plain mount(2) semantics, same # as Podman); a symlink sitting inside an already-mounted directory # doesn't get that treatment — the container would have to resolve it # itself, inside its own filesystem, where the host path it points to # doesn't exist. kiwix: image: ghcr.io/kiwix/kiwix-serve:latest restart: unless-stopped # No --port here: the image's own start.sh already prepends # --port=8080 (its default) before appending this command array, so # passing it again produced "Unexpected argument: --port=8080, # --port=8080, /data/wikipedia.zim" and kiwix-serve refused to start. # # --urlRootLocation=/kiwix makes every link/asset kiwix-serve itself # emits (search results, article pages, its CSS/JS) carry a /kiwix # prefix, matching where app.rb's GET /kiwix/* proxies requests in # from (confirmed against libkiwix's internalServer.cpp: it strips # this same prefix off incoming request paths, so both ends agree on # it) — no ports: mapping needed, nothing outside the compose network # ever talks to this container directly. command: ["--urlRootLocation=/kiwix", "/data/wikipedia.zim"] volumes: # :Z, same SELinux reasoning as maptool's own ./data mount above. - ./data/kiwix/wikipedia.zim:/data/wikipedia.zim:Z