Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/helm-chart-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,29 @@ jobs:

helm lint "$chart" --set formbricks.webappUrl=https://qa.example.com

helm template qa "$chart" \
--set formbricks.webappUrl=https://qa.example.com \
--show-only templates/cube-deployment.yaml > "$render_dir/cube-default.yaml"
grep -A1 'name: CUBEJS_EXTERNAL_DEFAULT' "$render_dir/cube-default.yaml" \
| grep -q 'value: "false"'

helm template qa "$chart" \
--set formbricks.webappUrl=https://qa.example.com \
--set-string cube.env.CUBEJS_EXTERNAL_DEFAULT=true \
--show-only templates/cube-deployment.yaml > "$render_dir/cube-external.yaml"
grep -A1 'name: CUBEJS_EXTERNAL_DEFAULT' "$render_dir/cube-external.yaml" \
| grep -q 'value: "true"'

if helm template qa "$chart" \
--set formbricks.webappUrl=https://qa.example.com \
--set cube.replicas=2 \
--show-only templates/cube-deployment.yaml > "$render_dir/cube-invalid.yaml" 2>&1; then
echo "Expected the in-memory Cube configuration with multiple replicas to fail" >&2
exit 1
fi
grep -q 'CUBEJS_CACHE_AND_QUEUE_DRIVER=memory is only supported when cube.replicas=1' \
"$render_dir/cube-invalid.yaml"

helm template qa "$chart" \
--set formbricks.webappUrl=https://qa.example.com \
--show-only templates/migration-job.yaml > "$render_dir/default.yaml"
Expand Down
6 changes: 5 additions & 1 deletion charts/formbricks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ Cube is part of the baseline Formbricks v5 stack and is deployed by this chart b
- The generated app secret supplies `CUBEJS_API_SECRET` by default. If you disable generated secrets,
provide it through your existing secret management flow.
- Provide `CUBEJS_DB_*` connection variables to the Cube deployment through `cube.envFrom` or `cube.env`.
- Keep `cube.replicas=1` while `cube.env.CUBEJS_CACHE_AND_QUEUE_DRIVER` is `memory`. Configure Cube Store before running multiple Cube replicas.
- The bundled single-replica Cube has no external pre-aggregations and defaults
`cube.env.CUBEJS_EXTERNAL_DEFAULT` to `false`, so it does not require Cube Store. If you add external
pre-aggregations, configure Cube Store before overriding this value to `true`.
- Keep `cube.replicas=1` while `cube.env.CUBEJS_CACHE_AND_QUEUE_DRIVER` is `memory`. Configure Cube Store
and switch cache and queue storage away from memory before running multiple Cube replicas.
- Keep Hub enabled. Cube should point at the same feedback records database that Hub writes to, unless you intentionally split that storage.

## Hub worker and self-hosted embeddings
Expand Down
3 changes: 3 additions & 0 deletions charts/formbricks/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,9 @@ cube:
# the Cube listener port through envFrom.
CUBEJS_DB_TYPE: "postgres"
CUBEJS_DEFAULT_API_SCOPES: "meta,data"
# The bundled schema has no external pre-aggregations, so Cube Store is not required.
# Override this only after configuring Cube Store for custom external pre-aggregations.
CUBEJS_EXTERNAL_DEFAULT: "false"
# Keep the in-memory cache/queue driver at one Cube replica only. The chart
# fails rendering when this remains "memory" and cube.replicas is greater than 1.
CUBEJS_CACHE_AND_QUEUE_DRIVER: "memory"
Expand Down
18 changes: 17 additions & 1 deletion docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,23 @@ That's it! After running the command and providing the required information, vis
The stack includes the [Formbricks Hub](https://github.com/formbricks/hub) API (`ghcr.io/formbricks/hub`) and the bundled Cube service. Hub and Cube share the same database as Formbricks by default and both start as part of the baseline `docker compose up`.

- **Migrations**: A `formbricks-migrate` service runs Formbricks Prisma migrations before `hub-migrate` writes Hub tables to the shared database. `hub-migrate` then runs Hub's database migrations (goose + river) before the Hub API starts. Both migration services run on every `docker compose up` and are idempotent.
- **Production** (`docker/docker-compose.yml`): Set non-empty `HUB_API_KEY` and `CUBEJS_API_SECRET` in `.env` before starting the stack. `docker compose config >/dev/null` validates compose syntax, but missing secrets are reported by the service that needs them at startup. `HUB_API_URL` defaults to `http://hub:8080` and `CUBEJS_API_URL` defaults to `http://cube:4000` so the Formbricks app reaches Hub and Cube inside the compose network. Cube JWT issuer/audience default to `formbricks-web` and `formbricks-cube`, and the bundled Cube service exposes only `meta,data` API scopes. Override `HUB_DATABASE_URL` and `CUBEJS_DB_*` only if Hub or Cube should use a separate database. The Hub image tracks `:latest` by default so `formbricks.sh update` advances Hub in lockstep with the app. `hub` and `hub-migrate` always resolve to the same image. To pin to an immutable reference, set `HUB_IMAGE_REF` in `docker/.env` to either a tag (e.g. `:0.3.0`) or a digest (e.g. `@sha256:14db7b3d...`).
- **Production** (`docker/docker-compose.yml`): Set non-empty `HUB_API_KEY` and
`CUBEJS_API_SECRET` in `.env` before starting the stack. `docker compose config >/dev/null` validates
Compose syntax, but missing secrets are reported by the service that needs them at startup.
`HUB_API_URL` defaults to `http://hub:8080` and `CUBEJS_API_URL` defaults to `http://cube:4000` so the
Formbricks app reaches Hub and Cube inside the Compose network. Cube JWT issuer/audience default to
`formbricks-web` and `formbricks-cube`, and the bundled Cube service exposes only `meta,data` API
scopes. The bundled single-replica Cube uses in-memory cache and queue storage and defaults
`CUBEJS_EXTERNAL_DEFAULT` to `false`, so it does not require Cube Store. If you add external
pre-aggregations, configure Cube Store before overriding `CUBEJS_EXTERNAL_DEFAULT=true`. Override
`HUB_DATABASE_URL` and `CUBEJS_DB_*` only if Hub or Cube should use a separate database. The Hub image
tracks `:latest` by default so `formbricks.sh update` advances Hub in lockstep with the app. `hub` and
`hub-migrate` always resolve to the same image. To pin to an immutable reference, set `HUB_IMAGE_REF`
in `docker/.env` to either a tag (e.g. `:0.3.0`) or a digest
(e.g. `@sha256:14db7b3d...`).
- **Existing production installs**: Pulling new images does not replace an existing
`docker-compose.yml`. Add `CUBEJS_EXTERNAL_DEFAULT: ${CUBEJS_EXTERNAL_DEFAULT:-false}` to the Cube
service's `environment` block, then run `docker compose up -d --no-deps --force-recreate cube`.
- **Development** (`docker-compose.dev.yml`): Hub uses a dedicated local `hub` database and `HUB_API_KEY` defaults to `dev-api-key`. The dev stack starts `hub` plus `hub-worker`; set `EMBEDDING_PROVIDER`, `EMBEDDING_MODEL`, and any provider credentials in the repo root `.env` to enable Hub embeddings locally. See the [Hub embeddings environment reference](https://hub.formbricks.com/reference/environment-variables/#embeddings) for provider-specific values. Cube starts with the dev stack, `CUBEJS_API_URL` defaults to `http://localhost:4000`, and `pnpm dev:setup` generates `CUBEJS_API_SECRET` in the repo root `.env`. The Hub image is pinned to a semver tag (`hub`, `hub-worker`, and `hub-migrate` share the same value); override `HUB_IMAGE_TAG` in the repo root `.env` to test a specific Hub release.

## Smart Functionality AI with Qwen/vLLM
Expand Down
9 changes: 9 additions & 0 deletions docker/__tests__/formbricks-script.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ afterEach(() => {
}
});

describe("docker/docker-compose.yml Cube configuration", () => {
test("disables external pre-aggregations by default while allowing an operator override", () => {
const composeContents = readFileSync(dockerComposeTemplatePath, "utf8");
const cubeBlock = getServiceBlock(composeContents, "cube");

expect(cubeBlock).toContain(" CUBEJS_EXTERNAL_DEFAULT: ${CUBEJS_EXTERNAL_DEFAULT:-false}");
});
});

describe("docker/formbricks.sh Traefik label injection", () => {
test("adds HTTPS Traefik labels to the formbricks service only", () => {
const composePath = writeDockerComposeTemplate();
Expand Down
1 change: 1 addition & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ services:
CUBEJS_JWT_ISSUER: ${CUBEJS_JWT_ISSUER:-formbricks-web}
CUBEJS_JWT_AUDIENCE: ${CUBEJS_JWT_AUDIENCE:-formbricks-cube}
CUBEJS_DEFAULT_API_SCOPES: meta,data
CUBEJS_EXTERNAL_DEFAULT: ${CUBEJS_EXTERNAL_DEFAULT:-false}
CUBEJS_CACHE_AND_QUEUE_DRIVER: memory
volumes:
- ./cube/cube.js:/cube/conf/cube.js:ro
Expand Down
8 changes: 1 addition & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,5 @@
"engines": {
"node": ">=20.19.0 <21 || >=22.12.0 <23 || >=24.0.0 <25"
},
"packageManager": "pnpm@11.7.0",
"nextBundleAnalysis": {
"budget": 358400,
"budgetPercentIncreaseRed": 20,
"minimumChangeThreshold": 0,
"showDetails": true
}
"packageManager": "pnpm@11.7.0"
}
Loading