Serving a Degraded Basemap During an Outage
This guide prepares a deliberately reduced basemap that can be served when the rendering tier is unavailable, so that a portal degrades into something usable rather than into grey squares. It belongs to Fallback Routing Strategies for Tile Servers, within the Core Portal Architecture & Security Boundaries framework.
Prerequisites
- A working fallback chain at the proxy, per setting up fallback tile routing in production.
- Object storage or a volume able to hold a small tile archive, served by something that is not the renderer.
- A seeding tool and enough render capacity for one off-peak run, per tuning MapProxy cache seeding for large extents.
- Agreement on what “usable” means for this portal’s readers — which is a conversation, not a technical decision.
Decide what degraded means before building it
“Serve something” is not a specification. The useful question is which capability readers must retain when everything else is gone, and the answer differs by portal: an emergency response map must keep orientation and place names at regional zoom; a planning portal must keep the parcel boundaries; a public transport map must keep the network.
The third row is the sweet spot for most portals. A basemap seeded to a mid zoom over the served extent is a few gigabytes, can be regenerated on a schedule, and is served by object storage that has no dependency on the renderer, the database, or the catalogue.
Step-by-step implementation
1. Seed the fallback archive
# A separate MapProxy configuration whose only job is the fallback archive:
# simplified style, restricted zooms, and its own cache directory.
mapproxy-seed -f mapproxy-fallback.yaml -s seed-fallback.yaml \
--seed=basemap_fallback --concurrency 4 --progress-file .fallback-progress
# seed-fallback.yaml
# seeds:
# basemap_fallback:
# caches: [basemap_fallback_cache]
# grids: [webmercator]
# levels: { from: 0, to: 11 } # orientation, not detail
# coverages: [served_extent]
Stopping at zoom 11 is the decision that keeps this affordable: it is a few thousand tiles for a national extent rather than the millions that deeper levels require, and it is enough for a reader to find where they are.
2. Publish it somewhere with no shared dependencies
# Sync the archive to object storage; it must be servable when the cluster is not.
aws s3 sync ./cache_fallback/ s3://portal-fallback-tiles/basemap/ \
--cache-control "public, max-age=86400" \
--content-type image/png --delete
# A fallback that lives on the same nodes as the renderer is not a fallback.
3. Route to it when every real tier is down
upstream tile_primary {
server cache-edge-1.internal:8080 max_fails=3 fail_timeout=15s;
server render-secondary.internal:8081 backup;
}
server {
listen 443 ssl;
server_name tiles.example.gov;
location ~ ^/wmts/(?<layer>[^/]+)/(?<z>\d+)/(?<x>\d+)/(?<y>\d+)\.png$ {
proxy_pass http://tile_primary;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_connect_timeout 1s;
# When every tier has failed, fall through to the archive rather than
# returning an error the client will render as a grey square.
proxy_intercept_errors on;
error_page 502 503 504 = @degraded;
}
location @degraded {
internal;
# Only shallow zooms exist in the archive; deeper requests get an
# explicit, cacheable "not available" rather than a broken image.
if ($z > 11) { return 404; }
proxy_pass https://portal-fallback-tiles.s3.amazonaws.com/basemap/$z/$x/$y.png;
proxy_set_header Authorization "";
add_header X-Map-Mode "degraded" always;
add_header Cache-Control "public, max-age=60" always;
}
}
4. Tell the reader, in the map
A degraded map that does not announce itself is worse than an error, because a reader will make a decision on it. The X-Map-Mode header above is what the client reads to show a banner.
// map.js — surface degraded mode as soon as any tile reports it
map.on("tileloadend", (event) => {
if (event.tile?.headers?.get?.("X-Map-Mode") !== "degraded") return;
showBanner({
text: "Showing a reduced basemap while the map service is unavailable. " +
"Detail beyond mid zoom and thematic layers are not available.",
level: "warning",
persistent: true,
});
disableThematicLayerToggles(); // do not offer what cannot be served
});
The third card matters more than it sounds. A blank tile where a building should be is indistinguishable, to a reader, from an area where nothing has been mapped — and on a planning or emergency portal that is a decision-affecting difference.
Keeping the archive current enough to be useful
A fallback archive is derived data, and derived data goes stale. The question is how stale is acceptable, and the answer is usually generous: an archive a month old is fine as an orientation map and would be unacceptable as a primary service.
Schedule the rebuild as a job with its own alert, because a fallback that has silently stopped rebuilding is the definition of a control that will fail when used. Assert on the archive’s newest object timestamp rather than on the job’s exit code.
Finally, rehearse the switch. Stopping the rendering tier deliberately on a quiet afternoon, confirming that the archive serves and the banner appears, and then restoring service takes about ten minutes and is the only way to know that the fallback path is still wired up. A degraded-mode configuration that has never been exercised has usually drifted — a changed bucket name, an expired credential on the storage path, a client that no longer reads the header — and each of those is invisible until the moment it matters.
Verification
# 1. The archive exists and covers the intended zooms
aws s3 ls --recursive s3://portal-fallback-tiles/basemap/ | awk -F/ '{print $2}' | sort -u
# expect: 0 through 11, and nothing deeper
# 2. With all tiers down, a shallow tile is still served
sudo systemctl stop tile-cache tile-renderer
curl -sI "https://tiles.example.gov/wmts/basemap/6/32/21.png" | grep -E 'HTTP/|X-Map-Mode'
# expect: 200 and X-Map-Mode: degraded
# 3. A deep tile returns an explicit 404, not a blank image
curl -s -o /dev/null -w '%{http_code}\n' "https://tiles.example.gov/wmts/basemap/16/32000/21000.png"
# expect: 404
# 4. The client shows the banner
# Load the map with the tiers still stopped.
# expect: a persistent warning, thematic toggles disabled
# 5. Recovery returns to full service without a deploy
sudo systemctl start tile-cache tile-renderer
curl -sI "https://tiles.example.gov/wmts/basemap/6/32/21.png" | grep -c 'X-Map-Mode'
# expect: 0
Troubleshooting matrix
| Symptom | Likely cause | Fix |
|---|---|---|
| Degraded mode never activates during a real outage | The failure returned a status not listed in error_page |
Include the statuses your tiers actually return, including 000-style timeouts |
| The fallback is unreachable exactly when needed | The archive is served from the same cluster as the renderer | Move it to storage with no shared dependency |
| Readers do not notice the map is reduced | The banner is a toast that fades | Make the disclosure persistent while the mode is active |
| Blank tiles at deep zoom in degraded mode | Deep requests proxied to an archive that has no such tiles | Return an explicit 404 above the seeded zoom, and cap the client’s zoom |
| Degraded tiles persist after recovery | The 60-second cache is longer than the recovery, or the CDN cached them | Keep the degraded cache short and purge that path on recovery |
| The archive is months old | The rebuild job failed silently | Alert on the newest object’s age, not on the job’s exit code |
| Degraded mode masks a partial outage | Every tier failing looks the same as one layer failing | Alert on the fallback rate per layer, per the routing guide |
FAQ
Is a degraded map better than an honest error page?
For a map that people use to orient themselves, yes — provided it says what it is. A reader who can see where they are and which region they are looking at can often still do the thing they came to do. What is not acceptable is a reduced map presented as the full one, because then the missing detail reads as absent data.
How much storage does this actually need?
For a national extent to zoom 11 with a simplified style, a few gigabytes is typical, and it compresses well. The number rises sharply with each additional zoom level, which is exactly why the level cap is the design decision rather than an implementation detail.
Should the fallback include thematic layers?
Generally no. Thematic layers are the ones most likely to be the reason someone is looking, and a stale copy of a flood extent or a road closure is actively dangerous. Keep the fallback to base cartography, and disable the thematic controls with a clear reason rather than serving stale versions of them.
Can this double as a load-shedding mechanism?
It can, and it should be a deliberate decision rather than an emergent one. Routing anonymous public traffic to the archive under extreme load, while authenticated users keep the full service, is a defensible policy — but it must announce itself in exactly the same way, or a reader is silently given a lesser map without knowing.
What about vector-tile portals?
The same structure applies with a smaller archive: publish a simplified vector tileset at shallow zooms and a matching minimal style, served from object storage. The client-side work is the same — announce the mode, disable what is unavailable, and cap the zoom to what the archive covers.
Related
- Setting Up Fallback Tile Routing in Production — the chain this is the last tier of.
- Configuring Stale-While-Revalidate Tile Caching — keeping tiles flowing before it gets this far.
- Replicating Tile Caches Across Regions — the larger version of the same idea.
Up one level: Fallback Routing Strategies for Tile Servers.