Self Hosting GrowthBook
GrowthBook consists of a NextJS front-end, an ExpressJS API, and a Python stats engine. Everything is bundled together in a single Docker Image.
In addition to the app itself, you will also need a MongoDB instance to store login credentials, cached experiment results, and metadata.
Don't want to install or host the app yourself? GrowthBook Cloud is a fully managed version that's free to get started.
Installation
You can use docker compose to get started quickly:
# docker-compose.yml
services:
mongo:
image: "mongo:latest"
environment:
- MONGO_INITDB_ROOT_USERNAME=root
- MONGO_INITDB_ROOT_PASSWORD=password
volumes:
- mongodata:/data/db
growthbook:
image: "growthbook/growthbook:latest"
ports:
- "3000:3000"
- "3100:3100"
depends_on:
- mongo
environment:
- MONGODB_URI=mongodb://root:password@mongo:27017/growthbook?authSource=admin
volumes:
- uploads:/usr/local/src/app/packages/back-end/uploads
volumes:
uploads:
mongodata:
Then, just run docker compose up -d
to start everything and view the app at http://localhost:3000
The use of the mongo image within the docker-compose.yml is meant to quickly get a dev or staging environment up and running. For production you may want to use a more scalable and stable solution (ie. AWS DocumentDB, Google Cloud MongoDB Atlas, Azure Cosmos DB for Mongo, etc.) You may also want to have a Growthbook Proxy running as well for speed, scalability, security, and real-time feature rollouts.
Docker Tags
Builds are published automatically from the GitHub repo main branch. The most recent commit is tagged with latest
.
GitHub Releases are also tagged using SemVer (e.g. 0.2.1
).
If you need to reference the image for a specific git commit for any reason, you can use the git shorthash tag (e.g. git-41278e9
).
Updating to Latest
If you are using docker-compose, and assuming you specify the growthbook container with :latest
, you can update with:
docker compose pull growthbook
docker compose stop growthbook
docker compose up -d growthbook
Kubernetes (Helm) Installation
Our official Helm chart to deploy GrowthBook on Kubernetes is distributed as a OCI artifact via ghcr.io:
- For production and scalable cloud-native deployments, use GCP (GKE) or AWS (EKS).
- For local testing and development, use Minikube.
Minimal Prerequisites
Before you apply the example values files, make sure you complete these minimal prerequisites for your environment:
Kubernetes Cluster Access
Ensure you have access to a running Kubernetes cluster (Minikube, GKE, or EKS) with permissions to deploy workloads and required controllers (such as load balancer controllers) installed. You'll need kubectl
and the GrowthBook Helm chart, which is available on Artifact Hub.
Authentication Secrets
Generate and Store JWT and encryption secrets for backend authentication and encryption:
openssl rand -hex 32 # Run twice, once for each secret
Store them as Kubernetes secrets in the same namespace as the one where the chart will be installed (replace the values with your generated secrets):
kubectl create secret generic jwt-secret \
--from-literal=jwt-secret=YOUR_JWT_SECRET
kubectl create secret generic encryption-key \
--from-literal=encryption-key=YOUR_ENCRYPTION_KEY
Reference these secrets in your environment specific values file.
DNS Resolution for Testing
For quick testing without configuring DNS, you can use public DNS services that automatically resolve to your cluster's ingress IP.
Services like sslip.io
and traefik.me
provide wildcard DNS that resolves based on the IP address in the hostname:
sslip.io
: If your ingress IP is35.X.X.X
, thenmyapp.35.X.X.X.sslip.io
resolves to35.X.X.X
traefik.me
: Similar functionality but with dashes, e.g.,myapp.35-X-X-X.traefik.me
These services are useful for:
- Local development with Minikube
- Quick testing in cloud environments before setting up proper DNS
- Temporary deployments or demos
The example values files below use sslip.io
for demonstration purposes. For production, you should use proper DNS records in your cloud provider.
Environment Configuration
In the next step you will need to decide on which environment to deploy to and configure your values files accordingly before installation.
The examples below provide minimal configurations to get you started quickly. For a complete list of all available configuration options and advanced customization, refer to the full values.yaml file in our repository.
Environment-Specific Values
- AWS (EKS)
- GCP
- Minikube
Default hostnames below use sslip.io for quick testing.
For production, use a domain registered in your cloud provider's DNS (e.g., Route53 for AWS).
The hostnames in the values file must match the domain you intend to use for Ingress.
Replace INGRESS_IP with the external IP assigned to your Ingress object.
Minimal values-aws.yaml
global:
env:
- name: APP_ORIGIN
# For testing only! Replace with your real domain for production.
value: http://growthbook.<INGRESS_IP>.sslip.io
frontend:
service:
name: frontend
port: 3000
targetPort: 3000
env:
- name: API_HOST
# For testing only! Replace with your real domain for production.
value: http://api.<INGRESS_IP>.sslip.io
backend:
mongodbEnabled: true
service:
name: backend
port: 3100
targetPort: 3100
env:
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: jwt-secret
key: jwt-secret
- name: ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: encryption-key
key: encryption-key
- name: UPLOAD_METHOD
value: s3
# ===== S3 uploads configuration =====
- name: S3_BUCKET
value: "BUCKET_NAME"
# default: us-east-1
- name: S3_REGION
value: "S3_REGION"
# default: https://${S3_BUCKET}.s3.amazonaws.com/
- name: S3_DOMAIN
value: "https://${S3_BUCKET}.s3.${S3_REGION}.amazonaws.com/"
mongodb:
enabled: true
ingress:
enabled: true
className: "alb"
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/scheme: internet-facing
hosts:
- host: growthbook.<INGRESS_IP>.sslip.io # For testing only! Replace for production.
paths:
- path: /*
pathType: ImplementationSpecific
service: frontend
- host: api.<INGRESS_IP>.sslip.io # For testing only! Replace for production.
paths:
- path: /*
pathType: ImplementationSpecific
service: backend
tls: []
Installation:
helm install growthbook growthbook/growthbook -f values-aws.yaml
After Install: Update Hostnames
- Wait for your Ingress object to be assigned an external IP. This might take anywhere from 3 to 5 minutes.
- Update your
values-aws.yaml
file to use this IP in allsslip.io
hostnames. - Run
helm upgrade --install growthbook oci://ghcr.io/growthbook/charts/growthbook -f values-aws.yaml
to apply the change.
Default hostnames below use sslip.io for quick testing.
For production, use a domain registered in your cloud provider's DNS (e.g., Cloud DNS for GCP).
The hostnames in the values file must match the domain you intend to use for Ingress.
Replace INGRESS_IP with the external IP assigned to your Ingress object.
Minimal values-gcp.yaml
global:
env:
- name: APP_ORIGIN
# For testing only! Replace with your real domain for production.
value: http://growthbook.<INGRESS_IP>.sslip.io
frontend:
service:
annotations:
cloud.google.com/neg: '{"ingress": true}'
name: frontend
port: 3000
targetPort: 3000
env:
- name: API_HOST
# For testing only! Replace with your real domain for production.
value: http://api.<INGRESS_IP>.sslip.io
backend:
mongodbEnabled: true
volumeClaim:
enabled: false
service:
annotations:
cloud.google.com/neg: '{"ingress": true}'
name: backend
port: 3100
targetPort: 3100
env:
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: jwt-secret
key: jwt-secret
- name: ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: encryption-key
key: encryption-key
- name: UPLOAD_METHOD
value: google-cloud
- name: GCS_BUCKET_NAME
value: "BUCKET_NAME"
- name: GCS_DOMAIN
value: "https://storage.googleapis.com/BUCKET_NAME/"
mongodb:
enabled: true
ingress:
enabled: true
className: "gce"
annotations:
kubernetes.io/ingress.class: gce
hosts:
- host: growthbook.<INGRESS_IP>.sslip.io # For testing only! Replace for production.
paths:
- path: /*
pathType: ImplementationSpecific
backend:
service:
name: frontend
port:
number: 3000
- host: api.<INGRESS_IP>.sslip.io # For testing only! Replace for production.
paths:
- path: /*
pathType: ImplementationSpecific
backend:
service:
name: backend
port:
number: 3100
tls: []
Installation:
helm install growthbook growthbook/growthbook -f values-gcp.yaml
After Install: Update Hostnames
- Wait for your Ingress object to be assigned an external IP.
- Update your
values-gcp.yaml
file to use this IP in allsslip.io
hostnames. - Run
helm upgrade --install growthbook oci://ghcr.io/growthbook/charts/growthbook -f values-gcp.yaml
to apply the change.
Minikube is for local testing and development only, not for production.
Default hostnames below use sslip.io for quick testing.
Replace MINIKUBE-IP
with the output of minikube ip
.
Tip: Local TLS with mkcert
For a more realistic development setup, you can use mkcert to issue locally-trusted TLS certificates within Minikube. This allows you to work with HTTPS without browser warnings.
Prerequisites Before starting the setup, make sure you have Minikube installed and running. You'll also need Get Your Minikube IP Start Minikube if it's not already running: Get your Minikube IP address: Make note of this IP address - you'll need to replace all instances of Enable NGINX Ingress Controller The setup requires NGINX Ingress Controller. Enable it in Minikube: Install cert-manager Install cert-manager to manage TLS certificates in your cluster: Install mkcert Install For other operating systems, follow the mkcert installation instructions. Install the local CA in your system trust store: Create CA Secret and ClusterIssuer Create a Kubernetes secret containing your local CA: Create a ClusterIssuer that will use this CA to issue certificates: Important Note About Annotations In the values file, make sure you're using the correct issuer annotation. For a ClusterIssuer (which we created above), use: Do not use Now you can proceed with deploying GrowthBook using the values file below:Click for mkcert setup instructions
kubectl
and helm
available on your system.minikube start
minikube ip
${MINIKUBE_IP}
in the values file with this actual IP.minikube addons enable ingress
# Create the namespace
kubectl create namespace cert-manager
# Install cert-manager using the official manifest
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.15.0/cert-manager.yaml
# Wait for cert-manager pods to be ready
kubectl get pods -n cert-manager --watchmkcert
on your local machine. On macOS, you can use Homebrew:brew install mkcert
mkcert -install
kubectl create secret tls mkcert-ca-key-pair \
--key "$(mkcert -CAROOT)"/rootCA-key.pem \
--cert "$(mkcert -CAROOT)"/rootCA.pem \
-n cert-managerkubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: mkcert
spec:
ca:
secretName: mkcert-ca-key-pair
EOFcert-manager.io/cluster-issuer: mkcert
cert-manager.io/issuer: mkcert
as that's for namespace-scoped issuers.
Minimal values-minikube.yaml (with TLS)
global:
security:
allowInsecureImages: true
env:
# Replace ${MINIKUBE-IP} with the output of `minikube ip`
- name: APP_ORIGIN
value: https://growthbook.${MINIKUBE-IP}.sslip.io
frontend:
service:
name: frontend
port: 3000
targetPort: 3000
env:
# Replace ${MINIKUBE-IP} with the output of `minikube ip`
- name: API_HOST
value: https://api.${MINIKUBE-IP}.sslip.io
backend:
mongodbEnabled: true
volumeClaim:
enabled: true
service:
name: backend
port: 3100
targetPort: 3100
env:
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: jwt-secret
key: jwt-secret
- name: ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: encryption-key
key: encryption-key
- name: UPLOAD_METHOD
value: local
mongodb:
enabled: true
persistence:
enabled: true
# Only needed if running Minikube on Apple Silicon (M1/M2/M3)
# image:
# registry: docker.io
# repository: dlavrenuek/bitnami-mongodb-arm
# tag: 7.0.15
ingress:
className: "nginx"
annotations:
# This tells cert-manager to use our mkcert issuer
cert-manager.io/cluster-issuer: mkcert
enabled: true
hosts:
- host: growthbook.${MINIKUBE-IP}.sslip.io
paths:
- path: /
pathType: ImplementationSpecific
service: frontend
- host: api.${MINIKUBE-IP}.sslip.io
paths:
- path: /
pathType: ImplementationSpecific
service: backend
tls:
- secretName: growthbook-tls
hosts:
- growthbook.${MINIKUBE-IP}.sslip.io
- api.${MINIKUBE-IP}.sslip.io
Installation:
helm upgrade --install growthbook oci://ghcr.io/growthbook/charts/growthbook -f values-minikube.yaml
Accessing GrowthBook
After deploying with Helm, access GrowthBook using the Ingress hostnames you configured in your values file:
-
Minikube:
- Run
minikube tunnel
in a separate terminal to expose the Ingress IP to your host. - Frontend:
https://growthbook.${MINIKUBE-IP}.sslip.io
- Backend/API:
https://api.${MINIKUBE-IP}.sslip.io
- (Replace
${MINIKUBE-IP}
with the output ofminikube ip
)
When running Minikube on MacOS with Docker Desktop, you may need to add some rules to your
/etc/hosts
file:echo "127.0.0.1 growthbook.${MINIKUBE-IP}.sslip.io" | sudo tee -a /etc/hosts
echo "127.0.0.1 api.${MINIKUBE-IP}.sslip.io" | sudo tee -a /etc/hosts - Run
-
AWS & GCP
- Use the hostname you set in your values file
- Check your Ingress/ALB resource for the external DNS name if using AWS Load Balancer Controller or domain if using a cloud-managed Ingress
Open the frontend URL in your browser to start using GrowthBook. The backend/API URL is used by the frontend and for API access.
Troubleshooting
If something isn't working as expected, use these commands to check the status of your resources:
- List all pods and their status:
kubectl get pods -A
- Describe a specific pod (replace
POD_NAME
andNAMESPACE
):kubectl describe pod POD_NAME -n NAMESPACE
- Check the status of your services:
kubectl get svc -A
- Inspect your Ingress resources:
kubectl get ingress -A
kubectl describe ingress INGRESS_NAME -n NAMESPACE