Skip to main content

Overview

Values are configuration settings that override schema defaults. They are authored as YAML files in the sentry-options-automator repository, organized by namespace and target. The CLI tool validates values against schemas, merges target-specific overrides with defaults, and generates JSON files for deployment to Kubernetes ConfigMaps.

Values format

YAML input format

Values are written as YAML files with a single options key:
options:
  system.url-prefix: "https://custom.sentry.io"
  traces.sample-rate: 0.5
  feature.enabled: true
  feature.allowed-orgs:
    - getsentry
    - sentry
The top-level options key is required. All option keys must be defined in the corresponding schema.

JSON output format

The CLI generates JSON files with the merged values:
{
  "options": {
    "system.url-prefix": "https://custom.sentry.io",
    "traces.sample-rate": 0.5,
    "feature.enabled": true,
    "feature.allowed-orgs": ["getsentry", "sentry"]
  },
  "generated_at": "2024-01-21T18:30:00.123456+00:00",
  "commit_sha": "abc123def456"
}
Metadata fields:
  • generated_at - ISO 8601 timestamp of generation
  • commit_sha - Git commit SHA that triggered the generation
These metadata fields enable propagation delay tracking in the hot-reload observability metrics.

Directory structure

In sentry-options-automator

Values are organized by namespace and target:
option-values/
├── seer/
│   ├── default/                 # Base values (required)
│   │   └── values.yaml
│   ├── us/                      # US region overrides
│   │   └── values.yaml
│   └── de/                      # DE region overrides
│       └── values.yaml
├── relay/
│   ├── default/
│   │   └── values.yaml
│   └── us/
│       └── values.yaml
└── getsentry/
    ├── default/
    │   ├── core.yaml            # Can split into multiple files
    │   └── features.yaml
    └── s4s/
        └── overrides.yaml
You can split default values across multiple YAML files in the same directory. They will be merged together during processing.

Target system

What is a target?

A target represents a deployment environment or region (e.g., us, de, s4s). Each target gets its own ConfigMap with values merged from default/ plus target-specific overrides.

Target requirements

  1. Every namespace must have a default target
  2. Non-default targets inherit from default
  3. Target-specific values override defaults
  4. The default target is not deployed directly - it serves as the base for other targets

Override behavior

Target values are merged with defaults using a simple override strategy: default/values.yaml:
options:
  feature.enabled: false
  feature.rate-limit: 100
  feature.endpoint: "https://api.example.com"
us/values.yaml:
options:
  feature.enabled: true
  feature.rate-limit: 200
Merged result for us target:
{
  "options": {
    "feature.enabled": true,        // overridden
    "feature.rate-limit": 200,      // overridden
    "feature.endpoint": "https://api.example.com"  // inherited from default
  }
}

Target naming rules

Target directory names must follow Kubernetes naming conventions:
  • Allowed characters: lowercase alphanumeric, -, .
  • Must start and end: with alphanumeric character
  • No uppercase: US is rejected, use us
  • No underscores: us_west is rejected, use us-west
Common target names:
  • default - Required base target
  • us - US region
  • de - DE region
  • s4s - Sentry for Sentry
  • saas - SaaS deployment

ConfigMap generation

Generation workflow

The CI/CD pipeline generates one ConfigMap per namespace/target combination:
# For each namespace and non-default target:
sentry-options-cli write \
  --schemas schemas/ \
  --root option-values/ \
  --output-format configmap \
  --namespace seer \
  --target us \
  --commit-sha "$COMMIT_SHA" \
  --commit-timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
This generates a ConfigMap named sentry-options-seer containing the merged values for the us target.

ConfigMap naming

ConfigMaps are named: sentry-options-{namespace} Examples:
  • sentry-options-seer - Contains seer namespace values for specific target
  • sentry-options-relay - Contains relay namespace values for specific target
The target is not in the ConfigMap name - different targets are deployed to different clusters.

ConfigMap structure

apiVersion: v1
kind: ConfigMap
metadata:
  name: sentry-options-seer
  namespace: default
data:
  values.json: |
    {
      "options": {
        "feature.enabled": true,
        "feature.rate-limit": 200
      },
      "generated_at": "2024-01-21T18:30:00.123456+00:00",
      "commit_sha": "abc123def456"
    }
The ConfigMap contains a single file values.json with the merged options.

Mounting ConfigMaps

In the ops repo, add pod annotations to your deployment:
spec:
  template:
    metadata:
      annotations:
        options.sentry.io/inject: 'true'
        options.sentry.io/namespace: seer
The sentry-options injector automatically mounts the ConfigMap to:
/etc/sentry-options/values/seer/values.json
For multiple namespaces:
options.sentry.io/namespace: seer-code-review,seer

Validation

Schema validation

Values are validated against their namespace schema:
sentry-options-cli validate-values \
  --schemas schemas/ \
  --root option-values/
Validation checks:
  1. Unknown options: Rejected (catches typos)
  2. Type mismatches: String where integer expected → Error
  3. Invalid values: Values that don’t match schema constraints
  4. Missing required fields: Top-level options key required

Validation errors

Example error output:
Value error for seer:
	feature.rate-limit "not-a-number" is not of type "integer"
	unknown-option Additional properties are not allowed
The CLI validates:
  • All values in default/ target
  • All values in override targets (after merging)
  • All namespaces in the option-values directory
If any namespace fails validation, the entire operation fails. This prevents partial deployments with invalid configuration.

Default values and schema fallback

The client libraries have a fallback chain for option values:
  1. Explicit value: Check if option is set in values.json
  2. Schema default: Return default from schema if not set
  3. Error: If option doesn’t exist in schema
# Schema defines: "feature.enabled": {"type": "boolean", "default": false}

# Case 1: Value set in ConfigMap
opts.get('feature.enabled')  # Returns: True (from values.json)

# Case 2: ConfigMap doesn't have this option
opts.get('feature.enabled')  # Returns: False (from schema default)

# Case 3: Option not in schema
opts.get('unknown.option')   # Raises: UnknownOptionError
This allows services to start before ConfigMaps are deployed - they’ll use schema defaults until values are applied.

File splitting

You can split values across multiple YAML files in the same target directory:
getsentry/
└── default/
    ├── core.yaml
    ├── features.yaml
    └── experimental.yaml
core.yaml:
options:
  system.url-prefix: "https://sentry.io"
  system.timeout: 30
features.yaml:
options:
  feature.autofix-enabled: true
  feature.grouping-enabled: false
The CLI merges all YAML files in the directory before validation. If the same option appears in multiple files, the behavior is undefined - avoid duplicates.

Target-to-cluster mapping

The CD pipeline maps targets to Kubernetes clusters:
# Example mapping (configured in CD pipeline)
targets:
  us:
    clusters:
      - us-west-2
      - us-east-1
  de:
    clusters:
      - eu-central-1
  s4s:
    clusters:
      - s4s-production
Each target’s ConfigMap is deployed to its configured clusters. The mapping is managed by the platform team.

Updating values workflow

To update configuration values:
  1. Edit YAML files in sentry-options-automator
    # option-values/seer/us/values.yaml
    options:
      feature.enabled: true  # Changed from false
    
  2. CI validates values against schemas (fetched via repos.json)
    sentry-options-cli validate-values --schemas schemas/ --root option-values/
    
  3. Merge to main triggers CD pipeline
  4. CD generates ConfigMaps for each namespace/target
    sentry-options-cli write --namespace seer --target us ...
    
  5. ConfigMaps applied to target clusters
    kubectl apply -f sentry-options-seer.yaml
    
  6. Kubelet syncs ConfigMap to pods (~1-2 minutes)
  7. Client library detects change via file watching (~5 seconds)
  8. New values active - total latency ~1-2 minutes
No pod restart is required. Values are hot-reloaded automatically.

Build docs developers (and LLMs) love