Repository navigation
DIE-359: Deploy pygeoapi on Cloud Run - #137
Merged
Merged
Conversation
- Dockerfile: use orchestration/ as the build context so products.yaml can be copied, and run generate_config.py with /venv/bin/python3 (the Noble base image has no `python` and blocks system pip). - cloudbuild.yaml: match the new build context and derive PYGEOAPI_SERVER_URL from $PROJECT_NUMBER/$_REGION instead of the _SUFFIX placeholder. - config.yml.j2: fix fields current pygeoapi requires — layer name `latest` (GDAL names GeoJSON layers after the file), `extents`, metadata.contact.name, server.map, and server.limits in place of the deprecated server.limit (default 500, max 10000 for NewWeaver). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
pygeoapi's config expects extents.temporal.begin/end, not the interval array used in API responses. With interval, any datetime= request crashed with KeyError: 'begin'. begin is left unquoted so YAML parses it as a tz-aware datetime, which pygeoapi requires. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The OGR provider ignores datetime filters and parses the whole 733 MB GeoJSON on every request (~9-10s locally). pygeoapi's Parquet provider applies datetime and serves the same layer in well under a second. - Dockerfile: install pyarrow and geopandas (pinned to the tested versions), which the Parquet provider needs and the base image lacks. - config.yml.j2: ogc_timeseries products use the Parquet provider on gs://.../latest.parquet with time_field: datetime; all other products stay on OGR + /vsigs/ GeoJSON. - generate_config.py: the GCS path check expects the parquet path for timeseries products. - cloudbuild.yaml: 2Gi memory; the Parquet provider peaked at ~1.8 GB across 4 workers locally. latest.parquet is uploaded manually for now; the pipeline doesn't write it yet. Known gaps: date-only datetime queries 500 against a tz-aware column, and numberMatched is wrong. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Timeseries products are now served by the Parquet provider from gs://.../latest.parquet; all other products stay on OGR + /vsigs/ GeoJSON. §3.3 and §6 now describe both paths, the GeoParquet file requirements, the manual upload, and known provider limitations. §6.2, §6.3 and §6.5 now mirror the current template, generator and Dockerfile, and the invariants gain a gs:// rule for the Parquet provider. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Deploy the Cloud Run service as die-pygeoapi@$PROJECT_ID instead of the Compute Engine default account. It needs storage.objectViewer on the products bucket, and Cloud Build's account needs iam.serviceAccountUser on it. SPEC §6.5 lists the same requirements. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On the live service all 30 GeoJSON collections returned 500 with "No valid GCS credentials found": GDAL doesn't detect Cloud Run as GCE, so /vsigs/ never asks the metadata server for the service account's credentials. The Parquet collection was unaffected (PyArrow finds ADC on its own). Set the variable in the deploy step, since --set-env-vars replaces the service's env on every deploy. SPEC §6.4/§6.5 updated. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Use a descriptive summary, point identification.url at the deployed service via PYGEOAPI_SERVER_URL, and add the data services contact email. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Your pull request is automatically being deployed to Dagster Cloud.
|
Contributor
Author
chasetmartin
added this pull request to stack #139
September 25, 2026 16:03
jirhiker
approved these changes
Sep 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Live service: https://die-pygeoapi-95715287188.us-central1.run.app
Summary
Deploys pygeoapi as a Cloud Run service (
die-pygeoapi) serving all 31 DIE products as OGC API - Features collections from GCS. It is live, and all 31 collections return features for plain andbboxitem queries./vsigs/).nm_waterlevels_timeseriesis served as GeoParquet through pygeoapi's Parquet provider (gs://). As GeoJSON (733 MB) every request took ~9–10 s, and the OGR provider ignoresdatetimefilters.A
latest.parquetfor the timeseries product was uploaded to the bucket manually. The pipeline doesn't produce it yet.Changes
orchestration/pygeoapi/Dockerfileorchestration/soconfig/products.yamlcan be copied (the oldCOPY ../config/...reached outside the context)./venv/bin/python3(the Noble base image has nopythonand blocks system pip).pyarrow==25.0.1andgeopandas==1.1.4for the Parquet provider.orchestration/pygeoapi/config.yml.j2— fixes for fields current pygeoapi requires, each of which failed at boot or first request:layer: latest(GDAL names a GeoJSON layer after the file basename, notOGRGeoJSON)metadata.contact.name,server.map, andextents(plural)server.limits(default_items: 500,max_items: 10000) replacing the deprecatedserver.limit, which broke the HTML items page and capped pages at 10temporal.begin/endinstead of theintervalarray (datetime=requests crashed withKeyError: 'begin')ogc_timeseriesproducts use the Parquet provider withtime_field: datetimeidentification.urlfromPYGEOAPI_SERVER_URL, contact emailorchestration/pygeoapi/generate_config.py— the GCS path check expectsgs://.../latest.parquetfor timeseries products.orchestration/pygeoapi/cloudbuild.yamlorchestrationPYGEOAPI_SERVER_URLderived from$PROJECT_NUMBER/$_REGIONinstead of a placeholderCPL_MACHINE_IS_GCE=YES— without it GDAL doesn't detect Cloud Run as GCE, skips the metadata-server credentials, and every GeoJSON collection returned 500 (No valid GCS credentials found) on the first deploydie-pygeoapiservice account (storage.objectVieweron the products bucket)SPEC.md— §3.3 and §6 updated to describe both providers, the GeoParquet requirements, GCS auth, and the Cloud Run settings.Local format benchmark (
nm_waterlevels_timeseries)The 733 MB timeseries GeoJSON was converted to FlatGeobuf, GeoPackage, and GeoParquet, and the same requests were timed against each in the local container (files on local disk, no CPU limit). GeoParquet is shown in the deployed configuration (UTC timestamps, 2 GB memory).
limit=5bboxdatetimeoffset=100000limit=10000datetimebut never applies it, so those requests returned unfiltered results. GeoParquet is the only format where the filter works, and it returns ISO 8601 datetimes (OGR returnsYYYY/MM/DD HH:MM:SS+00).offsetand large pages), which is why the service now has 2 GB.datetimeas UTC so standard RFC 3339 queries work (2020-01-01T00:00:00Z/..); date-only queries return 500 (see Known issues).On the live service, timeseries queries take ~0.8–1.5 s (reading from GCS on 1 vCPU) and a 10,000-feature page ~3.5–4 s (7.8 MB response).
Known issues
datetimequeries (2020-01-01/2020-12-31) return 500 against the tz-aware column;numberMatchedis wrong (reported 6 for 2020; the file has 10,518 matching rows).id_field: idis the well id, which repeats across observations, so/items/{id}is unreliable. Needs a unique feature id column.limit=5(e.g.nm_well_correlation, 44 MB, ~5.8 s).geopython/pygeoapi:latest.Next step
Update the processing pipeline to build GeoParquet for each layer and write
latest.parquetto GCS alongsidelatest.geojson, then move the remaining collections to the Parquet provider. That replaces the manual upload, fixes the slow large layers, and is the place to add a unique feature id. The files should have a bbox covering column, tz-aware UTC timestamps, plainstringcolumns, andschema_version="1.1.0".🤖 Generated with Claude Code