Requirements
Make sure the following tools are installed:- Docker Engine with Docker Compose V2 (
docker compose) curlopenssl- A POSIX-compatible shell such as
sh,bash, orzsh
docker compose without the hyphen is now the primary method of using docker-compose, according to the
Docker documentation.Choose Your Setup Path
- Use this manual Docker Compose guide for local installs, custom servers, or custom reverse-proxy setups.
- Use the one-click setup script for production Ubuntu servers where you want Traefik, HTTPS certificates, and optional RustFS automation.
- Use the migration guide before updating an existing Formbricks 4.x install or an older v5 compose file.
Starting with Formbricks v5, the production Docker Compose stack includes Formbricks Hub and Cube as part of
the baseline. Keep
HUB_API_URL at its internal default unless Hub runs elsewhere.Start
-
Create a New Directory for Formbricks
Open a terminal and run the following commands to create and enter a new directory for Formbricks:
-
Download the Docker Files
Get the Docker Compose file, AuthZed database bootstrap helper, and Cube configuration shipped with the
baseline stack:
-
Create the Environment File
Store your configuration in
.env. Docker Compose reads this file for variable interpolation, and the Formbricks container also loads it at startup. For a local install, usehttp://localhost:3000. For a server install, replace both URL values with your public HTTPS URL before starting the stack.KeepPOSTGRES_PASSWORDunchanged after PostgreSQL initializes thepostgresvolume. The generated hexadecimal value is already URL-safe, soPOSTGRES_PASSWORD_URL_ENCODEDuses the same value. If you replace an existingdocker-compose.yml, copy the exact password from that deployment instead of generating a new one. If it contains URI-reserved characters, setPOSTGRES_PASSWORD_URL_ENCODEDto its percent-encoded form; otherwise you can use the same value or omit the encoded variable. The PostgreSQL image only applies the raw password when it creates a new database volume. -
Validate the Docker Compose Configuration
Validate the Compose file after
.envcontains the required values:If validation fails, check that.envcontains the required values and thatdocker-compose.ymlhas valid syntax. -
Start the Docker Setup
Now, you’re ready to run Formbricks with Docker. Use the command below to start Formbricks together with
PostgreSQL, Redis, Formbricks Hub, Cube, and SpiceDB.
The
-dflag runs the containers in the background, so they keep running even after you close the terminal. -
Verify the Stack
Confirm the baseline services started and the Formbricks health endpoint responds:
formbricks-migrate,hub-migrate,authzed-db-bootstrap, andspicedb-migrateshould complete successfully.postgres,redis,cube,hub,spicedb, andformbricksshould be running or healthy. - Open Formbricks in Your Browser Once the setup is running, open http://localhost:3000 in your browser to access Formbricks. The first time you visit, you’ll see a setup wizard. Follow the steps to create your first user and start using Formbricks.
The bundled Docker stack keeps Formbricks Hub and Cube internal to the compose network. The app reaches them
through
http://hub:8080 and http://cube:4000. When AI taxonomy beta is enabled, Hub reaches taxonomy
internally through http://taxonomy:8000.AuthZed and SpiceDB
The bundled SpiceDB service stores its data in a dedicatedspicedb database and login inside the existing
PostgreSQL container. Database bootstrap, datastore migration, and the release-matched authzed-initialize
service are idempotent. Fresh installs apply the canonical schema and verify an empty or reconciled graph
without making Formbricks startup or /health depend on SpiceDB. SpiceDB is reachable only inside the Compose
network at spicedb:50051; it is not published on the host or routed through Traefik.
The Formbricks container receives the supported AuthZed variables automatically. Keep AUTHZED_TOKEN and
AUTHZED_DATABASE_PASSWORD private and include .env and the PostgreSQL volume in your backup plan. Running
docker compose down preserves the volume; docker compose down -v deletes both Formbricks and SpiceDB data.
The release image includes an opt-in operations CLI. It does not start during normal installation:
track_commit_timestamp at its default off value, so SpiceDB reports
that its Watch API is disabled. This does not affect schema writes, relationship writes, or permission checks.
If a future integration consumes the Watch API, enable that PostgreSQL setting separately and plan for the
required database restart.
Repository development exposes SpiceDB on 127.0.0.1:50051. To start the optional authenticated browser UI:
pnpm authzed:smoke for an isolated
application-health, authentication-failure, bounded-outage, schema, permission, migration-idempotency, and
persistence check. The smoke command uses its own Compose project and random host port, then removes its test
data afterward.
To check the developer stack through the Formbricks AuthZed client without writing data, run:
.env, prints one JSON result, and returns exit code 0 only when healthy. An empty
SpiceDB schema is still a healthy connection. There is no AuthZed browser health page, and the normal
Formbricks /health endpoint deliberately remains independent of SpiceDB. Restart Formbricks after changing
any AUTHZED_* value.
Optional Services
Start and verify the baseline stack before enabling optional services.If
.env already contains a COMPOSE_PROFILES line, update that line instead of adding a second one.Enable Bundled Qwen/vLLM For AI
The Docker stack can optionally run Qwen through vLLM as an OpenAI-compatible/v1 endpoint. Baseline
installs do not start vLLM and can still use Google Vertex, Azure, AWS Bedrock, or another OpenAI-compatible
endpoint.
The bundled Qwen/vLLM service requires a GPU-capable Docker host with the NVIDIA Container Toolkit installed.
Model files are stored in the qwen-model-cache Docker volume. Startup can take several minutes while vLLM
downloads and loads the model.
To use the bundled Qwen runtime, add these values to .env:
http://vllm:8000/v1 and is bound to
127.0.0.1:8000 by default for local checks.
If you run your own Qwen/vLLM service, do not enable the qwen profile. Set AI_PROVIDER, AI_MODEL, and
AI_OPENAI_COMPATIBLE_BASE_URL to your external endpoint instead.
Enable AI Taxonomy Beta
The standalone AI taxonomy service is included as an opt-in Docker Compose profile. Baseline installs do not start taxonomy and do not require taxonomy or LLM secrets. To enable taxonomy with the bundled Qwen runtime, add these values to.env:
:v0.1.0 with the current released ghcr.io/formbricks/taxonomy image tag for your Formbricks
version. Production installs should pin a release tag instead of relying on :latest.
If you run your own OpenAI-compatible endpoint, use COMPOSE_PROFILES=taxonomy instead and point
TAXONOMY_LLM_BASE_URL at that /v1 endpoint. The selected model must reliably return strict JSON because
taxonomy generation validates an exact 5-level tree.
To use Amazon Bedrock instead of an OpenAI-compatible endpoint, replace the taxonomy LLM values with:
.env. Compose reads COMPOSE_PROFILES from .env, so the same command works
for taxonomy and qwen,taxonomy setups:
127.0.0.1:8000 refers to the taxonomy service itself.
The preflight endpoint then checks Hub internal auth and LLM reachability from taxonomy.
The taxonomy service remains internal to the compose network by default. For production workloads,
TAXONOMY_MAX_RECORDS defaults to 50000; override it only as an advanced safety limit after sizing CPU,
memory, and LLM capacity.
The taxonomy service is internal to the Docker network. Formbricks Web still calls Hub with
HUB_API_KEY; Hub calls taxonomy with TAXONOMY_SERVICE_TOKEN; taxonomy calls Hub
internal APIs with HUB_INTERNAL_API_TOKEN.The one-click installer does not prompt for AI taxonomy settings. One-click users can enable the beta later
by editing
./formbricks/.env, adding the taxonomy variables above, ensuring COMPOSE_PROFILES=taxonomy is
set, and restarting with docker compose up -d.If you use the one-click Traefik setup, FeedbackRecords are available on the Formbricks origin at
/api/v3/feedbackRecords and /v1/feedback-records. Custom Docker reverse proxies need equivalent wiring:
run gateway auth against the Formbricks app, rewrite /api/v3/feedbackRecords to Hub’s
/v1/feedback-records, and inject Authorization: Bearer <HUB_API_KEY> only on the Hub-bound hop.Update
See our migration guide for version-specific steps to update Formbricks.Add SpiceDB to an Existing Docker Installation
The normal image update commands do not add new services to an existing customized Compose file. Before the first release that requires the AuthZed runtime:-
Back up
.env,docker-compose.yml, and the PostgreSQL volume. -
Download
authzed-postgres-bootstrap.shfrom the same Formbricks release as the Compose file. -
Generate
AUTHZED_TOKENandAUTHZED_DATABASE_PASSWORDwithopenssl rand -hex 32and add them to.env. -
Merge
authzed-db-bootstrap,spicedb-migrate,spicedb,authzed-initialize, and the profiledauthzed-opsservice, plus the sixAUTHZED_*Formbricks variables, from the released Compose file into your customized file. UseAUTHZED_CONSISTENCY=fully_consistent. Preserve proxy, storage, and SMTP settings. -
Run
docker compose configanddocker compose pull. Apply the release’s Formbricks database migrations while the old application remains available:docker compose run --rm formbricks-migrate. -
Start PostgreSQL and SpiceDB, then run the guarded preparation and read-only gate:
A non-empty mismatched schema requires the reviewed
remoteDigestas described in AuthZed Operations. -
Only after
upgrade checkexits0, setFORMBRICKS_AUTHZED_V6_MIGRATION_ACKNOWLEDGED=truein.envand rundocker compose up -d. The one-click updater performs the same preparation before it stops the old app. -
Confirm
authzed-db-bootstrap,spicedb-migrate, andauthzed-initializecompleted andspicedbis healthy withdocker compose ps -a. -
Back up the new authorization database with
docker compose exec -T postgres pg_dump -U postgres -d spicedb > spicedb-backup.sqlafter validation and include it in future database backup procedures.
For a major migration such as Formbricks 4.x to 5.0, update your compose structure and configuration first.
Pulling images alone is not enough if your stack does not yet include Hub (
HUB_API_KEY), Cube (cube/
config files plus CUBEJS_API_SECRET), or the new edge rate-limiting setup.-
Pull the latest Formbricks image
-
Stop the Formbricks stack
-
Re-start the Formbricks stack with the updated image
Optional: Add RustFS for File Storage
RustFS provides S3-compatible object storage for file uploads in Formbricks. It is not required for the baseline Docker setup. Add it only when you want features like image uploads, survey file uploads, or custom logos.For a broader overview of file storage options and required environment variables, see our File Uploads
Configuration guide.
Quick Start: Repository Development Stack
If you cloned the Formbricks repository, the fastest way to test file uploads locally is to use the includeddocker-compose.dev.yml, which already starts RustFS and auto-creates the formbricks bucket.
This development compose file is not downloaded by the manual production quickstart above. If you only
downloaded
docker-compose.yml, use the manual RustFS setup below or the one-click production setup.-
Start the local stack
From the repository root:
This starts PostgreSQL, Valkey (Redis), Mailhog, RustFS, a permissions helper, a one-time bucket bootstrap job, Formbricks Hub, and a local Cube instance for analytics testing.
-
Access the RustFS console
Open http://localhost:9001 in your browser and sign in with:
- Username:
devrustfs - Password:
devrustfs123
- Username:
-
Configure Formbricks
Update your
.envfile or environment variables: - Verify uploads After uploading a file in Formbricks, open http://localhost:9001 and navigate to Buckets → formbricks to confirm the object was stored successfully.
The development compose file also runs a
rustfs-init job so you do not need to create the bucket manually.Manual RustFS Setup (Custom Configuration)
Recommended: Prefer
docker-compose.dev.yml for local development unless you
need to fold RustFS into an existing custom Compose stack.docker-compose.yml, use a pinned RustFS image plus two helper
services:
.env file next to your docker-compose.yml instead of
hardcoding them in Compose:
rustfs-init job defined above, so no manual
RustFS console step is required.
Restrict the
.env file to 0600 and do not commit it to source control. For production, prefer the
one-click setup script, which creates a separate least-privilege service
account automatically.Tips & Common Gotchas
-
Permission denied on
/data: Ensure the mounted directory or volume is owned by UID10001. Therustfs-permshelper handles this for Compose-managed volumes. -
Storage medium matters: Prefer local SSD or NVMe storage for
rustfs-data, use XFS on dedicated host-managed disks where possible, and avoid NFS or other network filesystems for RustFS data. -
Connection refused: Ensure the
rustfscontainer is running and port9000is reachable from the Formbricks container. -
Bucket not found: Confirm that
rustfs-initcompleted successfully or create the bucket manually withmc. -
Auth failed: Confirm that
S3_ACCESS_KEYandS3_SECRET_KEYmatch the RustFS credentials configured on the server. -
Backups: Back up the
rustfs-datavolume regularly, especially for single-server deployments. - Console exposure: Do not expose the RustFS console port publicly in production. Keep it on a private network or behind admin-only controls.
-
Health check: From the Formbricks container:
Production Setup with Traefik
For production deployments, use the one-click setup script, which automatically configures:- RustFS behind Traefik on a dedicated
files.yourdomain.comsubdomain - Automatic SSL certificate generation via Let’s Encrypt
- CORS configuration scoped to your Formbricks domain
- Rate limiting middleware
- Separate RustFS admin and Formbricks service credentials
- A
rustfs-initjob that creates the bucket and access policy
Even in the one-click flow, bundled RustFS remains a convenience-oriented single-server deployment. For
higher availability, stricter operational requirements, or larger storage footprints, prefer external object
storage or a dedicated RustFS deployment managed separately from Formbricks.
Debug
If startup fails, first check the resolved configuration and container state:- Missing or empty secrets: Confirm
.envcontainsNEXTAUTH_SECRET,ENCRYPTION_KEY,CRON_SECRET,HUB_API_KEY, andCUBEJS_API_SECRET. - Migration failures: Check
formbricks-migrate,hub-migrate, andpostgreslogs. Do not remove Docker volumes on an existing install unless you intend to delete its data. - Cube is unhealthy: Confirm
cube/cube.js,cube/schema/FeedbackRecords.js, andCUBEJS_API_SECRETexist, then inspectdocker compose logs cube. - Hub auth errors: Confirm the same
HUB_API_KEYfrom.envis used by Formbricks Web and the Hub service. - Port conflicts: Confirm port
3000and any optional service ports you enabled are not already in use on the host. The current template does not publish the bundled Valkey (redisservice) to the host — it is reachable only on the internal compose network — so a fresh install does not need port6379free on the host. - Optional Qwen/vLLM issues: Check GPU availability, NVIDIA Container Toolkit installation, and
docker compose --profile qwen logs --tail=200 vllm.
To edit any of the available environment variables, check out our
Configuration section.