Deploy¶
The production site is static files behind a reverse proxy. The private repository mamut-routing-deploy pins this
repository as a submodule and carries the host templates and deploy.sh; its docs/OPERATIONS.md is the host-specific
runbook (paths, users, certificates). This page is the part that is true of any deployment.
Architecture¶
mamut-routing-publish serveserves the built tree, the repository artifact roots (LICENSE,benchmarks/, thedist/caches referenced by payloads) and/healthz, with cache headers, ETags, Range and precompressed negotiation. There is no compute endpoint.- In root mode nginx serves
dist/andbenchmarks/directly and proxies/api/*; in rootless mode the Python server serves everything on one port behind the domain's reverse proxy. - Releases are built into
<releases>/dist-<timestamp>and activated by an atomicdistsymlink swap. Publication state lives outside the release directories and is hydrated before each build. - Reference sizing: 4 to 6 vCPU, 8 to 16 GB RAM, 25+ GB disk. Serving is light; the build is what needs memory.
What deploy.sh does¶
git pull --ff-onlythe superproject and update submodules recursively (skipped withSKIP_SOURCE_UPDATE=1);- archive stale directories of retired submodules (
ARCHIVE_STALE_SUBMODULES); uv sync --frozen;- hydrate
publish-stateand seed the new release from olderatf-cache/route-geometry-cachetrees; site build --precompressinto a new release directory, static payload mode;- swap the
distsymlink; - restart the server (direct process or systemd unit), wait for
/healthz; - refresh nginx if the templates changed (root mode);
- keep the newest
KEEP_RELEASESrelease directories.
The environment contract¶
| Variable | Meaning |
|---|---|
ENV_FILE |
shell file exported before everything else (holds MAMUT_BASEMAP_API_KEY, never committed) |
APP_DIR, ROUTING_DIR, RELEASES_DIR |
superproject, the MAMUT-routing checkout, the releases root |
SITE_BUILD_JOBS, SITE_BUILD_ATF_MAX_N, SITE_BUILD_SKIP_ATF_CACHE (default 1), SITE_BUILD_SKIP_ROUTE_GEOMETRY, SITE_BUILD_FETCH_MISSING_OSM (default 0), ROUTE_GEOMETRY_JOBS (default 1) |
forwarded to site build |
SITE_BUILD_STATE_DIR, SITE_BUILD_HYDRATE_PUBLISH_STATE, SITE_BUILD_SEED_RELEASE_SIDECARS |
state and cache reuse across releases |
API_RESTART_MODE (direct or systemd), API_HOST, PORT, API_HEALTH_HOST, API_REPO_ROOT, API_PID_FILE, API_LOG_FILE |
how the server is (re)started and checked |
START_API_ONLY |
restart the server without building; always pass START_API_ONLY=0 explicitly for a real deploy, a stale export silently turns the deploy into a restart |
SKIP_SOURCE_UPDATE, GIT_SUBMODULE_TRANSPORT (ssh, https), GIT_SSH_KEY |
source update behaviour |
KEEP_RELEASES, REFRESH_NGINX, DOMAIN, CERTBOT_ROOT |
housekeeping, root mode only |
A deploy, end to end¶
# 1. locally: bump the MAMUT-routing pointer in the deploy superproject and push it
# 2. on the host, in a screen/tmux session or with nohup (a build outlives an ssh session).
# deploy.sh pulls the superproject and updates every submodule itself (SKIP_SOURCE_UPDATE=0, the default):
cd ~/mamut-routing-deploy
ENV_FILE=~/mamut.env START_API_ONLY=0 nohup ./scripts/deploy.sh > ~/deploy-$(date +%F).log 2>&1 < /dev/null &
tail -f ~/deploy-*.log
# 3. smoke test from the host and from outside
BASE_URL=http://127.0.0.1:8081 CHECK_HTTP_REDIRECT=0 ./scripts/smoke-test.sh
BASE_URL=https://<domain> ./scripts/smoke-test.sh
The smoke test checks the home page, payloads, artifacts, LICENSE, /healthz, that retired compute endpoints return
404, and precompressed serving. Add /docs/ to your manual check.
When the host cannot pull¶
SKIP_SOURCE_UPDATE=1 builds the host's current checkout as it is: set it only after you updated the sources
yourself, otherwise the deploy succeeds and republishes the old content. The rootless variant without a GitHub key
(the deploy repository is private; the site repositories are public over HTTPS):
# locally: push the superproject commit straight to the host
git push ssh://<host>/<path>/mamut-routing-deploy main:refs/remotes/local-push/main
# on the host: fast-forward, then update submodules over HTTPS, then deploy without a source update
cd ~/mamut-routing-deploy && git merge --ff-only local-push/main
git -c url."https://github.com/".insteadOf="git@github.com:" submodule update --init --recursive Mamut-routing
ENV_FILE=~/mamut.env START_API_ONLY=0 SKIP_SOURCE_UPDATE=1 GIT_SUBMODULE_TRANSPORT=https nohup ./scripts/deploy.sh > ~/deploy-$(date +%F).log 2>&1 < /dev/null &
Rollback¶
Content: point dist at the previous release directory (ln -sfn <releases>/dist-<older> dist) and restart the
server. Code: check out the previous superproject commit, git submodule update --init --recursive, restart. Then
smoke test.
Before you deploy a heavy change¶
- Route geometry for new or replaced BKS: compute locally, copy
dist/route-geometry-cacheinto the release directory (tar over ssh), let the build reuse it. - Free disk (
df -h) and memory (free -m) first; never unpack archives under a tmpfs/tmp. - New satellite: initialise it on the host with HTTPS remotes if the host has no GitHub key.