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

# Deploy

> Deploy Basedash with Docker Compose or Kubernetes, and size your infrastructure

> **Note:** This guide assumes you have access to the Distr customer portal. If you haven't received your invite yet, see the [Getting started](https://basedash.com/docs/self-hosting/overview.md#getting-started) section in Overview.

## Before you deploy

1. **Pick a size.** See [Sizing](#sizing) to choose between a single server and Kubernetes.
2. **Create the four required secrets** in the Distr portal before the first deployment: `LICENSE_KEY`, `POSTGRES_PASSWORD`, `CRYPTO_KEY`, and `PRISMA_FIELD_ENCRYPTION_KEY`. See [Configure](https://basedash.com/docs/self-hosting/configure.md#quick-start-generate-secrets) for how to generate them. For a manual deployment, put them in your `.env` file or Helm values instead.
3. **Set `BASE_URL`** to the full public URL, including the scheme: `http://<host>:3000` or `https://basedash.example.com`.
4. **Open inbound access.** Open port **3000**, or put a reverse proxy on 443 in front of it. See [TLS and reverse proxy](https://basedash.com/docs/self-hosting/configure.md#tls-and-reverse-proxy).
5. **Allow outbound HTTPS to `charts.basedash.com`.** The server validates its license there at startup and won't start without it. See [Network requirements](#network-requirements).

AI and SMTP can wait. Email defaults to `log` (login codes are written to the app logs), and AI shows a banner until you add a provider key. Each Compose version also ships a "First-run checklist" in the Distr portal.

## Deployment methods

Basedash uses [Distr](https://distr.sh/docs/getting-started/what-is-distr/) to distribute self-hosted releases. Releases use calendar versions (for example `2026.09.30`), and each one is published as both a Docker Compose and a Kubernetes (Helm) version.

| Method | Best for |
| - | - |
| [Docker Compose with the Distr agent](#docker-compose-with-the-distr-agent) | A single server, managed from the Distr portal (recommended up to 500 users) |
| [Docker Compose without the agent](#docker-compose-without-the-agent) | A single server, deployed with your own tooling |
| [Kubernetes](#kubernetes) | More than 500 users, multiple replicas, or an existing cluster |

### Docker Compose with the Distr agent

The Distr agent runs on your server, pulls the version you select in the Distr portal, deploys it with Docker Compose, and reports its status back. It only makes outbound connections, so you don't need to open any ports for it.

**Prerequisites:**

* A Linux server with Docker and the Docker Compose plugin
* Outbound HTTPS to Distr Hub and `registry.distr.sh`
* Target credentials from the Distr portal (`targetId` and `targetSecret`)

**Installation:**

1. In the Distr portal, open your deployment target and copy the installation command. It looks like `curl "https://<hub>/api/v1/connect?targetId=<id>&targetSecret=<secret>" | docker compose -f - up -d`.
2. Run the command on your server.
3. In the portal, select a Basedash version, fill in the environment template, and deploy.

The `targetSecret` is shown only once. Store it securely.

**Updates:** select a new version in the Distr portal. Basedash never updates your deployment automatically.

Don't run `docker compose up` yourself on an agent-managed server. If something fails, fix it and retry the deploy from the Distr portal.

**Registry authentication:** the agent logs in to `registry.distr.sh` by itself. If image pulls fail with `no basic auth credentials`:

* Use the same user that installed the agent. `sudo docker login` writes `/root/.docker/config.json`; without sudo it writes `~/.docker/config.json`. The agent mounts one of those paths, so if you mix them, login "succeeds" but pulls still fail.
* On Docker Desktop, remove `credStore` / `credsStore` from `~/.docker/config.json`, run `docker login` again so an `"auth"` entry is stored in the file, then restart the agent.
* Run `docker inspect` on the agent container to confirm `/root/.docker` is mounted from the directory you logged in to.

### Docker Compose without the agent

Use this when you deploy with your own tooling or CI/CD and want to control exactly when updates happen.

**Prerequisites:**

* A Linux server with Docker and the Docker Compose plugin
* A Personal Access Token (PAT) from the Distr portal

**Installation:**

1. Log in to the Distr registry with your PAT:

   ```bash theme={"dark"}
   echo "YOUR-ACCESS-TOKEN" | docker login registry.distr.sh --password-stdin -u -
   ```

2. Download the Compose file and environment template for the version you want from the Distr portal. The Compose file already pins the matching app image.

3. Fill in the environment template. See [Configure](https://basedash.com/docs/self-hosting/configure.md) for the required values.

4. Start Basedash:

   ```bash theme={"dark"}
   docker compose up -d
   ```

   > [!TIP]
   >
   > On the first deploy, run without `-d` to watch the logs. Configuration
   > errors stop the app before it starts listening.

If you write your own Compose file, pin the image to a release, for example `registry.distr.sh/basedash/charts-self-hosted-app:2026.09.30`. Don't use `latest`.

**Updates:** download the new version's Compose file from the Distr portal, replace the old one, then run:

```bash theme={"dark"}
docker compose pull
docker compose up -d
```

Database migrations run automatically when the new container starts.

### Kubernetes

Use Kubernetes when you need more than one app replica. That's usually above 500 users, or whenever you need high availability.

**Prerequisites:**

* Kubernetes 1.28+
* Helm 3.x and `kubectl` configured for your cluster
* An ingress controller (or load balancer) to terminate TLS
* A managed PostgreSQL 16+ instance for production (recommended; see [Sizing](#sizing))
* A Personal Access Token (PAT) from the Distr portal

You can deploy the Helm chart from the Distr portal, or install it yourself with Helm.

#### Option 1: Distr-managed

1. In the Distr portal, create a Kubernetes deployment target and run the install command it shows in your cluster. This installs the Distr agent.
2. Select a version of the **Basedash Kubernetes** application.
3. Fill in the values template. The four required secrets come from your Distr secrets, so you only need to set `app.baseUrl` plus any email, AI, and sizing values (see the example below). To use an external database, also create the `basedash-secrets` secret from step 1 below and set `secrets.existingSecret: basedash-secrets`.

To update, select a new version in the portal.

#### Option 2: Install with Helm

1. **Create an image pull secret and an app secret:**

   ```bash theme={"dark"}
   kubectl create secret docker-registry distr-registry \
     --docker-server=registry.distr.sh \
     --docker-username=- \
     --docker-password=YOUR_DISTR_ACCESS_TOKEN

   kubectl create secret generic basedash-secrets \
     --from-literal=crypto-key=<openssl rand -hex 16> \
     --from-literal=prisma-field-encryption-key=<npx @47ng/cloak generate> \
     --from-literal=license-key=<from Basedash> \
     --from-literal=postgres-password=<openssl rand -hex 24>
   ```

   The chart reads `postgres-password` from this secret even when you use an external database, so always include it.

2. **Log in to the chart registry** and check the latest chart version. Its `appVersion` is the Basedash release it was built for.

   ```bash theme={"dark"}
   echo "YOUR_DISTR_ACCESS_TOKEN" | helm registry login registry.distr.sh --username - --password-stdin
   helm show chart oci://registry.distr.sh/basedash/basedash-charts
   ```

3. **Create `values.yaml`:**

   ```yaml theme={"dark"}
   imagePullSecrets:
     - name: distr-registry

   app:
     image:
       repository: registry.distr.sh/basedash/charts-self-hosted-app
       tag: "2026.09.30" # The chart's appVersion
     baseUrl: "https://basedash.example.com"

     # Sizing: see the Sizing section below
     replicaCount: 2
     resources:
       requests:
         cpu: "1"
         memory: 3Gi
       limits:
         cpu: "2"
         memory: 4Gi
     nodeOptions: "--max-old-space-size=2456"

   # Recommended for production: bring your own PostgreSQL
   postgres:
     enabled: false
     external:
       url: "postgresql://user:password@your-postgres-host:5432/basedash_charts"

   secrets:
     existingSecret: basedash-secrets

   email:
     provider: log # Switch to smtp or ses before inviting users

   ingress:
     enabled: true
     className: nginx
     annotations:
       cert-manager.io/cluster-issuer: letsencrypt-prod
     hosts:
       - host: basedash.example.com
         paths:
           - path: /
             pathType: Prefix
     tls:
       - secretName: basedash-tls
         hosts:
           - basedash.example.com
   ```

   See [Configure](https://basedash.com/docs/self-hosting/configure.md) for AI, email, S3, and Slack values.

4. **Install the chart:**

   ```bash theme={"dark"}
   helm install basedash-charts oci://registry.distr.sh/basedash/basedash-charts \
     --version <chart-version> -f values.yaml
   ```

5. **Verify the deployment:**

   ```bash theme={"dark"}
   kubectl get pods -l app.kubernetes.io/name=basedash-charts
   kubectl logs -l app.kubernetes.io/component=app
   ```

**Bundled PostgreSQL:** if you leave `postgres.enabled: true`, the chart runs PostgreSQL 16 as a StatefulSet with a 10Gi volume and a 1Gi memory limit. That's fine for a trial, but raise `postgres.persistence.size` and `postgres.resources` before real use, or use a managed database.

**Updates:** set `app.image.tag` to the new release, then upgrade to the matching chart version:

```bash theme={"dark"}
helm upgrade basedash-charts oci://registry.distr.sh/basedash/basedash-charts \
  --version <chart-version> -f values.yaml
```

Migrations run automatically when the new pods start.

### Before you upgrade

* Back up the database (see [Database backups](https://basedash.com/docs/self-hosting/configure.md#database-backups)).
* Read the release notes for each version you're skipping. They're attached to every version in the Distr portal. Some releases include migrations that can't be rolled back, and the notes call these out.

### Restricted networks

Basedash must reach `charts.basedash.com` over HTTPS to validate its license at startup and every 6 hours. Everything else can stay inside your network: AI providers, email, and object storage are optional or can point at internal endpoints. If you can't allow outbound access to `charts.basedash.com`, contact [support@basedash.com](mailto:support@basedash.com) before deploying.

## Sizing

> **Note:** These numbers assume the machines run only Basedash. Add headroom for anything else running alongside it.

Basedash runs as an app container (web server, background jobs, and chart image rendering) plus PostgreSQL. What drives load is how many people use it at the same time, not how many seats you license. In enterprise rollouts, expect 10–20% of users to be active in the busiest hour.

One app container comfortably serves about 500 users, and it never uses more than about 4 GB of memory. Past that point, a bigger server won't help: add app replicas on Kubernetes and move PostgreSQL to a managed instance.

| Users | Deployment | App | PostgreSQL |
| - | - | - | - |
| Up to 100 | Docker Compose on one server | 2 vCPU, 8 GB RAM, 50 GB SSD (whole server) | Bundled |
| 100–500 | Docker Compose on one server | 4 vCPU, 16 GB RAM, 100 GB SSD (whole server) | Bundled, or managed |
| 500–2,500 | Kubernetes | 2–5 replicas | Managed: 4 vCPU, 16 GB RAM, 250 GB |
| 2,500–10,000 | Kubernetes | 5–12 replicas | Managed: 8 vCPU, 32 GB RAM, 500 GB |
| 10,000+ | [Contact us](mailto:support@basedash.com) | | |

**App replicas:** give each replica 2 vCPU and 4 GiB of memory (request 1 vCPU and 3 GiB), as in the Helm example above. Plan for about one replica per 500 users, with at least 2 for high availability. Add replicas if CPU stays above 70% at peak.

**PostgreSQL connections:** each app replica opens up to 60 connections. Set `max_connections` to at least 60 × your maximum replica count, plus 20. The bundled PostgreSQL allows 200, which is enough for 3 replicas.

**Storage:** the database holds AI chat history, cached query results, and metadata about your data sources' schemas. It grows with AI usage and the size of your connected schemas rather than with the number of dashboards. The sizes above leave plenty of headroom; monitor disk usage and expand as needed.

**Avoid burstable instances** (such as AWS `t3` or `t4g`) for production. Scheduled refreshes, automations, and chart rendering keep a steady CPU load that drains burst credits.

**Equivalent instance types:**

| Size | AWS | Google Cloud | Azure |
| - | - | - | - |
| 2 vCPU, 8 GB | `m7i.large` | `e2-standard-2` | `Standard_D2s_v5` |
| 4 vCPU, 16 GB | `m7i.xlarge` | `e2-standard-4` | `Standard_D4s_v5` |
| PostgreSQL 4 vCPU, 16 GB | RDS `db.m7g.xlarge` | Cloud SQL, 4 vCPU, 16 GB | Database for PostgreSQL, `Standard_D4ds_v5` |
| PostgreSQL 8 vCPU, 32 GB | RDS `db.m7g.2xlarge` | Cloud SQL, 8 vCPU, 32 GB | Database for PostgreSQL, `Standard_D8ds_v5` |

## Network requirements

**Inbound:**

* Port **3000** (plain HTTP) by default, or 443 if a reverse proxy or ingress terminates TLS in front of the app

**Outbound HTTPS:**

* `charts.basedash.com`: license validation at startup and every 6 hours (required)
* `registry.distr.sh`: container images and the Helm chart
* Distr Hub: agent deployments only
* Your AI provider, email provider (SMTP or SES), and S3-compatible storage, if configured

**Data sources:** the app needs a network path to every database and warehouse you connect.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.