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

# ClickHouse

> Deploy ClickHouse to Magic Containers

This guide walks you through deploying ClickHouse to Magic Containers, either as a standalone container or as part of a [multi-container](/magic-containers/multi-container) app alongside your application.

<Tip>
  Don't have a bunny.net account yet? [Sign up](https://dash.bunny.net/auth/register) and enable Magic Containers to get started.
</Tip>

## Quickstart

<Steps>
  <Step title="Create a new app">
    Go to the [bunny.net dashboard](https://dash.bunny.net), select **Magic Containers**, and click **Add App**. Select **Single region deployment**.

    <Warning>
      Databases should use single region deployment with a single instance. Each pod gets its own dedicated volume with no data replication — this applies both across regions and within the same region. Scaling to multiple pods or regions would result in separate, isolated databases each with their own data.
    </Warning>
  </Step>

  <Step title="Add the ClickHouse container">
    Click **Add Container** and configure the image:

    * **Registry**: Docker Hub
    * **Image**: `clickhouse/clickhouse-server`
    * **Tag**: `latest`

    Magic Containers will automatically detect the required endpoint and environment variables for the image. Configure the environment variables as needed:

    * `CLICKHOUSE_DB` = `app`
    * `CLICKHOUSE_USER` = `app`
    * `CLICKHOUSE_PASSWORD` = a strong password
    * `CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT` = `1`
  </Step>

  <Step title="Add persistent volumes">
    In the **Volumes** section of the container settings, add two volumes:

    * **Name**: `clickhouse-data`, **Mount path**: `/var/lib/clickhouse`
    * **Name**: `clickhouse-logs`, **Mount path**: `/var/log/clickhouse-server`

    This ensures your database files and logs persist across restarts and redeployments. Without volumes, all data is lost when the container stops.

    See [persistent volumes](/magic-containers/persistent-volumes) for more details on volume behavior and pricing.
  </Step>

  <Step title="Deploy">
    Review your settings and click **Confirm and Create**.
  </Step>
</Steps>

<Info>
  Always set a strong `CLICKHOUSE_PASSWORD`, even if ClickHouse is not exposed
  externally. Other containers in the same pod can access the network, and a
  password protects against accidental or unauthorized access.
</Info>

## Environment variables

The official ClickHouse image supports these environment variables:

| Variable                               | Description                         | Default   |
| -------------------------------------- | ----------------------------------- | --------- |
| `CLICKHOUSE_DB`                        | Default database created on start   | `default` |
| `CLICKHOUSE_USER`                      | Username to create                  | `default` |
| `CLICKHOUSE_PASSWORD`                  | Password for the user               | -         |
| `CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT` | Enable SQL-driven access management | `0`       |

## Connect from your app

In a [multi-container](/magic-containers/multi-container) setup, your app and ClickHouse share the same localhost network. ClickHouse exposes two interfaces:

* **HTTP**: port `8123`
* **Native**: port `9000`

<Tabs>
  <Tab title="HTTP">
    ```
    http://app:YOUR_PASSWORD@127.0.0.1:8123/?database=app
    ```
  </Tab>

  <Tab title="Node.js">
    ```javascript theme={null}
    import { createClient } from "@clickhouse/client";

    const client = createClient({
      url: "http://127.0.0.1:8123",
      username: "app",
      password: process.env.CLICKHOUSE_PASSWORD,
      database: "app",
    });

    const result = await client.query({ query: "SELECT now()" });
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    import "github.com/ClickHouse/clickhouse-go/v2"

    conn, err := clickhouse.Open(&clickhouse.Options{
    	Addr: []string{"127.0.0.1:9000"},
    	Auth: clickhouse.Auth{
    		Database: "app",
    		Username: "app",
    		Password: os.Getenv("CLICKHOUSE_PASSWORD"),
    	},
    })
    if err != nil {
    	log.Fatal(err)
    }
    defer conn.Close()
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import clickhouse_connect

    client = clickhouse_connect.get_client(
      host="127.0.0.1",
      port=8123,
      username="app",
      password=os.environ["CLICKHOUSE_PASSWORD"],
      database="app",
    )

    result = client.query("SELECT now()")
    ```
  </Tab>
</Tabs>

## Multi-container example

A typical setup pairs ClickHouse with your application. When configuring the app, add two containers:

### App container

* **Image**: your app image (e.g. `ghcr.io/<your-username>/my-app:latest`)
* **Endpoint**: the port your app listens on
* **Environment variables**:
  * `CLICKHOUSE_PASSWORD` = a strong password

### ClickHouse container

* **Image**: `clickhouse/clickhouse-server:latest`
* **Volumes**: `/var/lib/clickhouse` and `/var/log/clickhouse-server`
* **Environment variables**:
  * `CLICKHOUSE_DB` = `app`
  * `CLICKHOUSE_USER` = `app`
  * `CLICKHOUSE_PASSWORD` = a strong password
  * `CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT` = `1`

<Note>
  Both containers share the same localhost network, so your app connects to
  ClickHouse at `127.0.0.1:8123` (HTTP) or `127.0.0.1:9000` (native). See
  [multi-container apps](/magic-containers/multi-container) for more details.
</Note>

## External access

To connect to ClickHouse from outside Magic Containers (e.g. from your local terminal), add either a [CDN or Anycast endpoint](/magic-containers/endpoints). CDN endpoints work for ClickHouse's HTTP interface only; Anycast endpoints support both HTTP and the native protocol.

### Anycast endpoint

1. Go to your app's **Endpoints** tab and click **Add New Endpoint**
2. Select **Anycast** as the type
3. Set **Container Port** to `8123`
4. Set **Exposed Port** to `8123`
5. Click **Add Endpoint**

Then connect using the Anycast IP and the **exposed port**:

```bash theme={null}
curl "http://<anycast-ip>:<exposed-port>/?query=SELECT+1&user=app&password=YOUR_PASSWORD"
```

Or using the ClickHouse client:

```bash theme={null}
clickhouse-client --host <anycast-ip> --port <exposed-port> --user app --password YOUR_PASSWORD
```

<Note>
  The exposed port and container port may differ. When connecting externally,
  always use the exposed port shown in your endpoint configuration.
</Note>

<Note>
  The `clickhouse-client` uses the native protocol (port `9000`). If you want to
  use the native client externally, add a second Anycast endpoint for port
  `9000`.
</Note>

### CDN endpoint

A CDN endpoint exposes ClickHouse's HTTP interface through bunny.net's edge network at a `b-cdn.net` hostname, giving you HTTPS out of the box.

1. Go to your app's **Endpoints** tab and click **Add New Endpoint**
2. Select **CDN** as the type
3. Set **Container Port** to `8123`
4. Click **Add Endpoint**

Then connect using the assigned hostname. Replace `username` and `password` with your `CLICKHOUSE_USER` and `CLICKHOUSE_PASSWORD`, and `database` with your `CLICKHOUSE_DB`.

<Tabs>
  <Tab title="Query">
    ```bash theme={null}
    curl -u 'username:password' \
      'https://mc-abc123xyz0.b-cdn.net/?query=SELECT+1'
    ```
  </Tab>

  <Tab title="Create">
    ```bash theme={null}
    curl -u 'username:password' \
      'https://mc-abc123xyz0.b-cdn.net/?database=database' \
      --data-binary "
        CREATE TABLE IF NOT EXISTS events (
          ts        DateTime,
          user_id   UInt64,
          event     String,
          value     Float64
        )
        ENGINE = MergeTree()
        ORDER BY (ts, user_id)
      "
    ```
  </Tab>

  <Tab title="Insert">
    ```bash theme={null}
    curl -u 'username:password' \
      'https://mc-abc123xyz0.b-cdn.net/?database=database&query=INSERT+INTO+events+FORMAT+JSONEachRow' \
      --data-binary '
        {"ts":"2026-04-20 10:00:00","user_id":1,"event":"view","value":1.0}
        {"ts":"2026-04-20 10:01:00","user_id":2,"event":"click","value":2.5}
        {"ts":"2026-04-20 10:02:00","user_id":1,"event":"view","value":1.0}
      '
    ```
  </Tab>

  <Tab title="Aggregate">
    ```bash theme={null}
    curl -u 'username:password' \
      'https://mc-abc123xyz0.b-cdn.net/?database=database' \
      --data-binary "
        SELECT event, count() AS n, sum(value) AS total
        FROM events
        GROUP BY event
        ORDER BY n DESC
        FORMAT PrettyCompact
      "
    ```
  </Tab>
</Tabs>

<Warning>
  CDN endpoints are publicly reachable with no authentication at the edge —
  anyone who knows the hostname can reach ClickHouse, and only the database's
  basic auth stands in the way. Always set a strong `CLICKHOUSE_PASSWORD`
  (never leave it as `clickhouse/clickhouse`), especially with
  `CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1` enabled.
</Warning>

<Note>
  CDN endpoints only expose the HTTP interface (port `8123`). The native
  protocol (port `9000`) and clients that depend on it — including
  `clickhouse-client` without `--secure` and most BI tools' native drivers —
  won't work. Use HTTP-based drivers, or add an Anycast endpoint for port
  `9000`.
</Note>

<Warning>
  Exposing your database to the internet means anyone with the credentials can
  connect. Use a strong password and consider removing the endpoint when
  external access is no longer needed.
</Warning>
