# How to Configure Feature Flags in Apache Superset: Enabling and Disabling Experimental Functionality

> **Apache Superset controls experimental and optional functionality through a three-layer feature flag system defined in [`superset/config.py`](https://github.com/apache/superset/blob/main/superset/config.py) and managed by `Fea...

- Repository: [The Apache Software Foundation/superset](https://github.com/apache/superset)
- Tags: how-to-guide
- Published: 2026-03-03

---

**Apache Superset controls experimental and optional functionality through a three-layer feature flag system defined in [`superset/config.py`](https://github.com/apache/superset/blob/main/superset/config.py) and managed by `FeatureFlagManager`, allowing static configuration, environment variables, and dynamic per-request evaluation.**

Apache Superset uses **feature flags** to gate experimental features, beta visualizations, and legacy systems that are being deprecated. When you configure feature flags in Superset, you modify boolean switches that the application checks at runtime to determine which code paths to execute. The flag system is implemented across three distinct configuration layers that merge together when the Flask application initializes.

## Understanding the Feature Flag Architecture

The Superset feature flag system operates through a hierarchical merge of three distinct layers defined in [`superset/config.py`](https://github.com/apache/superset/blob/main/superset/config.py) and processed by [`superset/utils/feature_flag_manager.py`](https://github.com/apache/superset/blob/main/superset/utils/feature_flag_manager.py).

### Default Configuration Layer

The baseline feature flags are defined in [`superset/config.py`](https://github.com/apache/superset/blob/main/superset/config.py) within the `DEFAULT_FEATURE_FLAGS` dictionary (lines 539-604). These flags are grouped by lifecycle status—`development`, `testing`, `stable`, and `deprecated`—and ship with every Superset installation. This dictionary represents the factory defaults that apply unless explicitly overridden.

### User Override Layer

Custom configurations belong in [`superset_config.py`](https://github.com/apache/superset/blob/main/superset_config.py) (located on your `PYTHONPATH`) within a `FEATURE_FLAGS` dictionary (lines 842-844 in [`superset/config.py`](https://github.com/apache/superset/blob/main/superset/config.py)). When the Flask app initializes, the `FeatureFlagManager` merges these values on top of the defaults using Python's `dict.update()` method. Any flag defined here replaces the corresponding default for your entire deployment.

### Dynamic Evaluation Layer

For advanced use cases, [`superset/config.py`](https://github.com/apache/superset/blob/main/superset/config.py) supports two optional callable hooks (lines 848-864):

- **`GET_FEATURE_FLAGS_FUNC`**: Receives the entire merged flag dictionary and returns a modified copy
- **`IS_FEATURE_ENABLED_FUNC`**: Receives a single flag name and default value, returning a boolean

These functions enable per-request, per-user, or progressive rollout logic by intercepting flag queries at runtime.

## How to Configure Feature Flags in Superset

You can configure feature flags through static configuration files, environment variables, or dynamic functions depending on your deployment requirements.

### Static Configuration via superset_config.py

Create or modify [`superset_config.py`](https://github.com/apache/superset/blob/main/superset_config.py) in your Python path to override specific flags:

```python

# /path/to/your/superset_config.py

FEATURE_FLAGS = {
    # Enable the new Table-V2 (AG-Grid) visualization

    "AG_GRID_TABLE_ENABLED": True,
    # Turn off the legacy tagging system

    "TAGGING_SYSTEM": False,
}

```

This file is automatically imported at the end of [`superset/config.py`](https://github.com/apache/superset/blob/main/superset/config.py), and your `FEATURE_FLAGS` dictionary updates the defaults before the application starts serving requests.

### Environment Variable Overrides

Superset automatically parses environment variables matching the pattern `SUPERSET_FEATURE_<FLAG_NAME>` as booleans during configuration loading (lines 528-535 in [`superset/config.py`](https://github.com/apache/superset/blob/main/superset/config.py)). This approach is ideal for containerized deployments:

```bash
export SUPERSET_FEATURE_AG_GRID_TABLE_ENABLED=true
export SUPERSET_FEATURE_TAGGING_SYSTEM=false

```

These values are injected into `DEFAULT_FEATURE_FLAGS` at startup, providing a declarative way to toggle features without modifying Python files.

### Dynamic Rollouts with Custom Functions

For percentage-based rollouts or user-specific features, implement `GET_FEATURE_FLAGS_FUNC` in [`superset_config.py`](https://github.com/apache/superset/blob/main/superset_config.py):

```python

# superset_config.py

from flask import g

def GET_FEATURE_FLAGS_FUNC(all_flags):
    """
    Enable ALERTS_ATTACH_REPORTS only for beta-test users.
    """
    if getattr(g, "user", None) and g.user.username.startswith("beta_"):
        all_flags["ALERTS_ATTACH_REPORTS"] = True
    return all_flags

```

The `FeatureFlagManager` passes a **deep copy** of the merged dictionary to this function, ensuring your mutations affect only the current request context.

Alternatively, use `IS_FEATURE_ENABLED_FUNC` for per-flag logic without evaluating the entire dictionary:

```python

# superset_config.py

def IS_FEATURE_ENABLED_FUNC(name, default):
    # Enable SQLLAB_BACKEND_PERSISTENCE for internal users only

    if name == "SQLLAB_BACKEND_PERSISTENCE":
        return getattr(g, "user", None) and g.user.is_internal
    return default

```

## Core Implementation Details

The `FeatureFlagManager` class in [`superset/utils/feature_flag_manager.py`](https://github.com/apache/superset/blob/main/superset/utils/feature_flag_manager.py) (lines 22-59) handles the merging logic during application initialization:

```python

# Inside FeatureFlagManager.init_app()

self._feature_flags = app.config["DEFAULT_FEATURE_FLAGS"]      # defaults

self._feature_flags.update(app.config["FEATURE_FLAGS"])       # user overrides

```

When `is_feature_enabled()` is called, the manager first checks for `IS_FEATURE_ENABLED_FUNC`. If defined, it invokes the callable with the flag name and default value. Otherwise, it consults the merged dictionary. For `get_feature_flags()`, the manager invokes `GET_FEATURE_FLAGS_FUNC` if present, passing a deep copy of the current flag state to prevent accidental global modifications.

The merge order is immutable: **defaults → environment variables → static overrides → dynamic functions**. This hierarchy ensures that explicit code configuration takes precedence over environment settings, while dynamic functions have the final say for the current request context.

## Summary

- **Three-layer system**: Superset feature flags merge from `DEFAULT_FEATURE_FLAGS` (defaults), `FEATURE_FLAGS` (static overrides), and optional callable functions (dynamic evaluation).
- **Configuration location**: Define static overrides in [`superset_config.py`](https://github.com/apache/superset/blob/main/superset_config.py) on your `PYTHONPATH`, referenced in [`superset/config.py`](https://github.com/apache/superset/blob/main/superset/config.py) lines 842-844.
- **Environment support**: Set `SUPERSET_FEATURE_<FLAG_NAME>` variables for containerized deployments without code changes.
- **Dynamic control**: Implement `GET_FEATURE_FLAGS_FUNC` or `IS_FEATURE_ENABLED_FUNC` in [`superset_config.py`](https://github.com/apache/superset/blob/main/superset_config.py) for per-user or progressive rollout logic.
- **Manager implementation**: The `FeatureFlagManager` in [`superset/utils/feature_flag_manager.py`](https://github.com/apache/superset/blob/main/superset/utils/feature_flag_manager.py) caches merged flags per request and handles deep copies to isolate dynamic modifications.

## Frequently Asked Questions

### How do I check if a feature flag is enabled in Superset code?

Query the flag using `is_feature_enabled()` from the feature flag manager or import the utility function. According to [`superset/utils/feature_flag_manager.py`](https://github.com/apache/superset/blob/main/superset/utils/feature_flag_manager.py), this method checks for `IS_FEATURE_ENABLED_FUNC` first, then falls back to the merged dictionary containing defaults and your [`superset_config.py`](https://github.com/apache/superset/blob/main/superset_config.py) overrides.

### Can I enable feature flags for specific users only?

Yes. Define `GET_FEATURE_FLAGS_FUNC` or `IS_FEATURE_ENABLED_FUNC` in your [`superset_config.py`](https://github.com/apache/superset/blob/main/superset_config.py) to inspect the Flask `g.user` object. As shown in the dynamic evaluation examples, you can conditionally enable flags based on username prefixes, roles, or custom user attributes before returning the flag dictionary or boolean value.

### What is the difference between DEFAULT_FEATURE_FLAGS and FEATURE_FLAGS?

`DEFAULT_FEATURE_FLAGS` is the dictionary defined in [`superset/config.py`](https://github.com/apache/superset/blob/main/superset/config.py) (lines 539-604) containing Superset's factory settings grouped by lifecycle status. `FEATURE_FLAGS` is the empty dictionary in the same file (lines 842-844) that you populate in your custom [`superset_config.py`](https://github.com/apache/superset/blob/main/superset_config.py) to override those defaults. The `FeatureFlagManager` updates the defaults with your overrides during initialization.

### Do environment variables override superset_config.py settings?

No. The merge order places environment variables between defaults and static configuration. Environment variables matching `SUPERSET_FEATURE_*` are parsed into `DEFAULT_FEATURE_FLAGS` during config loading (lines 528-535), but values explicitly set in your [`superset_config.py`](https://github.com/apache/superset/blob/main/superset_config.py) `FEATURE_FLAGS` dictionary take precedence. Dynamic functions evaluated at request time have the final authority.