Consequence: any environment can be rebuilt identically from code — no snowflake server, no “only Bob knows how it's set up”.
Replicas are disposable — and we measured it
Config change visible on all replicas in ~3 seconds — 2 s of it is a deliberate, tunable debounce; delivery itself is milliseconds, and new data needs no propagation at all
User sessions survive the loss of a replica (shared in the cluster)
Durability: upgrades and failures become routine — replace instances, never repair them.
What is in the object store
Real content of the demo store — file names carry satellite/sensor or model/parameter and the exact time (naming convention):
buckets/
├── rasters/each image stored once, as COG
│ ├── models/arpege/t/
│ │ └── arpege_t_isobaric85000_20260901T00Z_20260901T06Z.tif run · validity
│ └── sat/
│ ├── meteosat-12/fci/meteosat-12_fci_ir105_20260901T1200Z.tif
│ └── goes-19/abi/goes-19_abi_ir103_20260904T1400Z.tif real GOES-19 data
└── gwc-tiles/rendered map tiles — shared cache,
└── meteo_goes19_abi_ir103/… a tile rendered once serves everyone
COG = Cloud-Optimized GeoTIFF: replicas read only the byte ranges they need, straight from S3 — no copies on local disks.
Finding the right image — satellite index
One S3 file = one row. GeoServer answers “this area, this instant” with one indexed query. (table sat_granule, real rows)
fid PK
satellite UQIDX
instrument UQIDX
product UQIDX
time UQIDX
param_key FK
location
7
goes-19
abi
ir103
2026-09-04 13:00Z
brightness_temperature
…_ir103_1300Z.tif
8
goes-19
abi
ir103
2026-09-04 14:00Z
brightness_temperature
…_ir103_1400Z.tif
2
meteosat-12
fci
ir105
2026-09-01 12:10Z
brightness_temperature
…_ir105_1210Z.tif
PK primary key ·
UQ UNIQUE (satellite, instrument, product, time) — same instant ingested twice = update, never a duplicate ·
IDX lookup index — the time navigation query
param_key points to the shared parameter referential: every quantity has a canonical unit (here kelvin). A measurement always has a unit.
Finding the right image — model index
Models add two dimensions: the run (reference_time) and the level. Same pattern, richer key. (table model_granule)
fid PK
model UQIDX
param_key UQIDX
reference_time UQ
time UQIDX
elevation UQ
level_type
1
arpege
air_temperature
2026-09-01 00Z run
2026-09-01 00Z
85000
isobaric (Pa)
2
arpege
air_temperature
2026-09-01 00Z
2026-09-01 06Z +6 h
85000
isobaric (Pa)
5
arpege
air_temperature
2026-09-01 00Z
2026-09-01 06Z
50000 500 hPa
isobaric (Pa)
UQ UNIQUE (model, param_key, reference_time, time, elevation) ·
IDX lookup (model, param_key, time) ·
GIST spatial index on the raster footprint (the_geom, not shown) ·
each row also carries location — the s3:// path of the granule file (shown on the satellite slide, omitted here for width)
Clients navigate time, altitude and forecast run — each step is one indexed query on this table, served through standard WMS dimensions.
Observations — SYNOP, METAR, …
The station (written once)
station_id PK
name
geom
elevation
LFPG
Paris-CDG
point (4326)
119
ESSA
Stockholm-Arlanda
point (4326)
42
The measurements (time-partitioned — TimescaleDB hypertable)
station_id PK
time PKIDX
param_key PKIDX
value
source_unit
message_type
LFPG
2026-09-04 14:00Z
air_temperature
293.4 K
Cel
METAR
LFPG
2026-09-04 15:00Z
air_temperature
292.8
Cel
SYNOP
ESSA
2026-09-04 14:00Z
air_temperature
286.1
Cel
METAR
PK (station_id, time, param_key) — natural key, duplicates impossible ·
IDX (param_key, time DESC) — “latest value everywhere” query
Values are stored in the canonical unit (kelvin), the source unit is kept. Decoded °C and model K can never be mixed up. GeoServer publishes SQL views on top — decoders and GIS stay decoupled.
Navigating time and altitude is standard
No custom API. Clients add standard WMS dimensions to the map request: