Cube is an open-source headless semantic layer with pre-aggregations and caching that accelerates warehouse analytics and unifies metric definitions.
Cube is an open-source headless semantic layer that connects data warehouses to downstream analytics applications. It centralizes metric definitions, access rules, and joins in code rather than locking them inside a visualization tool. Downstream applications query these models through SQL, REST, or GraphQL APIs backed by a built-in pre-aggregation engine that serves cached analytical rollups.
Enterprise analytics often couples data modeling with expensive visualization seats. In platforms like Looker, companies pay steep annual contracts just to give developers and analysts access to centralized metrics. Direct warehouse querying against Snowflake, BigQuery, or Databricks introduces a second cost: every dashboard reload or embedded query re-scans raw tables, driving up compute bills.
Cube decouples business definitions from visualization. Teams model metrics once in Cube and route queries from open-source dashboards, portals, or client applications through its API layer. Its aggregate-awareness engine materializes rollups into Cube Store or warehouse tables, intercepting repeated queries before they reach the main database. This shifts unpredictable on-demand query fees into predictable, fixed infrastructure costs.
Can Cube replace full-stack BI platforms like Looker or Metabase? Cube replaces only the modeling and caching layer; teams still need a frontend like Metabase, Superset, or custom web charts to render visualizations.
How does Cube reduce cloud data warehouse bills? Its engine intercepts analytical queries and serves them from local cache or pre-aggregated rollups, preventing repetitive dashboard refreshes from querying raw warehouse storage.
Is Cube free to self-host for commercial production use? Yes, the core engine is free to self-host and run inside commercial applications under its open-source license.
Cube is well suited for teams that want centralized metric definitions in code and lower warehouse compute costs, assuming they can support their own frontend layer. Its Apache-2.0 license grants full autonomy to embed the engine in commercial software without per-seat fees, while active maintenance and frequent updates ensure reliable database connectors.
We started this exact setup from scratch in a clean Linux machine on Oct 23, 2026 and checked it through Cube's own API: every check passed. Recipes are re-checked regularly and removed if they stop working.
mkdir -p cube && cd cube
curl -fsSL https://open-source.store/tools/cube/compose.yml -o compose.ymlcat > .env <<EOF
CUBEJS_API_SECRET=$(openssl rand -hex 32)
POSTGRES_PASSWORD=$(openssl rand -hex 12)
EOFCUBEJS_API_SECRET — Secret key used for signing Cube JWT tokens and API requests. Generated above; keep it secret.POSTGRES_PASSWORD — Password of the PostgreSQL database. Generated above; keep it secret.docker compose up -dservices:
cube:
image: cubejs/cube:v1.6.75
ports:
- "4000:4000"
environment:
CUBEJS_DEV_MODE: "false"
CUBEJS_CACHE_AND_QUEUE_DRIVER: memory
CUBEJS_DB_TYPE: postgres
CUBEJS_DB_HOST: db
CUBEJS_DB_PORT: "5432"
CUBEJS_DB_NAME: cubedb
CUBEJS_DB_USER: cube
CUBEJS_DB_PASS: ${POSTGRES_PASSWORD}
CUBEJS_API_SECRET: ${CUBEJS_API_SECRET}
volumes:
- ./model:/cube/conf/model
depends_on:
db:
condition: service_healthy
restart: always
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: cubedb
POSTGRES_USER: cube
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- pg_data:/var/lib/postgresql/data
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U cube -d cubedb"]
interval: 5s
timeout: 5s
retries: 5
volumes:
pg_data: {}
Free IDE for Java, Kotlin, Go, TypeScript and Python. Built on IntelliJ Community, fully open source, 300+ plugins.
Show that Cube is listed as an open-source alternative. Paste into your README:
Ubuntu 24.04 · Apple container, Docker 29.1.3, Compose 2.40.3+ds1-0ubuntu1~24.04.1. Images pulled in 115 s: postgres:16-alpine, cubejs/cube:v1.6.75.
Based on the upstream setup. Official Cube Docker image connected to PostgreSQL with CUBEJS_DEV_MODE disabled, in-memory cache/queue driver, and API requests secured with CUBEJS_API_SECRET JWT; no refresh worker (CUBEJS_REFRESH_WORKER) and no Cube Store: in-memory cache/queue only, no pre-aggregations, single instance.
cube-1 | 2026-10-23T17:05:23.543047345Z at endReadableNT (node:internal/streams/readable:1698:12)
cube-1 | 2026-10-23T17:05:23.543047887Z at processTicksAndRejections (node:internal/process/task_queues:89:21)
cube-1 | 2026-10-23T17:05:23.543124553Z {"level":"error","message":"Internal Server Error","error":"SyntaxError: Expected ',' or '}' after property value in JSON at position 65 (line 1 column 66)","duration":1}
cube-1 | 2026-10-23T17:05:23.543323387Z SyntaxError: Expected ',' or '}' after property value in JSON at position 65 (line 1 column 66)
cube-1 | 2026-10-23T17:05:23.543325303Z at JSON.parse (<anonymous>)
cube-1 | 2026-10-23T17:05:23.543326012Z at parse (/cube/node_modules/body-parser/lib/types/json.js:92:19)
cube-1 | 2026-10-23T17:05:23.543326595Z at /cube/node_modules/body-parser/lib/read.js:128:18
cube-1 | 2026-10-23T17:05:23.543327137Z at AsyncResource.runInAsyncScope (node:async_hooks:214:14)
cube-1 | 2026-10-23T17:05:23.543327720Z at invokeCallback (/cube/node_modules/raw-body/index.js:238:16)
cube-1 | 2026-10-23T17:05:23.543328220Z at done (/cube/node_modules/raw-body/index.js:227:7)
cube-1 | 2026-10-23T17:05:23.543328762Z at IncomingMessage.onEnd (/cube/node_modules/raw-body/index.js:287:7)
cube-1 | 2026-10-23T17:05:23.543329303Z at IncomingMessage.emit (node:events:519:28)
cube-1 | 2026-10-23T17:05:23.543329803Z at endReadableNT (node:internal/streams/readable:1698:12)
cube-1 | 2026-10-23T17:05:23.543330345Z at processTicksAndRejections (node:internal/process/task_queues:89:21)
cube-1 | 2026-10-23T17:05:50.495367718Z {"level":"error","message":"Internal Server Error","error":"SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)","duration":1}
cube-1 | 2026-10-23T17:05:50.497765343Z SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)
cube-1 | 2026-10-23T17:05:50.497777968Z at JSON.parse (<anonymous>)
cube-1 | 2026-10-23T17:05:50.497779593Z at parse (/cube/node_modules/body-parser/lib/types/json.js:92:19)
cube-1 | 2026-10-23T17:05:50.497780634Z at /cube/node_modules/body-parser/lib/read.js:128:18
cube-1 | 2026-10-23T17:05:50.497781676Z at AsyncResource.runInAsyncScope (node:async_hooks:214:14)
cube-1 | 2026-10-23T17:05:50.497782468Z at invokeCallback (/cube/node_modules/raw-body/index.js:238:16)
cube-1 | 2026-10-23T17:05:50.497783134Z at done (/cube/node_modules/raw-body/index.js:227:7)
cube-1 | 2026-10-23T17:05:50.497783759Z at IncomingMessage.onEnd (/cube/node_modules/raw-body/index.js:287:7)
cube-1 | 2026-10-23T17:05:50.497784426Z at IncomingMessage.emit (node:events:519:28)
cube-1 | 2026-10-23T17:05:50.497785093Z at endReadableNT (node:internal/streams/readable:1698:12)
cube-1 | 2026-10-23T17:05:50.497785759Z at processTicksAndRejections (node:internal/process/task_queues:89:21)
cube-1 | 2026-10-23T17:05:50.498207301Z {"level":"error","message":"Internal Server Error","error":"SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)","duration":0}
cube-1 | 2026-10-23T17:05:50.498822926Z SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)
cube-1 | 2026-10-23T17:05:50.498827259Z at JSON.parse (<anonymous>)
cube-1 | 2026-10-23T17:05:50.498828259Z at parse (/cube/node_modules/body-parser/lib/types/json.js:92:19)
cube-1 | 2026-10-23T17:05:50.498828968Z at /cube/node_modules/body-parser/lib/read.js:128:18
cube-1 | 2026-10-23T17:05:50.498829634Z at AsyncResource.runInAsyncScope (node:async_hooks:214:14)
cube-1 | 2026-10-23T17:05:50.498830343Z at invokeCallback (/cube/node_modules/raw-body/index.js:238:16)
cube-1 | 2026-10-23T17:05:50.498831134Z at done (/cube/node_modules/raw-body/index.js:227:7)
cube-1 | 2026-10-23T17:05:50.498831759Z at IncomingMessage.onEnd (/cube/node_modules/raw-body/index.js:287:7)
cube-1 | 2026-10-23T17:05:50.498832426Z at IncomingMessage.emit (node:events:519:28)
cube-1 | 2026-10-23T17:05:50.498833051Z at endReadableNT (node:internal/streams/readable:1698:12)
cube-1 | 2026-10-23T17:05:50.498833718Z at processTicksAndRejections (node:internal/process/task_queues:89:21)
cube-1 | 2026-10-23T17:06:00.495762014Z {"level":"error","message":"Internal Server Error","error":"SyntaxError: Unexpected token 't', \"t\" is not valid JSON","duration":0}
cube-1 | 2026-10-23T17:06:00.496587806Z SyntaxError: Unexpected token 't', "t" is not valid JSON
cube-1 | 2026-10-23T17:06:00.496599181Z at JSON.parse (<anonymous>)
cube-1 | 2026-10-23T17:06:00.496600556Z at createStrictSyntaxError (/cube/node_modules/body-parser/lib/types/json.js:169:10)
cube-1 | 2026-10-23T17:06:00.496608889Z at parse (/cube/node_modules/body-parser/lib/types/json.js:86:15)
cube-1 | 2026-10-23T17:06:00.496609973Z at /cube/node_modules/body-parser/lib/read.js:128:18
cube-1 | 2026-10-23T17:06:00.496610556Z at AsyncResource.runInAsyncScope (node:async_hooks:214:14)
cube-1 | 2026-10-23T17:06:00.496611139Z at invokeCallback (/cube/node_modules/raw-body/index.js:238:16)
cube-1 | 2026-10-23T17:06:00.496611681Z at done (/cube/node_modules/raw-body/index.js:227:7)
cube-1 | 2026-10-23T17:06:00.496612223Z at IncomingMessage.onEnd (/cube/node_modules/raw-body/index.js:287:7)
cube-1 | 2026-10-23T17:06:00.496612764Z at IncomingMessage.emit (node:events:519:28)
cube-1 | 2026-10-23T17:06:00.496613306Z at endReadableNT (node:internal/streams/readable:1698:12)
cube-1 | 2026-10-23T17:06:00.499692806Z {"level":"error","message":"Internal Server Error","error":"SyntaxError: Unexpected token 't', \"t\" is not valid JSON","duration":0}
cube-1 | 2026-10-23T17:06:00.499945431Z SyntaxError: Unexpected token 't', "t" is not valid JSON
cube-1 | 2026-10-23T17:06:00.499947889Z at JSON.parse (<anonymous>)
cube-1 | 2026-10-23T17:06:00.499948681Z at createStrictSyntaxError (/cube/node_modules/body-parser/lib/types/json.js:169:10)
cube-1 | 2026-10-23T17:06:00.499949306Z at parse (/cube/node_modules/body-parser/lib/types/json.js:86:15)
cube-1 | 2026-10-23T17:06:00.499949848Z at /cube/node_modules/body-parser/lib/read.js:128:18
cube-1 | 2026-10-23T17:06:00.499950514Z at AsyncResource.runInAsyncScope (node:async_hooks:214:14)
cube-1 | 2026-10-23T17:06:00.499951056Z at invokeCallback (/cube/node_modules/raw-body/index.js:238:16)
cube-1 | 2026-10-23T17:06:00.499951598Z at done (/cube/node_modules/raw-body/index.js:227:7)
cube-1 | 2026-10-23T17:06:00.499952098Z at IncomingMessage.onEnd (/cube/node_modules/raw-body/index.js:287:7)
cube-1 | 2026-10-23T17:06:00.499952681Z at IncomingMessage.emit (node:events:519:28)
cube-1 | 2026-10-23T17:06:00.499953181Z at endReadableNT (node:internal/streams/readable:1698:12)
db-1 | 2026-10-23T17:04:49.130427927Z The files belonging to this database system will be owned by user "postgres".
db-1 | 2026-10-23T17:04:49.130445177Z This user must also own the server process.
db-1 | 2026-10-23T17:04:49.130446260Z
db-1 | 2026-10-23T17:04:49.130446844Z The database cluster will be initialized with locale "en_US.utf8".
db-1 | 2026-10-23T17:04:49.130447510Z The default database encoding has accordingly been set to "UTF8".
db-1 | 2026-10-23T17:04:49.130448094Z The default text search configuration will be set to "english".
db-1 | 2026-10-23T17:04:49.130448802Z
db-1 | 2026-10-23T17:04:49.130449385Z Data page checksums are disabled.
db-1 | 2026-10-23T17:04:49.130450010Z
db-1 | 2026-10-23T17:04:49.130454427Z fixing permissions on existing directory /var/lib/postgresql/data ... ok
db-1 | 2026-10-23T17:04:49.131397552Z creating subdirectories ... ok
db-1 | 2026-10-23T17:04:49.131459469Z selecting dynamic shared memory implementation ... posix
db-1 | 2026-10-23T17:04:49.138695635Z selecting default max_connections ... 100
db-1 | 2026-10-23T17:04:49.147118510Z selecting default shared_buffers ... 128MB
db-1 | 2026-10-23T17:04:49.158087594Z selecting default time zone ... UTC
db-1 | 2026-10-23T17:04:49.158344094Z creating configuration files ... ok
db-1 | 2026-10-23T17:04:49.203894469Z running bootstrap script ... ok
db-1 | 2026-10-23T17:04:49.266774510Z sh: locale: not found
db-1 | 2026-10-23T17:04:49.266789302Z 2026-10-23 17:04:49.266 UTC [34] WARNING: no usable system locales were found
db-1 | 2026-10-23T17:04:49.388767135Z performing post-bootstrap initialization ... ok
db-1 | 2026-10-23T17:04:49.430178885Z syncing data to disk ... ok
db-1 | 2026-10-23T17:04:49.430196719Z
db-1 | 2026-10-23T17:04:49.430198010Z
db-1 | 2026-10-23T17:04:49.430198594Z Success. You can now start the database server using:
db-1 | 2026-10-23T17:04:49.430199219Z
db-1 | 2026-10-23T17:04:49.430199677Z pg_ctl -D /var/lib/postgresql/data -l logfile start
db-1 | 2026-10-23T17:04:49.430200260Z
db-1 | 2026-10-23T17:04:49.430235010Z initdb: warning: enabling "trust" authentication for local connections
db-1 | 2026-10-23T17:04:49.430237010Z initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb.
db-1 | 2026-10-23T17:04:49.447023302Z waiting for server to start....2026-10-23 17:04:49.446 UTC [40] LOG: starting PostgreSQL 16.15 on aarch64-unknown-linux-musl, compiled by gcc (Alpine 15.2.0) 15.2.0, 64-bit
db-1 | 2026-10-23T17:04:49.447734969Z 2026-10-23 17:04:49.447 UTC [40] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db-1 | 2026-10-23T17:04:49.449271927Z 2026-10-23 17:04:49.449 UTC [43] LOG: database system was shut down at 2026-10-23 17:04:49 UTC
db-1 | 2026-10-23T17:04:49.450944802Z 2026-10-23 17:04:49.450 UTC [40] LOG: database system is ready to accept connections
db-1 | 2026-10-23T17:04:49.539099844Z done
db-1 | 2026-10-23T17:04:49.539118927Z server started
db-1 | 2026-10-23T17:04:49.559790427Z CREATE DATABASE
db-1 | 2026-10-23T17:04:49.560943094Z
db-1 | 2026-10-23T17:04:49.560960094Z
db-1 | 2026-10-23T17:04:49.561006010Z /usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/*
db-1 | 2026-10-23T17:04:49.561008969Z
db-1 | 2026-10-23T17:04:49.561303677Z waiting for server to shut down....2026-10-23 17:04:49.561 UTC [40] LOG: received fast shutdown request
db-1 | 2026-10-23T17:04:49.562566177Z 2026-10-23 17:04:49.562 UTC [40] LOG: aborting any active transactions
db-1 | 2026-10-23T17:04:49.563410719Z 2026-10-23 17:04:49.563 UTC [40] LOG: background worker "logical replication launcher" (PID 46) exited with exit code 1
db-1 | 2026-10-23T17:04:49.564832010Z 2026-10-23 17:04:49.564 UTC [41] LOG: shutting down
db-1 | 2026-10-23T17:04:49.565445010Z 2026-10-23 17:04:49.565 UTC [41] LOG: checkpoint starting: shutdown immediate
db-1 | 2026-10-23T17:04:49.585953052Z 2026-10-23 17:04:49.584 UTC [41] LOG: checkpoint complete: wrote 926 buffers (5.7%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.007 s, sync=0.011 s, total=0.020 s; sync files=301, longest=0.004 s, average=0.001 s; distance=4283 kB, estimate=4283 kB; lsn=0/1925D10, redo lsn=0/1925D10
db-1 | 2026-10-23T17:04:49.586809927Z 2026-10-23 17:04:49.586 UTC [40] LOG: database system is shut down
db-1 | 2026-10-23T17:04:49.662369177Z done
db-1 | 2026-10-23T17:04:49.662385135Z server stopped
db-1 | 2026-10-23T17:04:49.662887302Z
db-1 | 2026-10-23T17:04:49.662892094Z PostgreSQL init process complete; ready for start up.
db-1 | 2026-10-23T17:04:49.662893177Z
db-1 | 2026-10-23T17:04:49.672644635Z 2026-10-23 17:04:49.672 UTC [1] LOG: starting PostgreSQL 16.15 on aarch64-unknown-linux-musl, compiled by gcc (Alpine 15.2.0) 15.2.0, 64-bit
db-1 | 2026-10-23T17:04:49.672676510Z 2026-10-23 17:04:49.672 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
db-1 | 2026-10-23T17:04:49.672738344Z 2026-10-23 17:04:49.672 UTC [1] LOG: listening on IPv6 address "::", port 5432
db-1 | 2026-10-23T17:04:49.673902177Z 2026-10-23 17:04:49.673 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db-1 | 2026-10-23T17:04:49.676392552Z 2026-10-23 17:04:49.676 UTC [56] LOG: database system was shut down at 2026-10-23 17:04:49 UTC
db-1 | 2026-10-23T17:04:49.678030927Z 2026-10-23 17:04:49.677 UTC [1] LOG: database system is ready to accept connections