Skip to content

Instance guide

An instance repository packages the configuration and plugins for a specific operational context — a country, a region, or an organisation — and references climate-api as a versioned dependency rather than including it directly.

This keeps the core service separate from context-specific concerns, and means your configuration lives in its own repository that can be versioned, shared, and deployed independently.

When to use this pattern

Use an instance repository when you:

  • Want to add custom datasets not included in climate-api (e.g. national meteorological data)
  • Want to track your configuration in version control separately from the climate-api codebase
  • Want to pin your service to a specific version of climate-api and upgrade deliberately
  • Want to share your configuration with others, or deploy across multiple environments

If you only need to run climate-api with built-in datasets and no custom plugins, the setup guide (cloning climate-api directly) is simpler.


Repository structure

my-climate-service/
├── pyproject.toml          # declares climate-api as a dependency
├── uv.lock                 # locked dependency tree for reproducible installs
├── Makefile                # install / run shortcuts
├── climate-api.yaml        # instance config: extent, CRS, data_dir, plugins_dir
├── .env.example            # committed template for environment variables
├── .gitignore
├── plugins/
│   ├── datasets/           # custom dataset template YAMLs
│   ├── <source>/           # custom download / ingestion functions
│   │   ├── __init__.py
│   │   └── daily.py
│   ├── transforms/         # custom transform functions
│   │   ├── __init__.py
│   │   └── my_transform.py
│   └── processes/          # custom process YAMLs + Python functions
│       ├── my_process.yaml
│       └── my_process.py
└── data/                   # gitignored — downloaded files and Zarr stores

Step 1: Create the repository

mkdir my-climate-service
cd my-climate-service
git init

Step 2: Declare climate-api as a dependency

Create pyproject.toml:

[project]
name = "my-climate-service"
version = "0.1.0"
requires-python = ">=3.13"
description = "Open Climate Service instance for [context]"
dependencies = [
    "climate-api @ git+https://github.com/dhis2/climate-api.git",
]

[tool.uv]
package = false

[tool.uv.sources]
climate-api = { git = "https://github.com/dhis2/climate-api.git", branch = "main" }

The package = false setting tells uv that this repository is not itself a Python package — it only declares dependencies. The [tool.uv.sources] block pins climate-api to the main branch on GitHub. To pin to a specific release tag instead, use rev = "v0.2.0" (once releases are published on PyPI the git+ source can be replaced with a plain version constraint).

Install dependencies:

uv sync

This creates a .venv and a uv.lock file. Commit uv.lock so that everyone working with this repository installs exactly the same versions.

Step 3: Add a Makefile

.DEFAULT_GOAL := help

help: ## Show this help
    @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "  %-15s %s\n", $$1, $$2}'

install: ## Install dependencies with uv
    uv sync

run: ## Start the API with uvicorn
    set -a && . ./.env && set +a && \
        uv run uvicorn climate_api.main:app --reload --reload-include "*.yaml" --reload-include "*.yml" --port 8000

Step 4: Configure the instance

Create climate-api.yaml:

extent:
  name: My Region
  bbox: [28.8, -2.9, 30.9, -1.0]    # [xmin, ymin, xmax, ymax] in WGS84
  country_code: RWA                   # ISO 3166-1 alpha-3, required for WorldPop

data_dir: ./data
crs: EPSG:4326                         # target CRS for Zarr outputs (optional, defaults to EPSG:4326)
plugins_dir: ./plugins/
Field Required Description
extent.bbox Yes Bounding box in WGS84 decimal degrees
extent.name No Human-readable label shown in API responses
extent.country_code No ISO 3166-1 alpha-3 — required for WorldPop downloads
data_dir Yes Directory for downloaded files and Zarr stores, resolved relative to the config file
crs No EPSG code for the output Zarr CRS. Defaults to EPSG:4326
plugins_dir No Directory containing custom datasets, functions, and processes

To find the bounding box for a region, bboxfinder.com is a useful tool.

Create .env:

CLIMATE_API_CONFIG=/absolute/path/to/my-climate-service/climate-api.yaml

And a committed .env.example as a template:

CLIMATE_API_CONFIG=/path/to/my-climate-service/climate-api.yaml

Step 5: Add a .gitignore

.env
.venv/
data/
__pycache__/
*.pyc
.DS_Store

Step 6: Run the instance

make install
make run

Visit http://localhost:8000 to confirm the API is running. The /extent endpoint should return your configured bounding box.


Adding plugins

Plugins extend the instance with custom datasets, download functions, transforms, and processes. They live in plugins_dir and are loaded automatically at startup. The plugins_dir is added to sys.path, so Python modules placed directly inside it are importable.

plugins/
├── datasets/
│   └── enacts_rainfall.yaml    # custom dataset template
├── enacts/
│   ├── __init__.py
│   └── daily.py                # download function referenced in the YAML
├── transforms/
│   ├── __init__.py
│   └── enacts.py               # transform function
└── processes/
    ├── spatial_stats.yaml
    └── spatial_stats.py

See Extensibility for the full specification of each extension point, and Adding custom datasets for the dataset template field reference and download function contract.


Keeping climate-api up to date

To pull the latest changes from the main branch:

uv lock --upgrade-package climate-api
uv sync

Commit the updated uv.lock. Everyone working with this repository will get the same updated version after running uv sync.

To pin to a specific commit for a more controlled upgrade:

[tool.uv.sources]
climate-api = { git = "https://github.com/dhis2/climate-api.git", rev = "abc1234" }

Deployment

For production deployments, the same repository can be used directly on a server:

git clone https://github.com/your-org/my-climate-service.git
cd my-climate-service
cp .env.example .env   # fill in absolute paths and credentials
uv sync
make run

A Docker-based deployment guide is in progress.