# sentry-opensearch-nodestore
Sentry NodeStore backend powered by OpenSearch.
Supported Sentry 24.x / 25.x and OpenSearch 2.x / 3.x.
Use an OpenSearch cluster to store Sentry NodeStore payloads for better scalability and simpler retention (delete old indices) compared to PostgreSQL.
- Stores payloads as compressed, non-indexed data
- Daily indices with optional prefix (e.g., `sentry-YYYY-MM-DD` or `sentry-<prefix>-YYYY-MM-DD`)
- Reads/deletes via alias (default: `sentry`)
- Composable index templates (`/_index_template`)
---
## Why OpenSearch for NodeStore
- Horizontal scalability by adding data nodes
- Sharding and replication for throughput and resilience
- Automatic rebalancing when cluster grows
- Cleanup is fast and reliable by dropping old daily indices (vs. large PostgreSQL tables)
---
## Installation
Install the package for your Sentry deployment.
### Option A: From PyPI
```sh
pip install sentry-opensearch-nodestore
```
### Option B: Rebuild Sentry image with this backend
```dockerfile
FROM getsentry/sentry:25.8.0
RUN pip install sentry-opensearch-nodestore
```
---
## Environment variables
| Variable | Default | Required | Notes |
|---|---:|:---:|---|
| `SENTRY_NODESTORE_OPENSEARCH_NUMBER_OF_SHARDS` | `3` | No | Number of primary shards per daily index. Must be an integer. |
| `SENTRY_NODESTORE_OPENSEARCH_NUMBER_OF_REPLICA` | `1` | No | Number of replicas per daily index. Must be an integer. |
| `SENTRY_NODESTORE_OPENSEARCH_INDEX_PATTERN` | `sentry-*` | No | Must be a single value (one pattern). Accepts a plain string, a JSON array with exactly one item, or a comma-separated list that resolves to exactly one item. Used for the composable index template. |
| `SENTRY_NODESTORE_OPENSEARCH_INDEX_CODEC` | `zstd` | No | Index codec. Use `best_compression` if your cluster doesn’t support `zstd`. |
| `SENTRY_NODESTORE_OPENSEARCH_INDEX_PREFIX` | — | No | Optional prefix for index names. If set (e.g., `dev`), indices become `sentry-<prefix>-YYYY-MM-DD` (e.g., `sentry-dev-2025-08-29`). If not set, indices are `sentry-YYYY-MM-DD`. |
## Configuration
Set the Sentry NodeStore backend and provide an OpenSearch client. The backend reads its settings from uppercase environment variables (see “Environment variables” below).
### Option A: Use method [self-hosted](https://github.com/getsentry/self-hosted/blob/master/sentry/sentry.conf.example.py)
```python
# sentry.conf.py
import os
from opensearchpy import OpenSearch
# Option 1 (preferred): set these in your process environment (Docker/Compose, systemd, shell)
# Option 2: set here before Sentry constructs the NodeStore:
os.environ["SENTRY_NODESTORE_OPENSEARCH_INDEX_PREFIX"] = "dev" # optional
os.environ["SENTRY_NODESTORE_OPENSEARCH_NUMBER_OF_SHARDS"] = "1" # default: 3
os.environ["SENTRY_NODESTORE_OPENSEARCH_NUMBER_OF_REPLICA"] = "0" # default: 1
os.environ["SENTRY_NODESTORE_OPENSEARCH_INDEX_PATTERN"] = "sentry-dev-*" # single value
# Optional (default: zstd). Use best_compression for broad compatibility across cluster versions:
os.environ["SENTRY_NODESTORE_OPENSEARCH_INDEX_CODEC"] = "best_compression"
os_client = OpenSearch(
["https://admin:myStrongPassword123!@opensearch:9200"],
http_compress=True,
verify_certs=False, # demo TLS only; in production, verify with a real CA/fingerprint
timeout=60,
ssl_show_warn=False,
# For production TLS, prefer certificate verification or fingerprint pinning:
# ssl_assert_fingerprint="AA:BB:CC:...:ZZ"
)
SENTRY_NODESTORE = "sentry_opensearch_nodestore.backend.OpenSearchNodeStorage"
SENTRY_NODESTORE_OPTIONS = {
"es": os_client,
# Optional overrides:
# "alias_name": "sentry",
# "template_name": "sentry",
# "index": "sentry-{prefix}-{date}", # default; resolved by env INDEX_PREFIX -> "sentry-<prefix>-{date}" or "sentry-{date}"
# "refresh": False, # default; set True only if you require read-after-write in-line
}
# Keep Sentry defaults
from sentry.conf.server import * # noqa
# Ensure the app is importable (if needed for Django discovery)
INSTALLED_APPS = list(INSTALLED_APPS)
INSTALLED_APPS.append("sentry_opensearch_nodestore")
INSTALLED_APPS = tuple(INSTALLED_APPS)
```
### Option B: Use helm chart [sentry] (https://github.com/sentry-kubernetes/charts/tree/develop/charts/sentry)
```yaml
sentryConfPy: |
# No Python Extension Config Given
from opensearchpy import OpenSearch
os.environ["SENTRY_NODESTORE_OPENSEARCH_INDEX_PREFIX"] = "dev"
os.environ["SENTRY_NODESTORE_OPENSEARCH_NUMBER_OF_REPLICA"] = "0"
os.environ["SENTRY_NODESTORE_OPENSEARCH_NUMBER_OF_SHARDS"] = "1"
os.environ["SENTRY_NODESTORE_OPENSEARCH_INDEX_PATTERN"] = "sentry-dev-*"
# Optional (default is zstd):
os.environ["SENTRY_NODESTORE_OPENSEARCH_INDEX_CODEC"] = "best_compression"
os_client = OpenSearch(
['https://admin:myStrongPassword123!@10.129.0.36:9200'],
http_compress=True,
verify_certs=False,
timeout=60,
ssl_show_warn=False
)
SENTRY_NODESTORE = 'sentry_opensearch_nodestore.OpenSearchNodeStorage'
SENTRY_NODESTORE_OPTIONS = {
'es': os_client,
'refresh': False
}
INSTALLED_APPS = list(INSTALLED_APPS)
INSTALLED_APPS.append('sentry_opensearch_nodestore')
INSTALLED_APPS = tuple(INSTALLED_APPS)
```
Raw data
{
"_id": null,
"home_page": "https://github.com/airdry/sentry-opensearch-nodestore",
"name": "sentry-opensearch-nodestore",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.8",
"maintainer_email": null,
"keywords": "sentry, opensearch, nodestore",
"author": "airdry",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/59/2d/7cc5fc522f59c6c1c713f1b10983350c4075c4d2ccde2f63813cae747830/sentry_opensearch_nodestore-1.1.1.tar.gz",
"platform": null,
"description": "# sentry-opensearch-nodestore\n\nSentry NodeStore backend powered by OpenSearch.\n\nSupported Sentry 24.x / 25.x and OpenSearch 2.x / 3.x.\n\nUse an OpenSearch cluster to store Sentry NodeStore payloads for better scalability and simpler retention (delete old indices) compared to PostgreSQL.\n\n- Stores payloads as compressed, non-indexed data\n- Daily indices with optional prefix (e.g., `sentry-YYYY-MM-DD` or `sentry-<prefix>-YYYY-MM-DD`)\n- Reads/deletes via alias (default: `sentry`)\n- Composable index templates (`/_index_template`)\n\n---\n\n## Why OpenSearch for NodeStore\n\n- Horizontal scalability by adding data nodes\n- Sharding and replication for throughput and resilience\n- Automatic rebalancing when cluster grows\n- Cleanup is fast and reliable by dropping old daily indices (vs. large PostgreSQL tables)\n\n---\n\n## Installation\n\nInstall the package for your Sentry deployment.\n\n### Option A: From PyPI\n\n```sh\npip install sentry-opensearch-nodestore\n```\n\n### Option B: Rebuild Sentry image with this backend\n\n```dockerfile\nFROM getsentry/sentry:25.8.0\nRUN pip install sentry-opensearch-nodestore\n\n```\n---\n## Environment variables\n| Variable | Default | Required | Notes |\n|---|---:|:---:|---|\n| `SENTRY_NODESTORE_OPENSEARCH_NUMBER_OF_SHARDS` | `3` | No | Number of primary shards per daily index. Must be an integer. |\n| `SENTRY_NODESTORE_OPENSEARCH_NUMBER_OF_REPLICA` | `1` | No | Number of replicas per daily index. Must be an integer. |\n| `SENTRY_NODESTORE_OPENSEARCH_INDEX_PATTERN` | `sentry-*` | No | Must be a single value (one pattern). Accepts a plain string, a JSON array with exactly one item, or a comma-separated list that resolves to exactly one item. Used for the composable index template. |\n| `SENTRY_NODESTORE_OPENSEARCH_INDEX_CODEC` | `zstd` | No | Index codec. Use `best_compression` if your cluster doesn\u2019t support `zstd`. |\n| `SENTRY_NODESTORE_OPENSEARCH_INDEX_PREFIX` | \u2014 | No | Optional prefix for index names. If set (e.g., `dev`), indices become `sentry-<prefix>-YYYY-MM-DD` (e.g., `sentry-dev-2025-08-29`). If not set, indices are `sentry-YYYY-MM-DD`. |\n\n\n\n## Configuration\n\nSet the Sentry NodeStore backend and provide an OpenSearch client. The backend reads its settings from uppercase environment variables (see \u201cEnvironment variables\u201d below).\n\n### Option A: Use method [self-hosted](https://github.com/getsentry/self-hosted/blob/master/sentry/sentry.conf.example.py)\n\n```python\n# sentry.conf.py\nimport os\nfrom opensearchpy import OpenSearch\n\n# Option 1 (preferred): set these in your process environment (Docker/Compose, systemd, shell)\n# Option 2: set here before Sentry constructs the NodeStore:\nos.environ[\"SENTRY_NODESTORE_OPENSEARCH_INDEX_PREFIX\"] = \"dev\" # optional\nos.environ[\"SENTRY_NODESTORE_OPENSEARCH_NUMBER_OF_SHARDS\"] = \"1\" # default: 3\nos.environ[\"SENTRY_NODESTORE_OPENSEARCH_NUMBER_OF_REPLICA\"] = \"0\" # default: 1\nos.environ[\"SENTRY_NODESTORE_OPENSEARCH_INDEX_PATTERN\"] = \"sentry-dev-*\" # single value\n# Optional (default: zstd). Use best_compression for broad compatibility across cluster versions:\nos.environ[\"SENTRY_NODESTORE_OPENSEARCH_INDEX_CODEC\"] = \"best_compression\"\n\nos_client = OpenSearch(\n [\"https://admin:myStrongPassword123!@opensearch:9200\"],\n http_compress=True,\n verify_certs=False, # demo TLS only; in production, verify with a real CA/fingerprint\n timeout=60,\n ssl_show_warn=False,\n # For production TLS, prefer certificate verification or fingerprint pinning:\n # ssl_assert_fingerprint=\"AA:BB:CC:...:ZZ\"\n)\n\nSENTRY_NODESTORE = \"sentry_opensearch_nodestore.backend.OpenSearchNodeStorage\"\nSENTRY_NODESTORE_OPTIONS = {\n \"es\": os_client,\n # Optional overrides:\n # \"alias_name\": \"sentry\",\n # \"template_name\": \"sentry\",\n # \"index\": \"sentry-{prefix}-{date}\", # default; resolved by env INDEX_PREFIX -> \"sentry-<prefix>-{date}\" or \"sentry-{date}\"\n # \"refresh\": False, # default; set True only if you require read-after-write in-line\n}\n\n# Keep Sentry defaults\nfrom sentry.conf.server import * # noqa\n\n# Ensure the app is importable (if needed for Django discovery)\nINSTALLED_APPS = list(INSTALLED_APPS)\nINSTALLED_APPS.append(\"sentry_opensearch_nodestore\")\nINSTALLED_APPS = tuple(INSTALLED_APPS)\n\n```\n\n### Option B: Use helm chart [sentry] (https://github.com/sentry-kubernetes/charts/tree/develop/charts/sentry)\n\n```yaml\n sentryConfPy: |\n # No Python Extension Config Given\n from opensearchpy import OpenSearch\n os.environ[\"SENTRY_NODESTORE_OPENSEARCH_INDEX_PREFIX\"] = \"dev\"\n os.environ[\"SENTRY_NODESTORE_OPENSEARCH_NUMBER_OF_REPLICA\"] = \"0\"\n os.environ[\"SENTRY_NODESTORE_OPENSEARCH_NUMBER_OF_SHARDS\"] = \"1\"\n os.environ[\"SENTRY_NODESTORE_OPENSEARCH_INDEX_PATTERN\"] = \"sentry-dev-*\"\n # Optional (default is zstd):\n os.environ[\"SENTRY_NODESTORE_OPENSEARCH_INDEX_CODEC\"] = \"best_compression\"\n os_client = OpenSearch(\n ['https://admin:myStrongPassword123!@10.129.0.36:9200'],\n http_compress=True,\n verify_certs=False,\n timeout=60,\n ssl_show_warn=False \n )\n SENTRY_NODESTORE = 'sentry_opensearch_nodestore.OpenSearchNodeStorage'\n SENTRY_NODESTORE_OPTIONS = {\n 'es': os_client,\n 'refresh': False\n }\n\n INSTALLED_APPS = list(INSTALLED_APPS)\n INSTALLED_APPS.append('sentry_opensearch_nodestore')\n INSTALLED_APPS = tuple(INSTALLED_APPS)\n```",
"bugtrack_url": null,
"license": "MIT",
"summary": "An Sentry NodeStorage backend for OpenSearch",
"version": "1.1.1",
"project_urls": {
"Homepage": "https://github.com/airdry/sentry-opensearch-nodestore",
"Repository": "https://github.com/airdry/sentry-opensearch-nodestore"
},
"split_keywords": [
"sentry",
" opensearch",
" nodestore"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "d29fcceed75da9ff8c18329a308cf7cf0c4ded331a0540693084a18645015130",
"md5": "cb200661834b5eebb2b7d65da2905524",
"sha256": "78903c3526a773f67df7227f7bc66a6dab12335dfe67507ce11be0791d0f6e9a"
},
"downloads": -1,
"filename": "sentry_opensearch_nodestore-1.1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "cb200661834b5eebb2b7d65da2905524",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.8",
"size": 8142,
"upload_time": "2025-08-29T12:38:15",
"upload_time_iso_8601": "2025-08-29T12:38:15.165274Z",
"url": "https://files.pythonhosted.org/packages/d2/9f/cceed75da9ff8c18329a308cf7cf0c4ded331a0540693084a18645015130/sentry_opensearch_nodestore-1.1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "592d7cc5fc522f59c6c1c713f1b10983350c4075c4d2ccde2f63813cae747830",
"md5": "932105e20c7ba09cdb9aa7b9e772178a",
"sha256": "d5ded6b9bf8218cea946fb238d610268fba967070ba364b170ff46a9d3cc8665"
},
"downloads": -1,
"filename": "sentry_opensearch_nodestore-1.1.1.tar.gz",
"has_sig": false,
"md5_digest": "932105e20c7ba09cdb9aa7b9e772178a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.8",
"size": 6988,
"upload_time": "2025-08-29T12:38:16",
"upload_time_iso_8601": "2025-08-29T12:38:16.633925Z",
"url": "https://files.pythonhosted.org/packages/59/2d/7cc5fc522f59c6c1c713f1b10983350c4075c4d2ccde2f63813cae747830/sentry_opensearch_nodestore-1.1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-29 12:38:16",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "airdry",
"github_project": "sentry-opensearch-nodestore",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "sentry-opensearch-nodestore"
}