---
title: "Flagpole"
url: https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole/
---

# Flagpole | Sentry Docs

Flagpole is Sentry's internal, sentry-options-backed feature flagging standard.

Features are defined in the [`sentry-options-automator`](https://github.com/getsentry/sentry-options-automator) repository as yaml objects within the [`option-values/getsentry-features/default/values.yaml`](https://github.com/getsentry/sentry-options-automator/blob/main/option-values/getsentry-features/default/values.yaml) file. This is symlinked to the legacy `options/default/flagpole.yaml` file. These yaml options are stored in a Kubernetes ConfigMap, and mounted as files in each of our deployments.

Here's the example configuration for a feature named `organizations:is_sentry`, which is enabled for an organization with the slug `sentry`.

```yaml
options:
  "feature.organizations:is_sentry":
    created_at: "2024-06-01T00:00:00.000000"
    enabled: false
    owner:
      team: dev-infra
      email: foo@sentry.io
    segments:
      - conditions:
          - operator: in
            value:
              - sentry
            property: organization_slug
        name: is_sentry
        rollout: 100
```

## [Flagpole Feature Yaml Structure](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole.md#flagpole-feature-yaml-structure)

* `created_at`

  The ISO 8601 datetime of when the feature was added to the config yaml.

* `enabled` \[optional]

  The global toggle for the feature flag. Defaults to `True`. Setting this to `False` can be a break-glass safety valve to disable a feature without having to modify existing segments or conditions.

* `segments`

  A wrapper around a list of conditions, acting as a logical grouping of customers/entities to enable the feature flag for. Segments allow you to create `OR` operations with other segments, meaning at least one segment must evaluate to `True` for a feature to be granted. Segments are checked in order, and the first segment which passes decides the rollout. If an empty segments list is provided, the feature will evaluate to `False`.

### [Owner](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole.md#owner)

* `team`

  The name of the team that owns this feature flag

* `email` \[optional]

  The email of the team that owns this feature flag

### [Segments](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole.md#segments)

* `conditions`

  A list of predicates to evaluate for the feature flag to be enabled for this segment. All conditions in a segment must be evaluate to `True` in order for the segment to be enabled. If no conditions are defined, the segment will evaluate to `False`.

* `name`

  A brief description of the segment identifying its intended purpose.

* `rollout` \[optional]

  A percentage of entities to enable the feature for, defined as an integer between `0` - `100`. Defaults to `100`.

### [Conditions](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole.md#conditions)

* `property`

  The property name of the evaluation context entry to match against.

* `value`

  The expected value to compare against the evaluation context's property value, using the provided operator.

* `operator`

  The comparison strategy to apply between the evaluation context's property value and the provided condition value.

### [Operators](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole.md#operators)

Flagpole currently supports the following `operator` types:

* `in`

  Given a list of values, evaluates to `True` if the context property value exists in the list.

* `not_in`

  The inverse of `in`, evaluates to `True` if the context property value does not exist in the provided list.

* `contains`

  Given a single int, string, float, or boolean value, evaluates to `True` if the context property value is a list containing the provided value.

* `not_contains`

  The inverse of `contains`, evaluates to `True` if the context property value is a list which does not contain the provided value.

* `equals`

  Given a single int, string, float, or boolean value, evaluates to `True` if the context property value exactly matches the provided value.

* `not_equals`

  The inverse of `equals`, evaluates to `True` if the context property value does not match the provided value.

* `matches`

  Given a list of patterns, evaluates to `True` if the context property value is a string matching at least one pattern. `*` matches any number of characters, and matching is case-insensitive.

* `not_matches`

  The inverse of `matches`, evaluates to `True` if the context property value does not match any of the provided patterns.

## [Evaluation Contexts](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole.md#evaluation-contexts)

When a feature flag is checked, the caller must provide one or more entity objects for the feature handler to match against. GetSentry's feature handler turns these objects into an evaluation context, a flat dictionary of keys and primitive values, and passes it to [`sentry-options`](https://github.com/getsentry/sentry-options), which evaluates each segment and condition against it to determine whether the feature should be enabled.

### [Context Properties](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole.md#context-properties)

The context is built by a set of context transformer functions, each responsible for one slice of the context, such as organization, project and user.

Here are the properties these transformers surface that you can use in your conditions:

**System Context Properties**

* `sentry_cell` \[str]

  The sentry cell or single-tenant the flag check is being performed in.

* `sentry_region` \[str] *(deprecated — use `sentry_cell` instead)*

  The sentry region or single-tenant the flag check is being performed in. This property is deprecated in favor of `sentry_cell` and will be removed in a future release.

* `sentry_singletenant` \[bool]

  Whether or not the application is operating in a single-tenant environment.

**Organization Context Properties**

* `organization_id` \[int]

  The organization's ID

* `organization_slug` \[str]

  The organization's slug

* `organization_name` \[str]

  The organization's name

* `organization_is-early-adopter` \[bool]

  Whether or not the organization has the `early_adopter` flag enabled

**Project Context Properties (If checking a project feature)**

* `project_id` \[int]

  The project's ID

* `project_slug` \[str]

  The project's slug

* `project_name` \[str]

  The project's name

* `project_platform` \[str]

  The project's platform

**User Context Properties (If checking a flag with an actor)**

* `user_id` \[int]

  The active user's ID

* `user_email` \[str]

  The active user's email address, only set if the address is verified. This should only contain Sentry employee email addresses.

* `user_domain` \[str]

  The domain of the active user's email address.

* `user_is-staff` \[bool]

  Is set to `true` for users that have `is_staff`.

* `user_is-superuser` \[bool]

  Is set to `true` for users that have `is_superuser`.

To see the latest context properties available, check out the repo-specific context transformers:

* [Sentry context transformers](https://github.com/getsentry/sentry/blob/master/src/sentry/features/flagpole_context.py)
* [GetSentry context transformers](https://github.com/getsentry/getsentry/blob/master/getsentry/feature_handlers/sentry_options_context.py)

## [Rolling out a new Flagpole feature](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole.md#rolling-out-a-new-flagpole-feature)

Creating a new Flagpole Feature is currently a 2 step process:

1. Register a new feature in Sentry's `temporary.py` file or GetSentry's `features.py` file with the Flagpole strategy:

```python
features.add(
    "<feature_scope>:<feature_name>",
    OrganizationFeature,
    FeatureHandlerStrategy.FLAGPOLE
)
```

2. Add a new feature object entry to sentry-options-automator's `flagpole.yml` file. The feature's option name must follow the following format in order to be picked up by Flagpole:

```bash
feature.<feature_scope>:<feature_name>
```

The feature config and the registration can be merged in either order. Until the registration is deployed nothing reads the config, and until the config is deployed the feature uses the `default` set in its registration. Make sure the key exactly matches the registered name. A misspelled key isn't reported as an error; the feature just never turns on.

Once the option change is deployed, the feature checks will be active within a minute or two in all environments and regions, without a restart.

## [Using Flagpole in single-tenants](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole.md#using-flagpole-in-single-tenants)

By default, every Flagpole feature is evaluated in single-tenant environments. To roll a feature everywhere except single-tenant environments, you can use the `*_customer_single_tenants` anchor and `sentry_cell` context values in your feature conditions. For example:

```yaml
feature.organizations:cool-new-feature:
  created_at: "2026-06-25"
  enabled: true
  owner:
    team: dev-infra
    email: foo@sentry.io
  segments:
    - name: GA
      rollout: 100
      conditions:
        - operator: not_in
          property: sentry_cell
          value: *_customer_single_tenants
```

Conversely, to roll a feature out to specific cells only, match on `sentry_cell`:

```yaml
- name: multi-region rollout
  rollout: 100
  conditions:
    - operator: in
      property: sentry_cell
      value:
        - us
        - de
```

## [Testing a Flagpole feature locally](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole.md#testing-a-flagpole-feature-locally)

To check how a feature config evaluates for a given context without running a devserver, use `sentry-options-cli eval`. `devenv sync` in `sentry-options-automator` installs it:

```bash
sentry-options-cli eval --values options/default/flagpole.yaml \
  --flag organizations:is_sentry \
  --context '{"organization_id": 1, "organization_slug": "sentry"}'
```

It prints whether the feature is enabled and which segment decided it. Only the context you pass is used, so include any properties your conditions depend on.

You can also test a flagpole feature flag locally using the GetSentry devserver. Because the feature handler for Flagpole only exists in GetSentry, it's not possible to test flagpole features using the Sentry devserver at this time.

Add your feature config to `sentry-options/values/getsentry-features/values.json` in your GetSentry checkout, creating the file if it doesn't exist:

```json
{
  "options": {
    "feature.organizations:is_sentry": {
      "created_at": "2024-06-01T00:00:00.000000",
      "enabled": true,
      "owner": { "team": "dev-infra", "email": "foo@sentry.io" },
      "segments": [
        {
          "name": "is_sentry",
          "rollout": 100,
          "conditions": [
            {
              "property": "organization_slug",
              "operator": "in",
              "value": ["sentry"]
            }
          ]
        }
      ]
    }
  }
}
```

Your devserver picks up changes to this file within a few seconds, without a restart.

To unset your feature, remove it from the file. Don't commit this file.

## [Removing Flagpole features](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole.md#removing-flagpole-features)

Flags should be removed in a specific order so the feature doesn't change for anyone while it's being cleaned up:

1. Remove all flag checks from the codebase, keeping the code path that should remain. You can remove the [feature registration](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole.md#rolling-out-a-new-flagpole-feature) from GetSentry or Sentry in the same change.
2. Once that change has been deployed to all environments, remove the feature flag config from options automator.

If the config is removed while flag checks remain, those checks fall back to the `default` in the feature's registration, which is usually `False`, so the feature turns off for everyone. Nothing flags a config whose registration has been removed, so don't skip step 2.
