-
Notifications
You must be signed in to change notification settings - Fork 190
catalog.data.gov
a.k.a Catalog is the public-facing dataset discovery and search application for Data.gov. It serves 515,000+ datasets from 120+ federal, state, municipal, university, and tribal publishing organizations. This is the main app of Data.gov and is generally what folks are thinking about when they refer to Data.gov.
This is a custom Python/Flask application that replaced the legacy CKAN-based catalog in 2025. It reads from the shared harvest database managed by datagov-harvester (it does not write to it -- all dataset metadata is written by the harvester) and uses OpenSearch for full-text search.
| Instance | Url |
|---|---|
| Production | catalog.data.gov |
| Staging | catalog-stage.data.gov |
| Development | catalog-dev.data.gov |
| Legacy catalog (CKAN-based, running through fall 2026) | catalog-old.data.gov |
All environments run on cloud.gov, cf space names prod, staging, and development.
- Web app: Python/Flask, served via NGINX proxy on cloud.gov
-
Database: Shared Postgres instance managed by datagov-harvester (
datagov-harvest-dbservice) -- read-only. The SQLAlchemy models are duplicated locally inapp/models.pyfor isolation; the app interacts with the shared DB throughCatalogDBInterface(app/database/interface.py) -
Search: OpenSearch (
((app_name))-opensearchcloud.gov service) - Storage: S3 for sitemaps and static assets
- Monitoring: New Relic
- Logging: Logstack (cloud.gov log drain)
Data flow: datagov-harvester pulls agency metadata and writes it to the shared Postgres DB → datagov-catalog reads from Postgres and keeps an OpenSearch index in sync → the Flask app serves search and browse pages from OpenSearch, and dataset/organization detail pages from Postgres.
Prerequisites: Docker + Docker Compose, Python 3.x with Poetry, Node.js/npm (static assets + accessibility testing).
cp .env.sample .env # first-time setup, values can be left as defaults for local dev
make install-static # install static assets (needs npm)
make up # start local docker env
make load-test-data # load fixture dataThen visit the app locally and iterate. Useful commands:
-
make test-- full Python test suite -
make test-pa11y-- accessibility (pa11y-ci) tests, requires the app running -
make lint-check/make lint-fix-- ruff, isort, black -
make poetry-update-- keep local Poetry in sync with what CI uses
Install the git pre-commit hooks once per clone (pip install pre-commit && pre-commit install) to run the same formatters on staged files automatically before each commit; CI still runs make lint-check across the whole tree.
See the datagov-catalog README for the full environment variable reference (DB connection, OpenSearch host, New Relic, S3/sitemap credentials, etc).
Search is powered by OpenSearch, kept in sync with Postgres via a daily batch job (flask search sync clears the index and re-indexes every dataset). See docs/opensearch.md for how indexing and cursor-based pagination (search_after/after) work.
Dataset search supports filters (keyword, organization, organization type, publisher, geography, spatial data, collection). Each filter is a single FilterDefinition registered in app/search/filters/ that owns its own request parsing, URL serialization, OpenSearch clause building, sidebar rendering, and OpenAPI docs -- adding a new filter means writing one new module and registering it, not touching four different files.
- How To Add Search Filters -- step-by-step guide for contributors
- Search Filters Refactor Walkthrough -- deeper architectural tour with diagrams of the request → OpenSearch → sidebar flow
See docs/load-test.md for ApacheBench comparisons against the legacy catalog. Summary: search response times are comparable except for very broad queries with many matches (an open area for improvement); page load times are dramatically better across the board (e.g. dataset detail pages saturate at ~120 req/s vs. ~5 req/s on the legacy catalog).
Some datasets are grouped into collections, where a parent dataset represents a set of related child datasets (e.g. a series of related files from the same agency program). Collections work differently depending on how the child datasets are harvested:
- For WAF sources,
datagov-harvesterhas a dedicatedwaf-collectionsource type: each source has acollection_parent_url, and the harvester creates a parent dataset that the child records point back to (seeharvester/harvest.py). - More generally, a dataset's
isPartOffield marks it as a member of a collection. The catalog indexesdcat.isPartOfin OpenSearch and exposes acollectionsearch filter (app/search/filters/collection.py) so members of a collection can be queried together.
This replaced the CKAN-era mechanism (collection_package_id, described on the Collections wiki page), which only applies to inventory.data.gov now.
The old CKAN-based system could harvest the same dataset twice under different identifiers, requiring manual cleanup via SQL queries and a standalone datagov-dedupe tool. That failure mode no longer exists in the current pipeline: datagov-harvester's HarvestSource.filter_duplicate_identifiers() (in harvester/harvest.py) checks every incoming record's identifier against the others in the same job before processing. Duplicates are recorded as a harvest_record_error (rather than becoming a dataset) and dropped from the job.
If you're debugging why a dataset didn't show up after a harvest, check for duplicate-identifier errors on the job via the Harvest API rather than looking for duplicate rows in dataset -- see Harvest O&M and Debugging for query examples.
See New Relic and/or the cloud.gov wiki page for how to review logs.
GitHub Actions (.github/workflows) run scheduled and on-push jobs against the current catalog:
-
Restart -- rolling-restarts
datagov-cataloganddatagov-catalog-proxyon staging and prod every 15 minutes -
Create Sitemaps -- runs
flask sitemap generateandflask sitemap verifyas cloud.gov tasks daily (and on demand per environment) - Snyk Scan -- weekly dependency vulnerability scan across all projects
-
Check DB Models -- on every push/PR, diffs
app/models.pyandshared/constants.pyagainst the copies indatagov-harvesterto catch drift between the two repos' duplicated schema
See Automated O&M Tasks for the equivalent automation on the legacy catalog (catalog-old.data.gov) and inventory.data.gov, which still use CKAN-era GitHub Actions.
Deployments run automatically via GitHub Actions (deploy.yml) on push to main:
- Lint -- ruff
-
Deploy to staging -- deploys to the
stagingcloud.gov space, runs a smoke test -
Deploy to prod -- deploys to the
prodcloud.gov space after staging succeeds, runs a smoke test - Lighthouse CI audit -- runs against prod after deploy
For emergency deployments outside of the normal CI/CD pipeline, see Break Glass deployment.
- datagov-catalog README -- local dev setup, environment variables, architecture
- harvest.data.gov -- harvest pipeline UI
- datagov-harvester -- harvester source code and shared DB owner
- Updating organization logos on Catalog
This page previously described the legacy CKAN-based catalog (SOLR, ckan-php-manager, apache2/Ansible ops). That system is now catalog-old.data.gov; this page covers the current Flask/OpenSearch app. Updated 2026-07-23.