pygeoapi in production: TLS, access, limits and monitoring
Deploy pygeoapi behind a production server and proxy with explicit limits, identity boundaries, cache rules, recovery and failure checks.
Editorial review: 2026-09-23
A production pygeoapi deployment is a maintained data service, not a development process left running. It needs a stable public URL, a process supervisor, bounded requests, protected data access, monitoring and a practiced recovery path. The correct deployment depends on whether the service is public read-only, restricted reading or an authorized write workflow.
Start with the local CSV tutorial or the reference lab. Preserve their expected identifiers and responses as smoke tests. The lab's measured local requests show specific fixture behavior; they are not a production availability commitment or a capacity recommendation for a different dataset.
1. Select a clear publication boundary
For a public service, expose only approved fields and rows through a read-only provider account. For restricted data, place authentication and authorization on the actual API path; a login screen in the map viewer does not protect direct requests. For writes, design validation, concurrency, audit, permission changes and retry behavior before enabling editing.
pygeoapi's configuration distinguishes hidden resources from access control. A collection can be absent from discovery and still accessible by a known URL. admin: false disables the administrative API setting; it does not mean every collection is private. The configuration reference also warns that editable resources need access control outside the simple publication configuration.
| Publication type | Data role | Exposure | Required negative test |
|---|---|---|---|
| Public read | Read-only approved source | Public HTTPS | No internal columns or writes |
| Partner read | Restricted source/view | Authenticated API | Other partner cannot retrieve records |
| Internal edit | Narrow write privileges | Authorized application route | Invalid/replayed edit cannot bypass rules |
| Administration | Configuration privileges | Private operator path | Reader cannot change collections |
2. Use a production server and supervised process
The pygeoapi running guide advises against using pygeoapi serve as the production server. For a Unix deployment using the Flask entry point, a WSGI server such as Gunicorn is one documented option. Container-based deployment is another option; use the operating model your team can restore and patch.
This command illustrates a small internal process after pygeoapi and Gunicorn are installed in a pinned environment. It is not a universal sizing recommendation:
export PYGEOAPI_CONFIG='/srv/pygeoapi/config.yml'
export PYGEOAPI_OPENAPI='/srv/pygeoapi/openapi.yml'
gunicorn pygeoapi.flask_app:APP \
--bind 127.0.0.1:5000 \
--workers 2 \
--timeout 30
A service manager or orchestrator should run it under a non-root account, set the working directory and environment, restart failed processes, and capture sanitized logs. If the reverse proxy runs on another host or container, bind to the intended private interface and restrict network access accordingly; 127.0.0.1 is only reachable within its own host/network namespace.
Pin dependencies, record the Python and server versions, and rebuild from that definition. Keep generated OpenAPI aligned with the configuration. Avoid hot-reload in production and avoid granting the process write access to unrelated application files.
3. Make HTTPS links and identity work end to end
Terminate TLS at a controlled proxy or platform edge and advertise the external HTTPS URL through server.url. If deployed below /oapi, verify collection links, next-page links, static assets and OpenAPI all preserve that prefix. Test from outside the host; local success can hide inaccessible private URLs.
Trust forwarded host, scheme and identity headers only from the known proxy. Strip caller-supplied identity headers before injecting trusted values. Protect the internal application port so clients cannot bypass the edge's authentication or rate limits. Check session expiry and revoked access through the same route the client uses.
CORS is browser behavior, not user authorization. Define permitted origins according to the publication, and test actual browser requests. A partner with a valid token still needs permission to the requested rows; “authenticated” is not automatically “authorized for every collection.”
4. Bound results and expensive work separately
The first local tutorial uses a small page maximum. Choose real limits from the consumer's task and measurements. For a simple bounded reading service, the following server fragment makes excessive page requests fail explicitly rather than silently truncating to the maximum:
server:
limits:
default_items: 25
max_items: 250
on_exceed: error
This fragment modifies an existing complete configuration; it is not a standalone service definition. The values are illustrative operating choices. A page of 250 complex polygons may be much heavier than 250 points. Measure bytes, serialization time and source query time for representative geometry.
Add appropriate edge rate/concurrency limits, provider timeouts and database statement limits. A small returned page can still require an expensive count or filter. Provide a separate bulk download when consumers legitimately need a complete dataset; forcing them through thousands of interactive requests can be worse for both sides.
For PostgreSQL, estimate the maximum connections created by all workers and providers. Increase worker count only when measured CPU, I/O and database capacity justify it. More workers can exhaust the database pool before improving service latency.
5. Treat schema and identifiers as a public API
Decide which changes are compatible. Adding an optional property differs from renaming an identifier or changing date interpretation. Keep a revision in metadata or release notes and notify known consumers before removing fields or changing item URLs. Maintain a fixture that includes nulls, accented text, dates, empty results and geometry near the edge of the advertised extent.
For each deployment check collection discovery, OpenAPI, a known item, missing item, attribute filter, bbox, pagination and excessive limit. Compare IDs and values, not only status. If the dataset changes during a multi-page export, document whether consumers get a live view or a stable snapshot.
6. Monitor availability and freshness
Record success rate and latency by route/operation class, response size, source failures, process memory and database pool saturation. Add a synthetic probe that checks expected content, not just HTTP 200. Keep a separate freshness check based on the source's update expectation. An API can be perfectly available while delivering last month's data.
| Observation | First investigation | Useful action |
|---|---|---|
| Item route works, large filters fail | Query plan and limits | Simplify/index bounded queries |
| Links use HTTP or internal host | Public URL/proxy config | Correct advertised base and rerun pagination |
| Memory grows on large results | Geometry/serialization/page size | Reduce page and provide bulk product |
| Database connections exhausted | Worker/pool multiplication | Bound connections and align timeouts |
| 200 response with stale data | Ingestion/update workflow | Alert on freshness, not only status |
| Private fields appear | Source view and properties | Restrict source and audit cached responses |
Avoid logging tokens, private property values or full confidential filters. Use request identifiers and safe operational categories to correlate a failure.
7. Practice replacement and rollback
Back up source data and metadata, configuration, environment definition and the mechanism for retrieving secrets. Restore into an isolated deployment and run the contract tests. A copied configuration is not a database backup; a database backup alone does not recover the public API path or identity setup.
Deploy a candidate version beside the working service where possible. Run fixture and consumer checks, then change traffic. Keep a rollback plan that includes data/schema changes; returning to old code cannot undo incompatible writes or migrations automatically. Confirm that the restored version still enforces access controls.
8. Define the operational handover
Before launch, identify who updates data, patches software, renews certificates, reviews incidents and authorizes schema changes. Agree which service failures require immediate attention and which can wait. Record how a new operator starts, stops, tests and restores the deployment without relying on one person's shell history.
Use the cost model to include this ongoing work. pygeoapi can reduce coupling to a proprietary service interface; it does not remove the need to operate a reliable service. If these responsibilities are not staffed internally, compare managed proposals with the same scope and response expectations.