Skip to content

Apache Superset BI

Template · Updated Jul 2026
Validated Jul 2026

Apache Superset BI

This validated OpenTofu template composes Compute, Network, and Block Storage into a self-hosted business-intelligence host you run on infrastructure you control.

What this template does#

Provisions a single instance running Apache Superset, an open-source BI platform (a self-hosted alternative to Tableau or Sigma):

  • Multi-container Docker stack: web app, Celery worker, bundled metadata PostgreSQL, and Redis on port 8088
  • Default sizing: m2a.large (2 vCPU / 8 GiB RAM) and 40 GiB data volume at /var/lib/docker
  • Private network, security group, floating IP; UI restricted to ui_allowed_cidr by default

Superset metadata and cache run on the instance. Connect your analytical warehouse (for example self-managed PostgreSQL) in the Superset UI after first boot. No credential ships with this template.

Parameters#

ParameterDescriptionDefault
key_nameSSH keypair name (must already exist in your project)required
flavor_nameInstance size (Superset stack on 2 vCPU / 8 GiB RAM)m2a.large
image_nameOperating system imageUbuntu-24.04
app_nameDisplay name prefix for resourcessuperset
volume_sizeBlock volume size in GiB, mounted at /var/lib/docker40
external_networkExternal network for router gateway and floating IPPublicStatic
private_cidrCIDR for the private subnet10.48.0.0/24
ui_allowed_cidrCIDR allowed to reach the Superset UI on port 808810.48.0.0/24

Estimated cost#

Monthly cost estimate

Pricing calculator ↗

Sized as a custom package on dedicated vCPU.

Starting template$67.40/mo

Monthly total for the required template above. Use the configurator below to add optional pieces and see the total update.

What each resource is for

Superset host

m2a.large · 2 dedicated vCPU, 8 GiB RAM, 0.5 Gbps

Runs Apache Superset in Docker (web app, Celery worker, metadata PostgreSQL, Redis).

2 vCPU and 8 GiB RAM default; size up for heavy SQL Lab or concurrent users.

$66.00/mo

Compute shown per role at custom-package rates ($29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM). The headline above is the billed total: the cheaper of a named plan and the custom package, plus add-ons.

Included in baseline

m2a.large

2 dedicated vCPU, 8 GiB RAM, 0.5 Gbps

$66.00

Compute + RAM rate basis

2 vCPU + 8 GiB RAM at $29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM (regular). Totals apply the flat −$5/mo package promotion.

—

Block storage (80 GiB)

80 GiB at $0.08/GiB/mo

$6.40

Public IP (included)

1 included with the custom package

$0.00

Package promotional discount

Flat −$5.00/mo on the custom package (same promotion as named plans).

$-5.00

Included at no charge

These line items are zero on Quake AI. Many other providers meter them separately.

Data transfer (inbound and outbound)

Unlimited data transfer on every plan; Quake AI does not meter per-GB egress.

AWS, GCP, and Azure meter outbound transfer per GB. DigitalOcean and Hetzner include an allowance on compute plans, then charge overage.

Learn more
$0.00

Private networking

Private networks, subnets, Neutron routers, and security groups are included with the plan.

VPC objects are usually free to create elsewhere, but NAT gateways bill hourly plus per-GB processed. Quake AI uses router SNAT with no separate NAT line item.

$0.00

Control-plane API requests

OpenStack API calls for provisioning and management are included.

Some managed services on other clouds meter API calls or charge for premium control-plane features.

$0.00

Dev/test vs production

Start on shared CPU for dev/test, then promote to dedicated for production with a flavor resize. The network, storage, and template stay the same.

Dev/test on shared CPU

Burstable s1a flavors; suited to prototyping and low or bursty load.

$17.90/mo

Production on dedicated CPU

The headline estimate above; predictable steady-load performance.

$67.40/mo

Saves $49.50/mo while you build on shared CPU.

Shared flavors carry less RAM (m2a.large (8 GiB RAM) -> s1a.small (2 GiB RAM)). A resize reboots the instance; data on attached volumes persists. Size the dedicated flavor for the RAM your production workload needs.

Pricing data last validated: . For current rates, check quake.ai/pricing.

Template source#

6 files. Download the zip or expand to copy any file.Download superset.zip
Show source (6 files)
main.tfHCL
data "openstack_images_image_v2" "os" {
  name        = var.image_name
  most_recent = true
}
data "openstack_networking_network_v2" "external" {
  name = var.external_network
}
resource "openstack_networking_network_v2" "private" {
  name           = "${var.app_name}-net"
  admin_state_up = true
}
resource "openstack_networking_subnet_v2" "private" {
  name            = "${var.app_name}-subnet"
  network_id      = openstack_networking_network_v2.private.id
  cidr            = var.private_cidr
  ip_version      = 4
  dns_nameservers = ["1.1.1.1", "8.8.8.8"]
}
resource "openstack_networking_router_v2" "main" {
  name                = "${var.app_name}-router"
  external_network_id = data.openstack_networking_network_v2.external.id
}
resource "openstack_networking_router_interface_v2" "private" {
  router_id = openstack_networking_router_v2.main.id
  subnet_id = openstack_networking_subnet_v2.private.id
}
resource "openstack_networking_secgroup_v2" "superset" {
  name        = "${var.app_name}-sg"
  description = "SSH, reverse-proxy HTTP/S, Superset UI 8088 restricted"
}
resource "openstack_networking_secgroup_rule_v2" "ssh" {
  direction = "ingress"
  ethertype = "IPv4"
  protocol  = "tcp"
  port_range_min = 22
  port_range_max = 22
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.superset.id
}
resource "openstack_networking_secgroup_rule_v2" "http" {
  direction = "ingress"
  ethertype = "IPv4"
  protocol  = "tcp"
  port_range_min = 80
  port_range_max = 80
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.superset.id
}
resource "openstack_networking_secgroup_rule_v2" "https" {
  direction = "ingress"
  ethertype = "IPv4"
  protocol  = "tcp"
  port_range_min = 443
  port_range_max = 443
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.superset.id
}
resource "openstack_networking_secgroup_rule_v2" "ui" {
  direction = "ingress"
  ethertype = "IPv4"
  protocol  = "tcp"
  port_range_min = 8088
  port_range_max = 8088
  remote_ip_prefix  = var.ui_allowed_cidr
  security_group_id = openstack_networking_secgroup_v2.superset.id
}
resource "openstack_networking_port_v2" "superset" {
  name               = "${var.app_name}-port"
  network_id         = openstack_networking_network_v2.private.id
  security_group_ids = [openstack_networking_secgroup_v2.superset.id]
  fixed_ip { subnet_id = openstack_networking_subnet_v2.private.id }
  depends_on = [openstack_networking_router_interface_v2.private]
}
resource "openstack_blockstorage_volume_v3" "data" {
  name = "${var.app_name}-data"
  size = var.volume_size
}
resource "openstack_compute_instance_v2" "superset" {
  name        = var.app_name
  flavor_name = var.flavor_name
  key_pair    = var.key_name
  user_data = templatefile("${path.module}/cloud-init/superset.yaml.tftpl", { app_name = var.app_name })
  block_device {
    uuid                  = data.openstack_images_image_v2.os.id
    source_type           = "image"
    destination_type      = "volume"
    volume_size           = 40
    boot_index            = 0
    delete_on_termination = true
  }
  network { port = openstack_networking_port_v2.superset.id }
}
resource "openstack_compute_volume_attach_v2" "data" {
  instance_id = openstack_compute_instance_v2.superset.id
  volume_id   = openstack_blockstorage_volume_v3.data.id
}
resource "openstack_networking_floatingip_v2" "superset" {
  pool = var.external_network
}
resource "openstack_networking_floatingip_associate_v2" "superset" {
  floating_ip = openstack_networking_floatingip_v2.superset.address
  port_id     = openstack_networking_port_v2.superset.id
}
variables.tfHCL
variable "key_name" {
  description = "SSH keypair name (must already exist in your project)"
  type        = string
}
variable "flavor_name" {
  description = "Instance size. Superset stack runs on 2 vCPU and 8 GiB RAM."
  type        = string
  default     = "m2a.large"
}
variable "image_name" {
  type    = string
  default = "Ubuntu-24.04"
}
variable "app_name" {
  type    = string
  default = "superset"
}
variable "volume_size" {
  type    = number
  default = 40
}
variable "external_network" {
  description = "Shared external network for router gateway and floating IPs; defaults to PublicStatic (persisted FIP / production pattern). Override with PublicEphemeral for ephemeral demos."
  type        = string
  default     = "PublicStatic"
}
variable "private_cidr" {
  type    = string
  default = "10.48.0.0/24"
}
variable "ui_allowed_cidr" {
  description = "CIDR allowed to reach Superset UI on port 8088."
  type        = string
  default     = "10.48.0.0/24"
}
outputs.tfHCL
output "instance_id" { value = openstack_compute_instance_v2.superset.id }
output "floating_ip" { value = openstack_networking_floatingip_v2.superset.address }
output "private_ip" { value = openstack_compute_instance_v2.superset.access_ip_v4 }
output "ui_url" { value = "http://${openstack_networking_floatingip_v2.superset.address}:8088" }
versions.tfHCL
terraform {
  required_version = ">= 1.6.0"
  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
      version = "~> 2.0"
    }
  }
}
provider "openstack" {}
terraform.tfvars.exampleHCL
key_name = "YOUR_KEY_NAME"
# ui_allowed_cidr = "203.0.113.10/32"
cloud-init/superset.yaml.tftpl
#cloud-config
package_update: true
packages: [ca-certificates, curl, openssl]
write_files:
  - path: /opt/superset/docker-compose.yml
    content: |
      services:
        db:
          image: postgres:16
          restart: unless-stopped
          environment: {POSTGRES_DB: superset, POSTGRES_USER: superset}
          env_file: [/opt/superset/.env]
          volumes: [db_data:/var/lib/postgresql/data]
          healthcheck: {test: ["CMD-SHELL", "pg_isready -U superset"], interval: 5s, retries: 10}
        redis:
          image: redis:7
          restart: unless-stopped
          volumes: [redis_data:/data]
          healthcheck: {test: ["CMD", "redis-cli", "ping"], interval: 5s, retries: 10}
        superset:
          image: apache/superset:latest
          restart: unless-stopped
          ports: ["8088:8088"]
          env_file: [/opt/superset/.env]
          depends_on: {db: {condition: service_healthy}, redis: {condition: service_healthy}}
          volumes: [superset_home:/app/superset_home]
        superset-worker:
          image: apache/superset:latest
          restart: unless-stopped
          command: [celery, --app=superset.tasks.celery_app:app, worker, -O, fair, -l, INFO]
          env_file: [/opt/superset/.env]
          depends_on: {db: {condition: service_healthy}, redis: {condition: service_healthy}}
          volumes: [superset_home:/app/superset_home]
      volumes: {db_data: {}, redis_data: {}, superset_home: {}}
  - path: /opt/superset/init.sh
    permissions: "0755"
    content: |
      #!/bin/bash
      set -euo pipefail
      cd /opt/superset
      docker compose up -d db redis
      for i in $(seq 1 60); do docker compose exec -T db pg_isready -U superset && break; sleep 5; done
      docker compose run --rm superset superset db upgrade
      docker compose run --rm superset superset fab create-admin --username "$ADMIN_USERNAME" --firstname Admin --lastname User --email "$ADMIN_EMAIL" --password "$ADMIN_PASSWORD" || true
      docker compose run --rm superset superset init
      docker compose up -d
runcmd:
  - |
    set -e
    DEV=/dev/sdb
    for i in $(seq 1 30); do [ -b "$DEV" ] && break; sleep 5; done
    if ! blkid "$DEV" >/dev/null 2>&1; then mkfs.ext4 -F -L supersetdata "$DEV"; fi
    mkdir -p /var/lib/docker && mount "$DEV" /var/lib/docker
    grep -q "$DEV" /etc/fstab || echo "$DEV /var/lib/docker ext4 defaults,nofail 0 2" >> /etc/fstab
    curl -fsSL https://get.docker.com | sh
    DATABASE_PASSWORD=$(openssl rand -hex 24)
    SUPERSET_SECRET_KEY=$(openssl rand -hex 32)
    ADMIN_USERNAME=admin [email protected]
    ADMIN_PASSWORD=$(openssl rand -hex 16)
    cat > /opt/superset/.env <<EOF
    POSTGRES_PASSWORD=$DATABASE_PASSWORD
    DATABASE_PASSWORD=$DATABASE_PASSWORD
    SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://superset:$DATABASE_PASSWORD@db:5432/superset
    REDIS_HOST=redis
    CELERY_BROKER_URL=redis://redis:6379/0
    CELERY_RESULT_BACKEND=redis://redis:6379/0
    SUPERSET_SECRET_KEY=$SUPERSET_SECRET_KEY
    SUPERSET_LOAD_EXAMPLES=no
    EOF
    chmod 600 /opt/superset/.env
    printf 'username: %s\npassword: %s\n' "$ADMIN_USERNAME" "$ADMIN_PASSWORD" > /opt/superset/.bootstrap-admin
    chmod 600 /opt/superset/.bootstrap-admin
    export ADMIN_USERNAME ADMIN_EMAIL ADMIN_PASSWORD
    /opt/superset/init.sh
Resources, parameters, and variables
Provisions
Parameterized by
Variables
  • key_namerequired
  • flavor_name="m2a.large"
  • image_name="Ubuntu-24.04"
  • app_name="superset"
  • volume_size=40
  • external_network="PublicStatic"
  • private_cidr="10.48.0.0/24"
  • ui_allowed_cidr="10.48.0.0/24"

See also#

Usage Guidelines

The sample code, software libraries, command line tools, proofs of concept, templates, and other related technology on this page (including any of the foregoing that is provided by Quake AI personnel) is provided to you as Quake AI Content under the Quake AI Customer Agreement, or the relevant written agreement between you and Quake AI (whichever applies). Do not use this Quake AI Content in your production accounts, or on production or other critical data. You are responsible for testing, securing, and optimizing the Quake AI Content (such as sample code) as appropriate for production grade use based on your specific quality control practices and standards. Deploying Quake AI Content may incur Quake AI charges for creating or using Quake AI chargeable resources, such as running Compute instances or storing data in Object Storage. Your use is also subject to the Acceptable Use Policy.

For the full policy, see Usage Guidelines.

Last validated: 07.07.2026

Was this page helpful?