> ## Documentation Index
> Fetch the complete documentation index at: https://docs.growthbook.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-Hosting GrowthBook

> Run GrowthBook on your own infrastructure with Docker Compose, including MongoDB, the API, and the front-end.

GrowthBook consists of a NextJS front-end, an ExpressJS API, and a Python stats engine. Everything is bundled together in a single [Docker Image](https://hub.docker.com/r/growthbook/growthbook).

In addition to the app itself, you will also need a MongoDB instance (or MongoDB compatible database) to store login credentials, cached experiment results, and metadata.

<Tip>
  **Don't want to install or host the app yourself?**

  <a href="https://app.growthbook.io">GrowthBook Cloud</a> is a fully managed version that's free to get started with.
</Tip>

## Quick Start

You can use **docker compose** to get started quickly:

```yml theme={null}
# 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](http://localhost:3000)

<Warning>
  **Use of mongo image**

  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.)
</Warning>

We also offer an official Helm chart for deploying GrowthBook on Kubernetes. See the [Kubernetes Self-Hosting Guide](/self-host/kubernetes) for more details.

## Production

We have a dedicated guide for [Self-Hosting GrowthBook in Production](/self-host/production) that covers best practices for security, scaling, performance, and more.

## Docker Tags

The `latest` tag is updated with every commit to the main branch. We run this tag in GrowthBook Cloud, so it's generally safe for production, but may occasionally have minor bugs.

We periodically create stable release tags (e.g. `4.1.0`) if you prefer to have more control over updates. Check out the [Releases page on GitHub](https://github.com/growthbook/growthbook/releases) for a list of releases with detailed release notes.

Lastly, 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`).

## Hardened (non-root, shell-less) image

The GrowthBook runtime image is a distroless [Docker Hardened Image](https://www.docker.com/products/hardened-images) (DHI): it runs as a **non-root user (uid 1000)** and contains **no shell or package manager**. This removes a recurring class of OS-package CVEs.

The published `growthbook/growthbook` image is an ordinary public image — `docker pull` needs no special credentials. The notes below cover the operational differences when you run it, plus the one extra step needed if you build the image from source yourself.

### Local file uploads must be writable by uid 1000

If you use `UPLOAD_METHOD=local`, a volume created by an older (root) GrowthBook image contains root-owned files that the non-root process can't write to. Newly created volumes work automatically; an **existing** volume needs a one-time ownership fix:

```bash theme={null}
# Docker named volume (e.g. the docker-compose "uploads" volume):
docker run --rm -v <volume-name>:/data busybox chown -R 1000:1000 /data

# Host bind-mount:
sudo chown -R 1000:1000 /path/to/uploads
```

Alternatively, keep running as root by setting `user: "0:0"` on the service, or switch to `s3`/`google-cloud` storage (no local volume needed). On Kubernetes, the Helm chart sets `fsGroup: 1000`, which handles this for you.

### Debugging without a shell

`docker exec <container> sh` no longer works. To inspect a running container, attach a throwaway debug container that shares its process namespace:

```bash theme={null}
docker run --rm -it --pid=container:<container-name> --cap-add=SYS_PTRACE busybox sh
# the running container's filesystem is under /proc/1/root/, e.g.:
#   ls /proc/1/root/usr/local/src/app
```

`--cap-add=SYS_PTRACE` is required to read the target process's filesystem. Any image with a shell works (`busybox`, `debian`); the matching `dhi.io/node:<version>-debian12-dev` image gives you the same libraries for running the app's own binaries, though pulling that one needs the login described below. On Kubernetes, use `kubectl debug -it <pod> --image=busybox --target=<container>`. One-off admin scripts (e.g. encryption-key migration) are run via `node` directly — see [Environment Variables](/self-host/env).

<h3 id="building-from-source">
  Building from source requires `docker login dhi.io`
</h3>

Most deployments should use the published `growthbook/growthbook` image. It's the same image we run on GrowthBook Cloud, and it needs no DHI account. If you build the `Dockerfile` yourself (a fork, an internal CI pipeline, or an air-gapped rebuild), log in to `dhi.io` to avoid this error:

```text theme={null}
failed to authorize: failed to fetch anonymous token: unexpected status from GET
https://dhi.io/token?scope=repository:node:pull...: 401 Unauthorized
```

Any free Docker account fixes this. The [DHI Community catalog](https://docs.docker.com/dhi/) is free under Apache 2.0, with no paid subscription or separate entitlement to provision:

```bash theme={null}
docker login dhi.io   # Docker ID + password or personal access token
docker build -t growthbook .
```

In CI, add a login step before the build. With GitHub Actions:

```yaml theme={null}
- uses: docker/login-action@v4
  with:
    registry: dhi.io
    username: ${{ secrets.DOCKER_HUB_USERNAME }}
    password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
```

<Warning>
  **Registry mirrors and egress allowlists.** `dhi.io` is a distinct registry from `docker.io`. A pull-through mirror (Artifactory, Harbor, Nexus) configured for Docker Hub does **not** cover it, and neither does a firewall rule that only permits `docker.io` / `registry-1.docker.io`. Add `dhi.io` as its own upstream remote or allowlisted host.
</Warning>

If you only build from source to layer something on top (a CA certificate or an internal config file), extend the published image instead. This needs no DHI access at all:

```dockerfile theme={null}
FROM growthbook/growthbook:latest
COPY my-ca.crt /opt/certs/my-ca.crt
ENV NODE_EXTRA_CA_CERTS=/opt/certs/my-ca.crt
```

Point `NODE_EXTRA_CA_CERTS` at the certificate rather than dropping it in `/usr/local/share/ca-certificates/`: the usual `update-ca-certificates` step needs a shell, which this image doesn't have, and Node wouldn't read the system bundle anyway. Node picks the variable up at startup, so it covers the back-end and front-end processes where GrowthBook makes its outbound connections.

If `dhi.io` is a hard blocker in your environment, [open an issue](https://github.com/growthbook/growthbook/issues) or find us in [Slack](https://slack.growthbook.io). We'd like to hear about it.
