---
title: "Deploy Cube"
description: "Define your business metrics once and query them from any tool"
category: "Analytics"
url: https://railway.com/deploy/cube
---

# Deploy Cube

Define your business metrics once and query them from any tool

**[Deploy Cube on Railway](https://railway.com/template/cube)**

- **Creator:** A3A
- **Category:** Analytics

## Template content

### cube-refresh-worker https://cube.dev/favicon.svg

- **Source:** https://github.com/gridalpha/cube-railway
- **Health check:** /readyz

### cube-api https://cube.dev/favicon.svg

- **Source:** https://github.com/gridalpha/cube-railway
- **Health check:** /readyz
- **Public domain:** Yes

### Postgres https://devicons.railway.app/i/postgresql.svg

- **Image:** ghcr.io/railwayapp-templates/postgres-ssl:18

### cubestore https://cube.dev/favicon.svg

- **Source:** https://github.com/gridalpha/cube-railway
- **Health check:** /livez

## Buckets

- **cubestore**

## Documentation

![Cube logo](https://repository-images.githubusercontent.com/149026292/a2901cd6-298a-4285-a023-a23670ec6431)

# Deploy and Host Cube on Railway

Cube is an open-source semantic layer that sits between your database and everything that reads from it. You define metrics, dimensions and joins once in YAML, and Cube serves them to BI tools, dashboards, embedded charts and AI agents through a REST API, a GraphQL API and a Postgres-compatible SQL API. It exists to stop metric drift: when "revenue" is written as SQL in six dashboards, six teams eventually report six numbers. Cube makes that definition one versioned object, applies access control to it, and caches results so dashboards stop re-scanning the warehouse.

To self-host Cube this template runs the documented production shape, not one container. **cube-api** compiles the model and answers REST, GraphQL and SQL queries; it is the only service with a public domain. **cube-refresh-worker** runs the same image with the refresh flag set, so building pre-aggregations never competes with query traffic. **cubestore** is Cube Store, Cube's own columnar engine, holding the query queue, the cache and the pre-aggregations the worker materialises, on a volume that survives redeploys. **Postgres** is the data source, pre-loaded with a sample e-commerce dataset and a matching model, so the deployment answers real queries the moment it is live.

![Diagram of the Cube, Cube Store and Postgres services on Railway](https://res.cloudinary.com/rroe4rtk/image/upload/v1788072012/cube-architecture.png)

## Getting Started with Cube on Railway

Deploy the template and wait for all four services to go green; cube-api seeds the sample dataset on its first boot. Cube Core runs in production mode, so there is no login page and no Playground — this deployment is an API, and every call must carry a JSON Web Token signed with your `CUBEJS_API_SECRET` using HS256. Mint one with any JWT library, then call `/cubejs-api/v1/meta` with an `Authorization` header: a healthy deployment answers with the catalogue of cubes, views, measures and dimensions your model defines. Next, POST a query to `/cubejs-api/v1/load` asking for `sales.revenue` grouped by `sales.products_category` and check the `external` field — `true` means Cube Store answered rather than Postgres, confirming the refresh worker built its pre-aggregations. For a BI tool, add a TCP proxy on port 15432 and point Tableau, Metabase, Superset or `psql` at the host Railway hands back, signing in with `CUBEJS_SQL_USER` and `CUBEJS_SQL_PASSWORD`. To use your own warehouse, repoint the `CUBEJS_DB_*` variables, set `CUBE_SAMPLE_DATA` to `false` and replace the model files in a fork of the source repository. `/readyz` and `/livez` stay open for uptime monitoring.

![Cube meta endpoint listing the deployed cubes and measures](https://res.cloudinary.com/rroe4rtk/image/upload/v1788072015/cube-model-catalogue.png)

![Cube query returning revenue and units by product category](https://res.cloudinary.com/rroe4rtk/image/upload/v1788072017/cube-revenue-by-category.png)

![Cube returning twelve months of completed-order revenue](https://res.cloudinary.com/rroe4rtk/image/upload/v1788072019/cube-monthly-revenue.png)

## About Hosting Cube

Cube is a server, not a library. It reads a data model from disk, compiles it into SQL for whichever database you point it at, and exposes the result over several protocols at once. Self-hosting suits teams who treat metric definitions as version-controlled business logic, need a cache in front of an expensive warehouse, or cannot let data leave their own infrastructure.

- **One model, four protocols** — REST, GraphQL, the Postgres-wire SQL API and Cube's JavaScript client read the same definitions.
- **Pre-aggregations** — Cube materialises rollups into Cube Store and rewrites queries to hit them, turning warehouse scans into fast lookups.
- **Views** — a curated member set you expose to analysts and AI tools without exposing every underlying cube.
- **Row-level security** — the token's claims flow into a security context your model filters on, which is what makes multi-tenant embedding safe.
- **Wide database support** — Postgres, MySQL, ClickHouse, BigQuery, Snowflake, Databricks, Redshift and DuckDB among others.

The split matters under load: an API instance that also builds pre-aggregations stalls user queries while it does.

## Why Deploy Cube on Railway

Railway removes the operational work around a four-service analytics stack:

- Private networking wires all four services together with no exposed ports.
- Only the API takes a public domain; cache and database stay off the internet.
- A persistent volume keeps pre-aggregations across deploys.
- Managed Postgres is provisioned, backed up and connected for you.
- Health checks and restart policies come configured on every service.

## Common Use Cases

- **A consistent metrics layer for BI** — define revenue, churn and retention once, then let Metabase, Superset, Tableau and Excel read the same numbers.
- **Embedded analytics in your own product** — query the REST API from React or Vue, with per-tenant filtering enforced by the token, not the frontend.
- **A governed interface for AI agents** — expose curated views so a model asks for `sales.revenue by month` instead of writing arbitrary SQL.
- **An acceleration cache over a costly warehouse** — pre-aggregate the queries dashboards run and cut both latency and per-query billing.

## Dependencies for Cube

- **cubejs/cube** (`latest`) — the Cube API server and the refresh worker, from [github.com/cube-js/cube](https://github.com/cube-js/cube).
- **cubejs/cubestore** (`latest`) — the columnar engine behind the cache, query queue and pre-aggregation storage.
- **PostgreSQL 18** — the data source, holding the sample schema the model queries.
- **A persistent volume** — mounted at `/cube/data` on Cube Store for its local replica and pre-aggregation storage.

### Environment Variables Reference

| Variable | Service | Purpose |
|---|---|---|
| `CUBEJS_API_SECRET` | api, worker | HS256 key every API token is signed with |
| `CUBEJS_CUBESTORE_HOST` | api, worker | Private hostname of the Cube Store service |
| `CUBEJS_REFRESH_WORKER` | worker | Makes that container build pre-aggregations only |
| `CUBEJS_DEV_MODE` | api, worker | Must stay `false`; `true` disables token checks |
| `CUBEJS_SQL_USER`, `CUBEJS_SQL_PASSWORD` | api | Credentials BI tools use on the SQL API |
| `CUBESTORE_REMOTE_DIR` | cubestore | Where pre-aggregations persist on the volume |

### Deployment Dependencies

- Source repository: [github.com/cube-js/cube](https://github.com/cube-js/cube) · Docs: [cube.dev/docs](https://cube.dev/docs)
- Images: [cubejs/cube](https://hub.docker.com/r/cubejs/cube), [cubejs/cubestore](https://hub.docker.com/r/cubejs/cubestore)
## Hardware Requirements for Self-Hosting Cube

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 2 vCPU across the services | 4+ vCPU |
| RAM | 2 GB total | 4–8 GB, most of it for Cube Store |
| Storage | 5 GB volume | 10 GB+, sized to your pre-aggregations |
| Runtime | Node.js 20+, Docker | Docker |

Cube Store is the memory-hungry component; the API and refresh worker stay modest.

## Self-Hosting Cube with Docker

A local run mounts a model directory and points Cube at a database, with `docker run`:

```
docker run -p 4000:4000 \
  -v $(pwd)/model:/cube/conf/model \
  -e CUBEJS_DB_TYPE=postgres \
  -e CUBEJS_DB_HOST=host.docker.internal \
  -e CUBEJS_DB_NAME=analytics \
  -e CUBEJS_DB_USER=postgres \
  -e CUBEJS_DB_PASS=secret \
  -e CUBEJS_API_SECRET=change-me \
  cubejs/cube:latest
```

Production adds Cube Store and a refresh worker, the shape this template deploys. A minimal cube in `model/cubes/orders.yml` is YAML:

```
cubes:
  - name: orders
    sql_table: public.orders
    dimensions:
      - name: id
        sql: id
        type: number
        primary_key: true
    measures:
      - name: count
        type: count
```

## How Much Does Cube Cost to Self-Host?

Cube Core is open source under the Apache 2.0 licence and free at any scale, with no seat limits and no query metering. Cube Cloud is the commercial product, from around $40 per developer per month up to custom enterprise pricing for the hosted workbench, managed scaling and SSO. Self-hosting on Railway costs infrastructure only, and nothing per query.

## FAQ

**What is Cube?**
An open-source semantic layer. It turns metric definitions written in YAML into governed APIs over your existing database, so every dashboard, application and AI tool reads the same numbers.

**What does this Railway template deploy?**
Four services: the Cube API, a refresh worker, Cube Store for caching and pre-aggregations, and a PostgreSQL database seeded with a sample dataset and matching model.

**Why does the template include Cube Store and a separate refresh worker?**
Cube Store holds the query queue, the cache and the pre-aggregation tables, so without it production mode has nowhere to store rollups. The refresh worker builds them on a schedule, in its own container so long refreshes never slow live queries.

**How do I authenticate against the self-hosted Cube API?**
Every request needs an `Authorization` header carrying a JWT signed with `CUBEJS_API_SECRET` using HS256. Anonymous requests get 403, and there is no default account to change.

**Can I connect Metabase, Superset or Tableau to Cube?**
Yes. Add a TCP proxy on port 15432 and connect any Postgres client with `CUBEJS_SQL_USER` and `CUBEJS_SQL_PASSWORD`. Views appear as tables; measures are read with `MEASURE()`.

**Why is there no Playground on my deployment?**
The Playground exists only in development mode, which also turns off token verification. This template runs production mode, so the API is never open to the internet.


## Similar templates

- [Betterlytics](https://railway.com/deploy/betterlytics) — Betterlytics is a cookieless analytics platform GDPR-compliant.
- [Finance Tracker](https://railway.com/deploy/finance-tracker-1) — Private multi-user household finance ledger with budgets and CSV import.
- [Matomo Analytics + MariaDB](https://railway.com/deploy/matomo-analytics-mariadb) — Privacy-friendly analytics with MariaDB and persistent volumes.

Open this page in a browser: https://railway.com/deploy/cube
