Deploy Apache Superset 4.x on Kubernetes Using Official Helm Charts
This guide walks through deploying Apache Superset 4.x on a Kubernetes cluster with the official Helm chart, covering prerequisites, adding the Helm repository, customizing values.yaml, installing required dependencies, configuring security, OAuth, alerts, Celery workers, and loading sample data and dashboards.
Preparation
Kubernetes cluster
Helm installed
For a simple single‑node setup, minikube is recommended because it is easy to install on many platforms and works well with the referenced Helm chart.
Minikube installation guide: https://minikube.sigs.k8s.io/docs/start/
Run
Add Superset's Helm repository
helm repo add superset https://apache.github.io/superset
"superset" has been added to your repositoriesSearch the repository for charts
helm search repo superset
NAME CHART VERSION APP VERSION DESCRIPTION
superset/superset 0.1.1 1.0 Apache Superset is a modern, enterprise‑ready b...Create a values.yaml file to override defaults. The upstream defaults are at https://github.com/apache/superset/tree/master/helm/superset/values.yaml
Install and run
helm upgrade --install --values my-values.yaml superset superset/supersetAfter the command runs you should see pods starting, for example:
kubectl get pods
NAME READY STATUS RESTARTS AGE
superset-celerybeat-7cdcc9575f-k6xmc 1/1 Running 0 119s
superset-f5c9c667-dw9lp 1/1 Running 0 4m7s
superset-f5c9c667-fk8bk 1/1 Running 0 4m11s
superset-init-db-zlm9z 0/1 Completed 0 111s
superset-postgresql-0 1/1 Running 0 6d20h
superset-redis-master-0 1/1 Running 0 6d20h
superset-worker-75b48bbcc-jmmjr 1/1 Running 0 4m8s
superset-worker-75b48bbcc-qrq49 1/1 Running 0 4m12sTypical pods (count depends on supersetNode.replicaCount and supersetWorker.replicaCount):
Multiple superset-xxxx-yyyy and superset-worker-xxxx-yyyy pods
One superset-postgresql-0 pod (if PostgreSQL is enabled)
One superset-redis-master-0 pod (if Redis is enabled)
One superset-celerybeat-xxxx-yyyy pod when
supersetCeleryBeat.enabled = trueAccess Superset
The Helm chart creates a Service exposing the UI inside the cluster. To reach it externally configure the Service as LoadBalancer or NodePort, set up an Ingress (adjust hostname, TLS, annotations), or use port‑forwarding: kubectl port-forward superset-xxxx-yyyy :8088 Default login credentials for a fresh install:
user: admin password:
adminImportant Settings
Security Settings
Change default passwords for production. Example PostgreSQL password override:
postgresql:
postgresqlPassword: supersetGenerate a strong SECRET_KEY (e.g., openssl rand -base64 42) and set it via configOverrides:
configOverrides:
secret: |
SECRET_KEY = 'YOUR_OWN_RANDOM_GENERATED_SECRET_KEY'To rotate an existing key replace the default thisISaSECRET_1234 with a new value.
Dependencies
Install Python DB‑API drivers and SQLAlchemy dialects for required data sources. Example bootstrap script installing BigQuery and Elasticsearch drivers:
bootstrapScript: |
#!/bin/bash
pip install psycopg2==2.9.6 \
sqlalchemy-bigquery==1.6.1 \
elasticsearch-dbapi==0.2.5 &&
if [ ! -f ~/bootstrap ]; then echo "Running Superset with uid {{ .Values.runAsUser }}" > ~/bootstrap; fisuperset_config.py
Extend the minimal default superset_config.py via configOverrides. Example enabling proxy fix and dynamic plugins:
configOverrides:
my_override: |
ENABLE_PROXY_FIX = True
FEATURE_FLAGS = {
"DYNAMIC_PLUGINS": True
}Values from values.yaml can be referenced, e.g., {{ .Values.ingress.hosts[0] }} resolves to the external hostname.
Environment Variables
Pass key/value pairs through extraEnv and secret values through extraSecretEnv. Access them in superset_config.py with os.getenv("VAR"):
extraEnv:
SMTP_HOST: smtp.gmail.com
SMTP_USER: [email protected]
SMTP_PORT: "587"
SMTP_MAIL_FROM: [email protected]
extraSecretEnv:
SMTP_PASSWORD: xxxx
configOverrides:
smtp: |
import ast
SMTP_HOST = os.getenv("SMTP_HOST","localhost")
SMTP_STARTTLS = ast.literal_eval(os.getenv("SMTP_STARTTLS", "True"))
SMTP_SSL = ast.literal_eval(os.getenv("SMTP_SSL", "False"))
SMTP_USER = os.getenv("SMTP_USER","superset")
SMTP_PORT = os.getenv("SMTP_PORT",25)
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD","superset")
SMTP_MAIL_FROM = os.getenv("SMTP_MAIL_FROM","[email protected]")System Packages
Install additional OS packages by overriding the container command before the application starts:
supersetWorker:
command:
- /bin/sh
- -c
- |
apt update
apt install -y somepackage
apt autoremove -yqq --purge
apt clean
# Run celery worker
. {{ .Values.configMountPath }}/superset_bootstrap.sh; celery --app=superset.tasks.celery_app:app workerData Sources
Declare data sources via extraConfigs in values.yaml:
extraConfigs:
import_datasources.yaml: |
databases:
- allow_file_upload: true
allow_ctas: true
allow_cvas: true
database_name: example-db
extra: "{
\"metadata_params\": {},
\"engine_params\": {},
\"metadata_cache_timeout\": {}
}"
sqlalchemy_uri: example://example-db.local
tables: []Configuration Examples
Set OAuth
OAuth requires the authlib Python library. Install it via the bootstrapScript using pip .
Authlib documentation: https://authlib.org/
extraEnv:
AUTH_DOMAIN: example.com
extraSecretEnv:
GOOGLE_KEY: xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com
GOOGLE_SECRET: xxxxxxxxxxxxxxxxxxxxxxxx
configOverrides:
enable_oauth: |
ENABLE_PROXY_FIX = True
from flask_appbuilder.security.manager import AUTH_OAUTH
AUTH_TYPE = AUTH_OAUTH
OAUTH_PROVIDERS = [
{
"name": "google",
"icon": "fa-google",
"token_key": "access_token",
"remote_app": {
"client_id": os.getenv("GOOGLE_KEY"),
"client_secret": os.getenv("GOOGLE_SECRET"),
"api_base_url": "https://www.googleapis.com/oauth2/v2/",
"client_kwargs": {"scope": "email profile"},
"request_token_url": None,
"access_token_url": "https://accounts.google.com/o/oauth2/token",
"authorize_url": "https://accounts.google.com/o/oauth2/auth",
"authorize_params": {"hd": os.getenv("AUTH_DOMAIN", "")}
},
}
]
AUTH_ROLE_ADMIN = 'Admin'
AUTH_ROLE_PUBLIC = 'Public'
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Admin"Enable Alerts and Reports
Refer to the official alerts‑reports documentation: https://superset.apache.org/docs/configuration/alerts-reports
Install supported webdriver in Celery worker
Either use a custom image with the webdriver pre‑installed or install it at startup via command:
supersetWorker:
command:
- /bin/sh
- -c
- |
# Install chrome webdriver
apt-get update
apt-get install -y wget
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt-get install -y --no-install-recommends ./google-chrome-stable_current_amd64.deb
wget https://chromedriver.storage.googleapis.com/88.0.4324.96/chromedriver_linux64.zip
apt-get install -y zip
unzip chromedriver_linux64.zip
chmod +x chromedriver
mv chromedriver /usr/bin
apt-get autoremove -yqq --purge
apt-get clean
rm -f google-chrome-stable_current_amd64.deb chromedriver_linux64.zip
. {{ .Values.configMountPath }}/superset_bootstrap.sh; celery --app=superset.tasks.celery_app:app workerRun Celery Beat
Enable the beat pod to trigger scheduled alert/report jobs:
supersetCeleryBeat:
enabled: trueConfigure Celery jobs and SMTP/Slack
extraEnv:
SMTP_HOST: smtp.gmail.com
SMTP_USER: [email protected]
SMTP_PORT: "587"
SMTP_MAIL_FROM: [email protected]
extraSecretEnv:
SLACK_API_TOKEN: xoxb-xxxx-yyyy
SMTP_PASSWORD: xxxx-yyyy
configOverrides:
feature_flags: |
import ast
FEATURE_FLAGS = {"ALERT_REPORTS": True}
SMTP_HOST = os.getenv("SMTP_HOST","localhost")
SMTP_STARTTLS = ast.literal_eval(os.getenv("SMTP_STARTTLS", "True"))
SMTP_SSL = ast.literal_eval(os.getenv("SMTP_SSL", "False"))
SMTP_USER = os.getenv("SMTP_USER","superset")
SMTP_PORT = os.getenv("SMTP_PORT",25)
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD","superset")
SMTP_MAIL_FROM = os.getenv("SMTP_MAIL_FROM","[email protected]")
SLACK_API_TOKEN = os.getenv("SLACK_API_TOKEN",None)
celery_conf: |
from celery.schedules import crontab
class CeleryConfig:
broker_url = f"redis://{env('REDIS_HOST')}:{env('REDIS_PORT')}/0"
imports = ("superset.sql_lab", "superset.tasks.cache", "superset.tasks.scheduler")
result_backend = f"redis://{env('REDIS_HOST')}:{env('REDIS_PORT')}/0"
task_annotations = {"sql_lab.get_sql_results": {"rate_limit": "100/s"}}
beat_schedule = {
"reports.scheduler": {"task": "reports.scheduler", "schedule": crontab(minute="*", hour="*")},
"reports.prune_log": {"task": "reports.prune_log", "schedule": crontab(minute=0, hour=0)},
"cache-warmup-hourly": {"task": "cache-warmup", "schedule": crontab(minute="*/30", hour="*"), "kwargs": {"strategy_name": "top_n_dashboards", "top_n": 10, "since": "7 days ago"}}
}
CELERY_CONFIG = CeleryConfig
reports: |
EMAIL_PAGE_RENDER_WAIT = 60
WEBDRIVER_BASEURL = "http://{{ template \"superset.fullname\" . }}:{{ .Values.service.port }}/"
WEBDRIVER_BASEURL_USER_FRIENDLY = "https://www.example.com/"
WEBDRIVER_TYPE = "chrome"
WEBDRIVER_OPTION_ARGS = [
"--force-device-scale-factor=2.0",
"--high-dpi-support=2.0",
"--headless",
"--disable-gpu",
"--disable-dev-shm-usage",
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-extensions"
]Load Sample Data and Dashboards
To explore Superset with example data, add the following to my-values.yaml and redeploy:
init:
loadExamples: trueSeries
Related article: https://mp.weixin.qq.com/s?__biz=MzA4Mzc4NTE5MQ==∣=2692296804&idx=1&sn=87b7725314598cbbfff6e99d0fa7784d&scene=21
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
