diff --git a/docs/admin_docs/installation/docker-compose.mdx b/docs/admin_docs/installation/docker-compose.mdx
index 04d14915860..9ec86e3c060 100644
--- a/docs/admin_docs/installation/docker-compose.mdx
+++ b/docs/admin_docs/installation/docker-compose.mdx
@@ -223,8 +223,9 @@ compose based installation, edit the `x-superset-image:` line in your `docker-co
`docker-compose-non-dev.yml` files, replacing `apachesuperset.docker.scarf.sh/apache/superset` with
`apache/superset` to pull the image directly from Docker Hub.
-To disable the Scarf telemetry pixel, set the `SCARF_ANALYTICS` environment variable to `False` in
-your terminal and/or in your `docker/.env` file.
+To disable the Scarf telemetry pixel, set the `SCARF_ANALYTICS` environment variable to `false` in
+your `docker/.env` file. This is read at runtime, so it disables the pixel on the pre-built image
+without rebuilding the frontend.
:::
## 3. Log in to Superset
diff --git a/docs/admin_docs/installation/kubernetes.mdx b/docs/admin_docs/installation/kubernetes.mdx
index c18bf803f92..6433564f8bd 100644
--- a/docs/admin_docs/installation/kubernetes.mdx
+++ b/docs/admin_docs/installation/kubernetes.mdx
@@ -136,7 +136,17 @@ init:
:::note
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
-To opt-out of this data collection in your Helm-based installation, edit the `repository:` line in your `helm/superset/values.yaml` file, replacing `apachesuperset.docker.scarf.sh/apache/superset` with `apache/superset` to pull the image directly from Docker Hub.
+There are two independent telemetry channels:
+
+- **Image pulls** (Scarf Gateway): to opt out, edit the `repository:` line in your `helm/superset/values.yaml` file, replacing `apachesuperset.docker.scarf.sh/apache/superset` with `apache/superset` to pull the image directly from Docker Hub.
+- **The analytics pixel** rendered in the UI: to opt out, set the `SCARF_ANALYTICS` environment variable to `false` on the Superset containers via `extraEnv` in your `values.yaml`:
+
+ ```yaml
+ extraEnv:
+ SCARF_ANALYTICS: "false"
+ ```
+
+ This is read at runtime, so it takes effect on the pre-built images without rebuilding the frontend.
:::
### Dependencies
diff --git a/docs/docs/faq.mdx b/docs/docs/faq.mdx
index 2e6ff2fefa8..26c92a4a849 100644
--- a/docs/docs/faq.mdx
+++ b/docs/docs/faq.mdx
@@ -321,8 +321,8 @@ This can be used, for example, to convert UTC time to local time.
Superset uses [Scarf](https://about.scarf.sh/) by default to collect basic telemetry data upon installing and/or running Superset. This data helps the maintainers of Superset better understand which versions of Superset are being used, in order to prioritize patch/minor releases and security fixes.
We use the [Scarf Gateway](https://docs.scarf.sh/gateway/) to sit in front of container registries, the [scarf-js](https://about.scarf.sh/package-sdks) package to track `npm` installations, and a Scarf pixel to gather anonymous analytics on Superset page views.
Scarf purges PII and provides aggregated statistics. Superset users can easily opt out of analytics in various ways documented [here](https://docs.scarf.sh/gateway/#do-not-track) and [here](https://docs.scarf.sh/package-analytics/#as-a-user-of-a-package-using-scarf-js-how-can-i-opt-out-of-analytics).
-Superset maintainers can also opt out of telemetry data collection by setting the `SCARF_ANALYTICS` environment variable to `false` in the Superset container (or anywhere Superset/webpack are run).
-Additional opt-out instructions for Docker users are available on the [Docker Installation](/admin-docs/installation/docker-compose) page.
+You can also opt out of the analytics pixel by setting the `SCARF_ANALYTICS` environment variable to `false`. This is read at runtime, so setting it on the Superset container (for example via `extraEnv` in the Helm chart, or `docker/.env` for Docker Compose) disables the pixel on the pre-built images without rebuilding the frontend.
+Additional opt-out instructions are available on the [Docker Compose](/admin-docs/installation/docker-compose) and [Kubernetes](/admin-docs/installation/kubernetes) installation pages.
## Does Superset have an archive panel or trash bin from which a user can recover deleted assets?
diff --git a/superset-frontend/packages/superset-ui-core/src/components/TelemetryPixel/TelemetryPixel.test.tsx b/superset-frontend/packages/superset-ui-core/src/components/TelemetryPixel/TelemetryPixel.test.tsx
index 7dc5d4e63d6..b7b32ebc3bf 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/TelemetryPixel/TelemetryPixel.test.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/TelemetryPixel/TelemetryPixel.test.tsx
@@ -46,3 +46,19 @@ test('should NOT render the pixel link when FF is off', () => {
const image = document.querySelector('img[src*="scarf.sh"]');
expect(image).not.toBeInTheDocument();
});
+
+test('should NOT render the pixel link when disabled at runtime', () => {
+ process.env.SCARF_ANALYTICS = 'true';
+ render();
+
+ const image = document.querySelector('img[src*="scarf.sh"]');
+ expect(image).not.toBeInTheDocument();
+});
+
+test('should render the pixel link when enabled at runtime', () => {
+ process.env.SCARF_ANALYTICS = 'true';
+ render();
+
+ const image = document.querySelector('img[src*="scarf.sh"]');
+ expect(image).toBeInTheDocument();
+});
diff --git a/superset-frontend/packages/superset-ui-core/src/components/TelemetryPixel/index.tsx b/superset-frontend/packages/superset-ui-core/src/components/TelemetryPixel/index.tsx
index dfe0a7e0057..f0e3062dabd 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/TelemetryPixel/index.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/TelemetryPixel/index.tsx
@@ -23,17 +23,25 @@ interface TelemetryPixelProps {
version?: string;
sha?: string;
build?: string;
+ enabled?: boolean;
}
/**
* Renders a telemetry pixel component to capture anonymous, aggregated telemetry via Scarf.
- * This can be disabled by setting the SCARF_ANALYTICS environment variable to false.
+ *
+ * Telemetry can be disabled in two ways:
+ * - At build time, by setting the SCARF_ANALYTICS environment variable to `false`
+ * (inlined by webpack; only effective when building the frontend yourself).
+ * - At runtime, by passing `enabled={false}`, which the app derives from the
+ * `SCARF_ANALYTICS` backend config exposed via the bootstrap payload. This is
+ * what allows opting out in pre-built images, where the build-time flag is fixed.
*
* @component
* @param {TelemetryPixelProps} props - The props for the TelemetryPixel component.
* @param {string} props.version - The version of Superset that's currently in use.
* @param {string} props.sha - The SHA of Superset that's currently in use.
* @param {string} props.build - The build of Superset that's currently in use.
+ * @param {boolean} props.enabled - Runtime opt-out switch; when false the pixel is not rendered.
* @returns {JSX.Element | null} The rendered TelemetryPixel component.
*/
@@ -43,9 +51,11 @@ export const TelemetryPixel = ({
version = 'unknownVersion',
sha = 'unknownSHA',
build = 'unknownBuild',
+ enabled = true,
}: TelemetryPixelProps): ReactElement | null => {
const pixelPath = `https://apachesuperset.gateway.scarf.sh/pixel/${PIXEL_ID}/${version}/${sha}/${build}`;
- return process.env.SCARF_ANALYTICS === 'false' ? null : (
+ const disabled = !enabled || process.env.SCARF_ANALYTICS === 'false';
+ return disabled ? null : (
(state => state.common.conf);
const [showDatabaseModal, setShowDatabaseModal] = useState(false);
const [showCSVUploadModal, setShowCSVUploadModal] = useState(false);
@@ -781,6 +782,7 @@ const RightMenu = ({
// segments in the Scarf pixel URL.
sha={navbarRight.version_sha || undefined}
build={navbarRight.build_number || undefined}
+ enabled={SCARF_ANALYTICS !== false}
/>
);
diff --git a/superset-frontend/src/features/home/types.ts b/superset-frontend/src/features/home/types.ts
index 633f0bffcfa..09814bdd72f 100644
--- a/superset-frontend/src/features/home/types.ts
+++ b/superset-frontend/src/features/home/types.ts
@@ -36,6 +36,7 @@ export interface ExtensionConfigs {
COLUMNAR_EXTENSIONS: Array;
EXCEL_EXTENSIONS: Array;
HAS_GSHEETS_INSTALLED: boolean;
+ SCARF_ANALYTICS?: boolean;
}
export interface RightMenuProps {
align: 'flex-start' | 'flex-end';
diff --git a/superset/config.py b/superset/config.py
index fdfdbbda513..ac6d39c1ace 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -2336,6 +2336,13 @@ DATABASE_OAUTH2_TIMEOUT = timedelta(seconds=30)
# Enable/disable CSP warning
CONTENT_SECURITY_POLICY_WARNING = True
+# Superset uses Scarf (https://about.scarf.sh/) to collect anonymous, aggregated
+# telemetry via a pixel rendered in the UI. Set the SCARF_ANALYTICS environment
+# variable to "false" to opt out. This value is exposed to the frontend through
+# the bootstrap payload so it takes effect at runtime, including in pre-built
+# images where the webpack build-time flag of the same name cannot be changed.
+SCARF_ANALYTICS = utils.cast_to_boolean(os.environ.get("SCARF_ANALYTICS", True))
+
# Do you want Talisman enabled?
TALISMAN_ENABLED = utils.cast_to_boolean(os.environ.get("TALISMAN_ENABLED", True))
diff --git a/superset/views/base.py b/superset/views/base.py
index aa069d69b23..5ab75a5876a 100644
--- a/superset/views/base.py
+++ b/superset/views/base.py
@@ -125,6 +125,7 @@ FRONTEND_CONF_KEYS = (
"MAPBOX_API_KEY",
"DEFAULT_MAP_RENDERER",
"CSV_STREAMING_ROW_THRESHOLD",
+ "SCARF_ANALYTICS",
)
logger = logging.getLogger(__name__)
diff --git a/tests/unit_tests/views/test_base.py b/tests/unit_tests/views/test_base.py
index 75a59baef44..b32f97ae770 100644
--- a/tests/unit_tests/views/test_base.py
+++ b/tests/unit_tests/views/test_base.py
@@ -76,6 +76,14 @@ def test_default_map_renderer_is_exposed_to_frontend_config() -> None:
assert "DEFAULT_MAP_RENDERER" in FRONTEND_CONF_KEYS
+def test_scarf_analytics_is_exposed_to_frontend_config() -> None:
+ # Exposed at runtime so pre-built images can opt out via the SCARF_ANALYTICS
+ # config/env var (the webpack build-time flag cannot be changed there).
+ from superset.views.base import FRONTEND_CONF_KEYS
+
+ assert "SCARF_ANALYTICS" in FRONTEND_CONF_KEYS
+
+
_FULL_METADATA = {
"version_string": "4.0.0",
"version_sha": "abcdef12",