Repository: tensorzero/tensorzero (language: rust)

Issue:
Expand options for enabling or disabling observability
→ From https://github.com/tensorzero/tensorzero/discussions/796

Today, the gateway assumes that observability is turned on and will fail if the ClickHouse credentials aren't provided. You can optionally force-disable observability by setting the `gateway.disable_observability` flag to `true`.

---

We can create a new _nullable_ boolean flag `gateway.observability.enabled` (default `null`).

|                        | `gateway.observability.enabled = null` (default) | `gateway.observability.enabled = false` | `gateway.observability.enabled = true` |
| ---------------------- | --------------------------------------- | ------------------------------ | ----------------------------- |
| Valid ClickHouse URL   | On                                      | Off                            | On                            |
| Invalid ClickHouse URL | Exit on Startup (Error)                 | Off                            | Exit on Startup (Error)       |
| Missing ClickHouse URL | Off (+ Warning)                         | Off                            | Exit on Startup (Error)       |

---

Expected usage:

```toml
[gateway]
observability = { enabled = true }
```

---

Over time, we can deprecate the existing `gateway.disable_observability` flag.

It should trigger the following warning if someone uses it:

```
Deprecation Warning: The configuration flag `gateway.disable_observability.enabled` is deprecated in favor of `gateway.observability.enabled`. See https://github.com/tensorzero/tensorzero/issues/797 on GitHub for details.
```
Migrate to `TENSORZERO_CLICKHOUSE_URL`
The gateway and the UI use `CLICKHOUSE_URL`.

We should migrate to the following:

- Update the variable to `TENSORZERO_CLICKHOUSE_URL` everywhere
- If not provided, attempt `CLICKHOUSE_URL`; if available, warn about deprecation and continue
- If neither provided, same behavior as right now.

```
Deprecation Warning: The environment variable "CLICKHOUSE_URL" has been renamed to "TENSORZERO_CLICKHOUSE_URL" and will be removed in a future version. Please update your environment to use "TENSORZERO_CLICKHOUSE_URL" instead.
```


Discussion / hints:
hi, can I take this issue ?
hey @kardSIM I am gonna take this one as there was some offline discussion about how best to handle this going forward. Thanks for offering and we'd definitely love for you to contribute to some other issues here!


Fix patch:
diff --git a/.github/workflows/general.yml b/.github/workflows/general.yml
index e19b06822..c297e03b2 100644
--- a/.github/workflows/general.yml
+++ b/.github/workflows/general.yml
@@ -7,7 +7,7 @@ on:
 
 env:
   FORCE_COLOR: 1
-  CLICKHOUSE_URL: "http://localhost:8123/tensorzero"
+  TENSORZERO_CLICKHOUSE_URL: "http://localhost:8123/tensorzero"
 
 jobs:
   validate:
@@ -108,15 +108,15 @@ jobs:
       - name: pnpm TypeScript type checking
         working-directory: ui
         run: pnpm run typecheck
-
+      
       - name: Start services for ui tests
         working-directory: ui
         run: |
           echo "OPENAI_API_KEY=not_used" >> .env
           echo "FIREWORKS_API_KEY=not_used" >> .env
           echo "FIREWORKS_ACCOUNT_ID=not_used" >> .env
-          echo "CLICKHOUSE_URL=http://localhost:8123/tensorzero" >> .env
-          docker compose -f fixtures/docker-compose.yml up -d
+          echo "TENSORZERO_CLICKHOUSE_URL=http://localhost:8123/tensorzero" >> .env
+          docker compose -f fixtures/docker-compose.yml up -d --build
 
       - name: Run ui tests
         working-directory: ui
diff --git a/.github/workflows/merge-queue.yml b/.github/workflows/merge-queue.yml
index 4ff445407..b57f64dca 100644
--- a/.github/workflows/merge-queue.yml
+++ b/.github/workflows/merge-queue.yml
@@ -74,9 +74,9 @@ jobs:
           GCP_JWT_KEY: ${{ secrets.GCP_JWT_KEY }}
         run: echo "$GCP_JWT_KEY" > $GITHUB_WORKSPACE/gcp_jwt_key.json
 
-      - name: Set up CLICKHOUSE_URL for E2E tests
+      - name: Set up TENSORZERO_CLICKHOUSE_URL for E2E tests
         run: |
-          echo "CLICKHOUSE_URL=http://localhost:8123/tensorzero_e2e_tests" >> $GITHUB_ENV
+          echo "TENSORZERO_CLICKHOUSE_URL=http://localhost:8123/tensorzero_e2e_tests" >> $GITHUB_ENV
 
       - name: Launch ClickHouse container for E2E tests
         run: docker compose -f tensorzero_internal/tests/e2e/docker-compose.yml up -d --wait
@@ -181,9 +181,9 @@ jobs:
       - name: Build Docker container for production deployment tests
         run: docker build -t tensorzero/gateway .
 
-      - name: Set up CLICKHOUSE_URL for production deployment tests
+      - name: Set up TENSORZERO_CLICKHOUSE_URL for production deployment tests
         run: |
-          echo "CLICKHOUSE_URL=http://172.17.0.1:8123/tensorzero" >> $GITHUB_ENV
+          echo "TENSORZERO_CLICKHOUSE_URL=http://172.17.0.1:8123/tensorzero" >> $GITHUB_ENV
 
       - name: Set up .env file for production deployment tests
         run: |
@@ -219,11 +219,11 @@ jobs:
       - name: Start services for ui tests
         working-directory: ui
         run: |
-          echo "OPENAI_API_KEY=${{secrets.OPENAI_API_KEY}}" >> .env
-          echo "FIREWORKS_API_KEY=${{secrets.FIREWORKS_API_KEY}}" >> .env
-          echo "FIREWORKS_ACCOUNT_ID=${{secrets.FIREWORKS_ACCOUNT_ID}}" >> .env
-          echo "CLICKHOUSE_URL=http://localhost:8123/tensorzero" >> .env
-          docker compose -f fixtures/docker-compose.yml up -d
+          echo "OPENAI_API_KEY=not_used" >> .env
+          echo "FIREWORKS_API_KEY=not_used" >> .env
+          echo "FIREWORKS_ACCOUNT_ID=not_used" >> .env
+          echo "TENSORZERO_CLICKHOUSE_URL=http://localhost:8123/tensorzero" >> .env
+          docker compose -f fixtures/docker-compose.yml up -d --build
 
       - name: Run ui tests
         working-directory: ui
@@ -233,9 +233,9 @@ jobs:
         working-directory: ui
         run: docker compose -f fixtures/docker-compose.yml down
 
-      - name: Set up CLICKHOUSE_URL for batch tests
+      - name: Set up TENSORZERO_CLICKHOUSE_URL for batch tests
         run: |
-          echo "CLICKHOUSE_URL=${{ secrets.CLICKHOUSE_CLOUD_URL }}" >> $GITHUB_ENV
+          echo "TENSORZERO_CLICKHOUSE_URL=${{ secrets.CLICKHOUSE_CLOUD_URL }}" >> $GITHUB_ENV
       - name: Launch the gateway for batch tests
         run: |
           cargo run-e2e &
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 01c2ab728..d0def6b01 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -179,7 +179,7 @@ Here are the steps in order to run or test the UI assuming you have the prerequi
 OPENAI_API_KEY=<your-key>
 FIREWORKS_API_KEY=<your-key>
 FIREWORKS_ACCOUNT_ID=<your-account-id>
-CLICKHOUSE_URL=<your-clickhouse-url> # For testing, set to http://localhost:8123/tensorzero
+TENSORZERO_CLICKHOUSE_URL=<your-clickhouse-url> # For testing, set to http://localhost:8123/tensorzero
 TENSORZERO_UI_CONFIG_PATH=<path-to-config-file> # For testing, set to ./fixtures/config/tensorzero.toml
 ```
 
diff --git a/examples/babyai/README.md b/examples/babyai/README.md
index a9070db48..04cdd0c49 100644
--- a/examples/babyai/README.md
+++ b/examples/babyai/README.md
@@ -67,7 +67,7 @@ uv pip sync requirements.txt
 ## Running the Example
 
 You can run the example in the `babyai.ipynb` notebook.
-Make sure to install the dependencies in the `requirements.txt` file and set `CLICKHOUSE_URL=http://localhost:8123/tensorzero` in the shell your notebook will run in.
+Make sure to install the dependencies in the `requirements.txt` file and set `TENSORZERO_CLICKHOUSE_URL=http://localhost:8123/tensorzero` in the shell your notebook will run in.
 It should not require any changes to run and will automatically connect to the TensorZero Gateway you started.
 
 The notebook will evaluate the performance of the default `gpt-4o-mini` variant on the five tasks in the BabyAI environment.
diff --git a/examples/babyai/docker-compose.yml b/examples/babyai/docker-compose.yml
index 0f0828fd8..5a5686949 100644
--- a/examples/babyai/docker-compose.yml
+++ b/examples/babyai/docker-compose.yml
@@ -17,7 +17,7 @@ services:
       # Mount our tensorzero.toml file into the container
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://tensorzero-clickhouse-babyai:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://tensorzero-clickhouse-babyai:8123/tensorzero
       - OPENAI_API_KEY=${OPENAI_API_KEY:?Environment variable OPENAI_API_KEY must be set.}
     ports:
       - "3000:3000"
@@ -31,7 +31,7 @@ services:
     volumes:
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://tensorzero-clickhouse-babyai:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://tensorzero-clickhouse-babyai:8123/tensorzero
     env_file:
       - ${ENV_FILE:-.env}
     ports:
diff --git a/examples/chess-puzzles-best-of-n-sampling/README.md b/examples/chess-puzzles-best-of-n-sampling/README.md
index 6b5cc97cf..486266acc 100644
--- a/examples/chess-puzzles-best-of-n-sampling/README.md
+++ b/examples/chess-puzzles-best-of-n-sampling/README.md
@@ -66,7 +66,7 @@ pip install -r requirements.txt
 ## Running the Example
 
 You can run the example in the `chess_puzzles.ipynb` notebook.
-Make sure to install the dependencies in the `requirements.txt` file and set `CLICKHOUSE_URL=http://localhost:8123/tensorzero` in the shell your notebook will run in.
+Make sure to install the dependencies in the `requirements.txt` file and set `TENSORZERO_CLICKHOUSE_URL=http://localhost:8123/tensorzero` in the shell your notebook will run in.
 It should not require any changes to run and will automatically connect to the TensorZero Gateway you started.
 
 The notebook will evaluate the performance of the default `gpt-4o-mini` variant on the test set of chess puzzles.
diff --git a/examples/chess-puzzles-best-of-n-sampling/docker-compose.yml b/examples/chess-puzzles-best-of-n-sampling/docker-compose.yml
index b410c0697..fbae14dfa 100644
--- a/examples/chess-puzzles-best-of-n-sampling/docker-compose.yml
+++ b/examples/chess-puzzles-best-of-n-sampling/docker-compose.yml
@@ -30,7 +30,7 @@ services:
       - ./config:/app/config:ro
       - ${GCP_VERTEX_CREDENTIALS_PATH:-/dev/null}:/app/gcp-credentials.json:ro
     environment:
-      - CLICKHOUSE_URL=http://tensorzero-clickhouse-chess-puzzles-best-of-n-sampling:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://tensorzero-clickhouse-chess-puzzles-best-of-n-sampling:8123/tensorzero
       - GCP_VERTEX_CREDENTIALS_PATH=${GCP_VERTEX_CREDENTIALS_PATH:+/app/gcp-credentials.json}
     env_file:
       - ${ENV_FILE:-.env}
@@ -45,7 +45,7 @@ services:
     volumes:
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://tensorzero-clickhouse-chess-puzzles-best-of-n-sampling:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://tensorzero-clickhouse-chess-puzzles-best-of-n-sampling:8123/tensorzero
     env_file:
       - ${ENV_FILE:-.env}
     ports:
diff --git a/examples/chess-puzzles-mixture-of-n-sampling/README.md b/examples/chess-puzzles-mixture-of-n-sampling/README.md
index 46387ff42..7130e7eb5 100644
--- a/examples/chess-puzzles-mixture-of-n-sampling/README.md
+++ b/examples/chess-puzzles-mixture-of-n-sampling/README.md
@@ -66,7 +66,7 @@ pip install -r requirements.txt
 ## Running the Example
 
 You can run the example in the `chess_puzzles.ipynb` notebook.
-Make sure to install the dependencies in the `requirements.txt` file and set `CLICKHOUSE_URL=http://localhost:8123/tensorzero` in the shell your notebook will run in.
+Make sure to install the dependencies in the `requirements.txt` file and set `TENSORZERO_CLICKHOUSE_URL=http://localhost:8123/tensorzero` in the shell your notebook will run in.
 It should not require any changes to run and will automatically connect to the TensorZero Gateway you started.
 
 The notebook will evaluate the performance of the default `gpt-4o-mini` variant on the test set of chess puzzles.
diff --git a/examples/chess-puzzles-mixture-of-n-sampling/docker-compose.yml b/examples/chess-puzzles-mixture-of-n-sampling/docker-compose.yml
index 4d4ecdef1..5ff7e28e7 100644
--- a/examples/chess-puzzles-mixture-of-n-sampling/docker-compose.yml
+++ b/examples/chess-puzzles-mixture-of-n-sampling/docker-compose.yml
@@ -30,7 +30,7 @@ services:
       - ./config:/app/config:ro
       - ${GCP_VERTEX_CREDENTIALS_PATH:-/dev/null}:/app/gcp-credentials.json:ro
     environment:
-      - CLICKHOUSE_URL=http://tensorzero-chess-puzzles-mixture-of-n-sampling:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://tensorzero-chess-puzzles-mixture-of-n-sampling:8123/tensorzero
       - GCP_VERTEX_CREDENTIALS_PATH=${GCP_VERTEX_CREDENTIALS_PATH:+/app/gcp-credentials.json}
     env_file:
       - ${ENV_FILE:-.env}
@@ -45,7 +45,7 @@ services:
     volumes:
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://tensorzero-chess-puzzles-mixture-of-n-sampling:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://tensorzero-chess-puzzles-mixture-of-n-sampling:8123/tensorzero
     env_file:
       - ${ENV_FILE:-.env}
     ports:
diff --git a/examples/data-extraction-ner/docker-compose.yml b/examples/data-extraction-ner/docker-compose.yml
index 350be8162..6b5d4753c 100644
--- a/examples/data-extraction-ner/docker-compose.yml
+++ b/examples/data-extraction-ner/docker-compose.yml
@@ -28,7 +28,7 @@ services:
       - ./config:/app/config:ro
       - ${GCP_VERTEX_CREDENTIALS_PATH:-/dev/null}:/app/gcp-credentials.json:ro
     environment:
-      - CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
       - GCP_VERTEX_CREDENTIALS_PATH=${GCP_VERTEX_CREDENTIALS_PATH:+/app/gcp-credentials.json}
     env_file:
       - ${ENV_FILE:-.env}
@@ -43,7 +43,7 @@ services:
     volumes:
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
     env_file:
       - ${ENV_FILE:-.env}
     ports:
diff --git a/examples/gsm8k-custom-recipe-dspy/README.md b/examples/gsm8k-custom-recipe-dspy/README.md
index ef0701bf0..519a5e4df 100644
--- a/examples/gsm8k-custom-recipe-dspy/README.md
+++ b/examples/gsm8k-custom-recipe-dspy/README.md
@@ -26,7 +26,7 @@ See `tensorzero.toml` for the main configuration details.
 
 To get started, create a `.env` file with your Together API key (`TOGETHER_API_KEY`) and run the following command.
 Docker Compose will launch the TensorZero gateway and a test ClickHouse database.
-Set `CLICKHOUSE_URL=http://localhost:8123/tensorzero` and `TOGETHER_API_KEY` in the shell your notebook will run in.
+Set `TENSORZERO_CLICKHOUSE_URL=http://localhost:8123/tensorzero` and `TOGETHER_API_KEY` in the shell your notebook will run in.
 
 ```bash
 docker compose up
diff --git a/examples/gsm8k-custom-recipe-dspy/docker-compose.yml b/examples/gsm8k-custom-recipe-dspy/docker-compose.yml
index 09695ffe2..bec6b929d 100644
--- a/examples/gsm8k-custom-recipe-dspy/docker-compose.yml
+++ b/examples/gsm8k-custom-recipe-dspy/docker-compose.yml
@@ -29,7 +29,7 @@ services:
       - ./config:/app/config:ro
       - ${GCP_VERTEX_CREDENTIALS_PATH:-/dev/null}:/app/gcp-credentials.json:ro
     environment:
-      - CLICKHOUSE_URL=http://tensorzero-clickhouse-gsm8k-custom-recipe-dspy:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://tensorzero-clickhouse-gsm8k-custom-recipe-dspy:8123/tensorzero
       - GCP_VERTEX_CREDENTIALS_PATH=${GCP_VERTEX_CREDENTIALS_PATH:+/app/gcp-credentials.json}
     env_file:
       - ${ENV_FILE:-.env}
@@ -44,7 +44,7 @@ services:
     volumes:
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://tensorzero-clickhouse-gsm8k-custom-recipe-dspy:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://tensorzero-clickhouse-gsm8k-custom-recipe-dspy:8123/tensorzero
     env_file:
       - ${ENV_FILE:-.env}
     ports:
diff --git a/examples/gsm8k-custom-recipe-dspy/gsm8k_dspy.ipynb b/examples/gsm8k-custom-recipe-dspy/gsm8k_dspy.ipynb
index 246597813..f21dd42e7 100644
--- a/examples/gsm8k-custom-recipe-dspy/gsm8k_dspy.ipynb
+++ b/examples/gsm8k-custom-recipe-dspy/gsm8k_dspy.ipynb
@@ -220,7 +220,7 @@
     "\n",
     "To get started:\n",
     "\n",
-    "- Set the `CLICKHOUSE_NATIVE_URL` environment variable. It should correspond to the native protocol port (i.e. not the HTTP port) of your ClickHouse instance. This is typically port 9000.\n",
+    "- Set the `TENSORZERO_CLICKHOUSE_URL` environment variable. \n",
     "- Set the `OPENAI_API_KEY` environment variable.\n",
     "- Update the following parameters to those that apply to your use case.\n"
    ]
@@ -277,8 +277,10 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "assert \"CLICKHOUSE_URL\" in os.environ, \"CLICKHOUSE_URL environment variable not set\"\n",
-    "clickhouse_client = get_client(dsn=os.environ[\"CLICKHOUSE_URL\"])"
+    "assert \"TENSORZERO_CLICKHOUSE_URL\" in os.environ, (\n",
+    "    \"TENSORZERO_CLICKHOUSE_URL environment variable not set\"\n",
+    ")\n",
+    "clickhouse_client = get_client(dsn=os.environ[\"TENSORZERO_CLICKHOUSE_URL\"])"
    ]
   },
   {
diff --git a/examples/guides/batch-inference/docker-compose.yml b/examples/guides/batch-inference/docker-compose.yml
index cb06f4f82..2a7888033 100644
--- a/examples/guides/batch-inference/docker-compose.yml
+++ b/examples/guides/batch-inference/docker-compose.yml
@@ -18,7 +18,7 @@ services:
       # Mount our tensorzero.toml file into the container
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
       - OPENAI_API_KEY=${OPENAI_API_KEY:?Environment variable OPENAI_API_KEY must be set.}
     ports:
       - "3000:3000"
diff --git a/examples/guides/episodes/docker-compose.yml b/examples/guides/episodes/docker-compose.yml
index cb06f4f82..2a7888033 100644
--- a/examples/guides/episodes/docker-compose.yml
+++ b/examples/guides/episodes/docker-compose.yml
@@ -18,7 +18,7 @@ services:
       # Mount our tensorzero.toml file into the container
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
       - OPENAI_API_KEY=${OPENAI_API_KEY:?Environment variable OPENAI_API_KEY must be set.}
     ports:
       - "3000:3000"
diff --git a/examples/guides/metrics-feedback/docker-compose.yml b/examples/guides/metrics-feedback/docker-compose.yml
index cb06f4f82..2a7888033 100644
--- a/examples/guides/metrics-feedback/docker-compose.yml
+++ b/examples/guides/metrics-feedback/docker-compose.yml
@@ -18,7 +18,7 @@ services:
       # Mount our tensorzero.toml file into the container
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
       - OPENAI_API_KEY=${OPENAI_API_KEY:?Environment variable OPENAI_API_KEY must be set.}
     ports:
       - "3000:3000"
diff --git a/examples/guides/prompts-templates-schemas/docker-compose.yml b/examples/guides/prompts-templates-schemas/docker-compose.yml
index cb06f4f82..2a7888033 100644
--- a/examples/guides/prompts-templates-schemas/docker-compose.yml
+++ b/examples/guides/prompts-templates-schemas/docker-compose.yml
@@ -18,7 +18,7 @@ services:
       # Mount our tensorzero.toml file into the container
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
       - OPENAI_API_KEY=${OPENAI_API_KEY:?Environment variable OPENAI_API_KEY must be set.}
     ports:
       - "3000:3000"
diff --git a/examples/haiku-hidden-preferences/docker-compose.yml b/examples/haiku-hidden-preferences/docker-compose.yml
index 21d06ea7a..2bfee971a 100644
--- a/examples/haiku-hidden-preferences/docker-compose.yml
+++ b/examples/haiku-hidden-preferences/docker-compose.yml
@@ -30,7 +30,7 @@ services:
       - ./config:/app/config:ro
       - ${GCP_VERTEX_CREDENTIALS_PATH:-/dev/null}:/app/gcp-credentials.json:ro
     environment:
-      - CLICKHOUSE_URL=http://tensorzero-clickhouse-haiku-hidden-preferences:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://tensorzero-clickhouse-haiku-hidden-preferences:8123/tensorzero
       - GCP_VERTEX_CREDENTIALS_PATH=${GCP_VERTEX_CREDENTIALS_PATH:+/app/gcp-credentials.json}
     env_file:
       - ${ENV_FILE:-.env}
@@ -46,7 +46,7 @@ services:
     volumes:
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://tensorzero-clickhouse-haiku-hidden-preferences:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://tensorzero-clickhouse-haiku-hidden-preferences:8123/tensorzero
     env_file:
       - ${ENV_FILE:-.env}
     ports:
diff --git a/examples/production-deployment/.env.example b/examples/production-deployment/.env.example
index 04a728d6d..381745d49 100644
--- a/examples/production-deployment/.env.example
+++ b/examples/production-deployment/.env.example
@@ -2,16 +2,16 @@
 # │                                 CLICKHOUSE                                 │
 # └────────────────────────────────────────────────────────────────────────────┘
 
-CLICKHOUSE_URL="http[s]://[username]:[password]@[hostname]:[port]/[database]"
+TENSORZERO_CLICKHOUSE_URL="http[s]://[username]:[password]@[hostname]:[port]/[database]"
 
 # Example: ClickHouse running locally
-# CLICKHOUSE_URL="http://localhost:8123/tensorzero"
+# TENSORZERO_CLICKHOUSE_URL="http://localhost:8123/tensorzero"
 
 # Example: ClickHouse Cloud
-# CLICKHOUSE_URL="https://USERNAME:PASSWORD@XXXXX.clickhouse.cloud:8443/tensorzero"
+# TENSORZERO_CLICKHOUSE_URL="https://USERNAME:PASSWORD@XXXXX.clickhouse.cloud:8443/tensorzero"
 
 # Example: TensorZero Gateway running in a container, ClickHouse running on host machine
-# CLICKHOUSE_URL="http://host.docker.internal:8123/tensorzero"
+# TENSORZERO_CLICKHOUSE_URL="http://host.docker.internal:8123/tensorzero"
 
 # ┌────────────────────────────────────────────────────────────────────────────┐
 # │                              MODEL PROVIDERS                               │
diff --git a/examples/production-deployment/docker-compose.yml b/examples/production-deployment/docker-compose.yml
index a104b0603..54cc5fd2e 100644
--- a/examples/production-deployment/docker-compose.yml
+++ b/examples/production-deployment/docker-compose.yml
@@ -7,7 +7,8 @@ services:
       - ${GCP_VERTEX_CREDENTIALS_PATH:-/dev/null}:/app/gcp-credentials.json:ro
     environment:
       - GCP_VERTEX_CREDENTIALS_PATH=${GCP_VERTEX_CREDENTIALS_PATH:+/app/gcp-credentials.json}
-      - CLICKHOUSE_URL=${CLICKHOUSE_URL}
+      - TENSORZERO_CLICKHOUSE_URL=${TENSORZERO_CLICKHOUSE_URL}
+      - CLICKHOUSE_URL=${CLICKHOUSE_URL}  # Deprecated, but still supported
     env_file:
       - ${ENV_FILE:-.env}
     ports:
diff --git a/examples/quickstart/docker-compose.yml b/examples/quickstart/docker-compose.yml
index cb06f4f82..2a7888033 100644
--- a/examples/quickstart/docker-compose.yml
+++ b/examples/quickstart/docker-compose.yml
@@ -18,7 +18,7 @@ services:
       # Mount our tensorzero.toml file into the container
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
       - OPENAI_API_KEY=${OPENAI_API_KEY:?Environment variable OPENAI_API_KEY must be set.}
     ports:
       - "3000:3000"
diff --git a/examples/tutorial/01-simple-chatbot/docker-compose.yml b/examples/tutorial/01-simple-chatbot/docker-compose.yml
index cb06f4f82..2a7888033 100644
--- a/examples/tutorial/01-simple-chatbot/docker-compose.yml
+++ b/examples/tutorial/01-simple-chatbot/docker-compose.yml
@@ -18,7 +18,7 @@ services:
       # Mount our tensorzero.toml file into the container
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
       - OPENAI_API_KEY=${OPENAI_API_KEY:?Environment variable OPENAI_API_KEY must be set.}
     ports:
       - "3000:3000"
diff --git a/examples/tutorial/02-email-copilot/docker-compose.yml b/examples/tutorial/02-email-copilot/docker-compose.yml
index 62cbae808..df379224f 100644
--- a/examples/tutorial/02-email-copilot/docker-compose.yml
+++ b/examples/tutorial/02-email-copilot/docker-compose.yml
@@ -19,7 +19,7 @@ services:
       - ./config:/app/config:ro
     environment:
       - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:?Environment variable ANTHROPIC_API_KEY must be set.}
-      - CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
       - OPENAI_API_KEY=${OPENAI_API_KEY:?Environment variable OPENAI_API_KEY must be set.}
     ports:
       - "3000:3000"
diff --git a/examples/tutorial/03-weather-rag/docker-compose.yml b/examples/tutorial/03-weather-rag/docker-compose.yml
index cb06f4f82..2a7888033 100644
--- a/examples/tutorial/03-weather-rag/docker-compose.yml
+++ b/examples/tutorial/03-weather-rag/docker-compose.yml
@@ -18,7 +18,7 @@ services:
       # Mount our tensorzero.toml file into the container
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
       - OPENAI_API_KEY=${OPENAI_API_KEY:?Environment variable OPENAI_API_KEY must be set.}
     ports:
       - "3000:3000"
diff --git a/examples/tutorial/04-email-data-extraction/docker-compose.yml b/examples/tutorial/04-email-data-extraction/docker-compose.yml
index cb06f4f82..2a7888033 100644
--- a/examples/tutorial/04-email-data-extraction/docker-compose.yml
+++ b/examples/tutorial/04-email-data-extraction/docker-compose.yml
@@ -18,7 +18,7 @@ services:
       # Mount our tensorzero.toml file into the container
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://clickhouse:8123/tensorzero
       - OPENAI_API_KEY=${OPENAI_API_KEY:?Environment variable OPENAI_API_KEY must be set.}
     ports:
       - "3000:3000"
diff --git a/gateway/src/main.rs b/gateway/src/main.rs
index 13b6c5b2d..54cc88dad 100644
--- a/gateway/src/main.rs
+++ b/gateway/src/main.rs
@@ -5,6 +5,7 @@ use std::fmt::Display;
 use std::net::SocketAddr;
 use std::sync::Arc;
 use std::time::Duration;
+use tensorzero_internal::clickhouse::ClickHouseConnectionInfo;
 use tokio::signal;
 
 use tensorzero_internal::clickhouse_migration_manager;
@@ -32,8 +33,8 @@ async fn main() {
         .await
         .expect_pretty("Failed to initialize AppState");
 
-    // Run ClickHouse migrations (if any)
-    if !config.gateway.disable_observability {
+    // Run ClickHouse migrations (if any) if we have a production ClickHouse connection
+    if let ClickHouseConnectionInfo::Production { .. } = &app_state.clickhouse_connection_info {
         clickhouse_migration_manager::run(&app_state.clickhouse_connection_info)
             .await
             .expect_pretty("Failed to run ClickHouse migrations");
diff --git a/recipes/dicl/dicl.ipynb b/recipes/dicl/dicl.ipynb
index 9ac21898d..d54073c40 100644
--- a/recipes/dicl/dicl.ipynb
+++ b/recipes/dicl/dicl.ipynb
@@ -18,7 +18,7 @@
    "source": [
     "To get started:\n",
     "\n",
-    "- Set the `CLICKHOUSE_URL` environment variable. For example: `CLICKHOUSE_URL=\"http://localhost:8123/tensorzero\"`\n",
+    "- Set the `TENSORZERO_CLICKHOUSE_URL` environment variable. For example: `TENSORZERO_CLICKHOUSE_URL=\"http://localhost:8123/tensorzero\"`\n",
     "- Set the `OPENAI_API_KEY` environment variable.\n",
     "- Update the following parameters:\n"
    ]
@@ -158,9 +158,11 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "assert \"CLICKHOUSE_URL\" in os.environ, \"CLICKHOUSE_URL environment variable not set\"\n",
+    "assert \"TENSORZERO_CLICKHOUSE_URL\" in os.environ, (\n",
+    "    \"TENSORZERO_CLICKHOUSE_URL environment variable not set\"\n",
+    ")\n",
     "\n",
-    "clickhouse_client = get_client(dsn=os.environ[\"CLICKHOUSE_URL\"])"
+    "clickhouse_client = get_client(dsn=os.environ[\"TENSORZERO_CLICKHOUSE_URL\"])"
    ]
   },
   {
diff --git a/recipes/dpo/openai/README.md b/recipes/dpo/openai/README.md
index b9cac04ca..74aa33b7c 100644
--- a/recipes/dpo/openai/README.md
+++ b/recipes/dpo/openai/README.md
@@ -2,7 +2,7 @@
 
 The `openai.ipynb` notebook provides a step-by-step recipe to perform **Direct Preference Optimization (DPO) &mdash; also known as Preference Fine-tuning &mdash; of OpenAI models** based on data collected by the TensorZero Gateway.
 
-Set `CLICKHOUSE_URL=http://localhost:8123/tensorzero` and `OPENAI_API_KEY` in the shell your notebook will run in.
+Set `TENSORZERO_CLICKHOUSE_URL=http://localhost:8123/tensorzero` and `OPENAI_API_KEY` in the shell your notebook will run in.
 
 ## Setup
 
diff --git a/recipes/dpo/openai/openai_dpo.ipynb b/recipes/dpo/openai/openai_dpo.ipynb
index fcb4ae578..d4dd22ade 100644
--- a/recipes/dpo/openai/openai_dpo.ipynb
+++ b/recipes/dpo/openai/openai_dpo.ipynb
@@ -17,7 +17,7 @@
    "source": [
     "To get started:\n",
     "\n",
-    " - Set the `CLICKHOUSE_URL` environment variable. For example: `CLICKHOUSE_URL`=`\"http://localhost:8123/tensorzero\"`\n",
+    " - Set the `TENSORZERO_CLICKHOUSE_URL` environment variable. For example: `TENSORZERO_CLICKHOUSE_URL`=`\"http://localhost:8123/tensorzero\"`\n",
     " - Set the `OPENAI_API_KEY` environment variable.\n",
     " - Update the following parameters:"
    ]
@@ -174,9 +174,11 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "assert \"CLICKHOUSE_URL\" in os.environ, \"CLICKHOUSE_URL environment variable not set\"\n",
+    "assert \"TENSORZERO_CLICKHOUSE_URL\" in os.environ, (\n",
+    "    \"TENSORZERO_CLICKHOUSE_URL environment variable not set\"\n",
+    ")\n",
     "\n",
-    "clickhouse_client = get_client(dsn=os.environ[\"CLICKHOUSE_URL\"])"
+    "clickhouse_client = get_client(dsn=os.environ[\"TENSORZERO_CLICKHOUSE_URL\"])"
    ]
   },
   {
diff --git a/recipes/supervised_fine_tuning/demonstrations/fireworks/README.md b/recipes/supervised_fine_tuning/demonstrations/fireworks/README.md
index e78d5f859..d6b6d01e7 100644
--- a/recipes/supervised_fine_tuning/demonstrations/fireworks/README.md
+++ b/recipes/supervised_fine_tuning/demonstrations/fireworks/README.md
@@ -1,7 +1,7 @@
 # TensorZero Recipe: Supervised Fine-Tuning with Fireworks
 
 The `fireworks.ipynb` notebook provides a step-by-step recipe to perform supervised fine-tuning with Fireworks based on demonstrations collected by the TensorZero Gateway.
-Set `CLICKHOUSE_URL=http://localhost:8123/tensorzero` in the shell your notebook will run in and install `firectl` following the instructions in the notebook.
+Set `TENSORZERO_CLICKHOUSE_URL=http://localhost:8123/tensorzero` in the shell your notebook will run in and install `firectl` following the instructions in the notebook.
 
 ## Setup
 
diff --git a/recipes/supervised_fine_tuning/demonstrations/fireworks/fireworks.ipynb b/recipes/supervised_fine_tuning/demonstrations/fireworks/fireworks.ipynb
index 6214d7bee..6f4b9c105 100644
--- a/recipes/supervised_fine_tuning/demonstrations/fireworks/fireworks.ipynb
+++ b/recipes/supervised_fine_tuning/demonstrations/fireworks/fireworks.ipynb
@@ -17,7 +17,7 @@
    "source": [
     "To get started:\n",
     "\n",
-    "- Set the `CLICKHOUSE_URL` environment variable. For example: `CLICKHOUSE_URL=\"http://localhost:8123/tensorzero\"`\n",
+    "- Set the `TENSORZERO_CLICKHOUSE_URL` environment variable. For example: `TENSORZERO_CLICKHOUSE_URL=\"http://localhost:8123/tensorzero\"`\n",
     "- You'll also need to [install](https://docs.fireworks.ai/tools-sdks/firectl/firectl) the CLI tool `firectl` on your machine and sign in with `firectl signin`. You can test that this all worked with `firectl whoami`.\n",
     "- Update the following parameters:\n"
    ]
@@ -30,9 +30,9 @@
    "source": [
     "import os\n",
     "\n",
-    "CLICKHOUSE_URL = os.getenv(\"CLICKHOUSE_URL\")\n",
+    "CLICKHOUSE_URL = os.getenv(\"TENSORZERO_CLICKHOUSE_URL\")\n",
     "\n",
-    "assert CLICKHOUSE_URL is not None, \"CLICKHOUSE_URL is not set\""
+    "assert CLICKHOUSE_URL is not None, \"TENSORZERO_CLICKHOUSE_URL is not set\""
    ]
   },
   {
diff --git a/recipes/supervised_fine_tuning/demonstrations/openai/README.md b/recipes/supervised_fine_tuning/demonstrations/openai/README.md
index dd1f0fd4d..4543d15ba 100644
--- a/recipes/supervised_fine_tuning/demonstrations/openai/README.md
+++ b/recipes/supervised_fine_tuning/demonstrations/openai/README.md
@@ -1,7 +1,7 @@
 # TensorZero Recipe: Supervised Fine-Tuning with OpenAI
 
 The `openai.ipynb` notebook provides a step-by-step recipe to perform supervised fine-tuning of OpenAI models based on data collected by the TensorZero Gateway.
-Set `CLICKHOUSE_URL=http://localhost:8123/tensorzero` and `OPENAI_API_KEY` in the shell your notebook will run in.
+Set `TENSORZERO_CLICKHOUSE_URL=http://localhost:8123/tensorzero` and `OPENAI_API_KEY` in the shell your notebook will run in.
 
 ## Setup
 
diff --git a/recipes/supervised_fine_tuning/demonstrations/openai/openai.ipynb b/recipes/supervised_fine_tuning/demonstrations/openai/openai.ipynb
index d61a00ad8..eb7144f27 100644
--- a/recipes/supervised_fine_tuning/demonstrations/openai/openai.ipynb
+++ b/recipes/supervised_fine_tuning/demonstrations/openai/openai.ipynb
@@ -16,7 +16,7 @@
    "source": [
     "To get started:\n",
     "\n",
-    "- Set the `CLICKHOUSE_URL` environment variable. For example: `CLICKHOUSE_URL=\"http://localhost:8123/tensorzero\"`\n",
+    "- Set the `TENSORZERO_CLICKHOUSE_URL` environment variable. For example: `TENSORZERO_CLICKHOUSE_URL=\"http://localhost:8123/tensorzero\"`\n",
     "- Set the `OPENAI_API_KEY` environment variable.\n",
     "- Update the following parameters:\n"
    ]
@@ -165,9 +165,11 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "assert \"CLICKHOUSE_URL\" in os.environ, \"CLICKHOUSE_URL environment variable not set\"\n",
+    "assert \"TENSORZERO_CLICKHOUSE_URL\" in os.environ, (\n",
+    "    \"TENSORZERO_CLICKHOUSE_URL environment variable not set\"\n",
+    ")\n",
     "\n",
-    "clickhouse_client = get_client(dsn=os.environ[\"CLICKHOUSE_URL\"])"
+    "clickhouse_client = get_client(dsn=os.environ[\"TENSORZERO_CLICKHOUSE_URL\"])"
    ]
   },
   {
diff --git a/recipes/supervised_fine_tuning/metrics/fireworks/README.md b/recipes/supervised_fine_tuning/metrics/fireworks/README.md
index 718a1a72a..d6f316f8a 100644
--- a/recipes/supervised_fine_tuning/metrics/fireworks/README.md
+++ b/recipes/supervised_fine_tuning/metrics/fireworks/README.md
@@ -1,7 +1,7 @@
 # TensorZero Recipe: Supervised Fine-Tuning with Fireworks
 
 The `fireworks.ipynb` notebook provides a step-by-step recipe to perform supervised fine-tuning with Fireworks based on data collected by the TensorZero Gateway.
-Set `CLICKHOUSE_URL=http://localhost:8123/tensorzero` in the shell your notebook will run in and install `firectl` following the instructions in the notebook.
+Set `TENSORZERO_CLICKHOUSE_URL=http://localhost:8123/tensorzero` in the shell your notebook will run in and install `firectl` following the instructions in the notebook.
 
 ## Setup
 
diff --git a/recipes/supervised_fine_tuning/metrics/openai/README.md b/recipes/supervised_fine_tuning/metrics/openai/README.md
index dd1f0fd4d..4543d15ba 100644
--- a/recipes/supervised_fine_tuning/metrics/openai/README.md
+++ b/recipes/supervised_fine_tuning/metrics/openai/README.md
@@ -1,7 +1,7 @@
 # TensorZero Recipe: Supervised Fine-Tuning with OpenAI
 
 The `openai.ipynb` notebook provides a step-by-step recipe to perform supervised fine-tuning of OpenAI models based on data collected by the TensorZero Gateway.
-Set `CLICKHOUSE_URL=http://localhost:8123/tensorzero` and `OPENAI_API_KEY` in the shell your notebook will run in.
+Set `TENSORZERO_CLICKHOUSE_URL=http://localhost:8123/tensorzero` and `OPENAI_API_KEY` in the shell your notebook will run in.
 
 ## Setup
 
diff --git a/recipes/supervised_fine_tuning/metrics/openai/openai.ipynb b/recipes/supervised_fine_tuning/metrics/openai/openai.ipynb
index 091f95d7f..30b4b8a42 100644
--- a/recipes/supervised_fine_tuning/metrics/openai/openai.ipynb
+++ b/recipes/supervised_fine_tuning/metrics/openai/openai.ipynb
@@ -16,7 +16,7 @@
    "source": [
     "To get started:\n",
     "\n",
-    "- Set the `CLICKHOUSE_URL` environment variable. For example: `CLICKHOUSE_URL=\"http://localhost:8123/tensorzero\"`\n",
+    "- Set the `TENSORZERO_CLICKHOUSE_URL` environment variable. For example: `TENSORZERO_CLICKHOUSE_URL=\"http://localhost:8123/tensorzero\"`\n",
     "- Set the `OPENAI_API_KEY` environment variable.\n",
     "- Update the following parameters:\n"
    ]
@@ -193,9 +193,11 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "assert \"CLICKHOUSE_URL\" in os.environ, \"CLICKHOUSE_URL environment variable not set\"\n",
+    "assert \"TENSORZERO_CLICKHOUSE_URL\" in os.environ, (\n",
+    "    \"TENSORZERO_CLICKHOUSE_URL environment variable not set\"\n",
+    ")\n",
     "\n",
-    "clickhouse_client = get_client(dsn=os.environ[\"CLICKHOUSE_URL\"])"
+    "clickhouse_client = get_client(dsn=os.environ[\"TENSORZERO_CLICKHOUSE_URL\"])"
    ]
   },
   {
diff --git a/tensorzero_internal/src/clickhouse.rs b/tensorzero_internal/src/clickhouse.rs
index 97f69ae6c..90ff42a2e 100644
--- a/tensorzero_internal/src/clickhouse.rs
+++ b/tensorzero_internal/src/clickhouse.rs
@@ -66,10 +66,8 @@ impl ClickHouseConnectionInfo {
             database,
             client: Client::new(),
         };
-
-        // Error will be constructed and logged in health()
-        // Don't need to do anything with the error here
-        let _ = connection_info.health().await;
+        // If the connection is unhealthy, we won't be able to run / check migrations. So we just fail here.
+        connection_info.health().await?;
         Ok(connection_info)
     }
 
diff --git a/tensorzero_internal/src/config_parser.rs b/tensorzero_internal/src/config_parser.rs
index bc6dc3a65..fc6041bd9 100644
--- a/tensorzero_internal/src/config_parser.rs
+++ b/tensorzero_internal/src/config_parser.rs
@@ -31,16 +31,58 @@ pub struct Config<'c> {
     pub templates: TemplateConfig<'c>,
 }
 
+#[derive(Debug, Default)]
+pub struct GatewayConfig {
+    pub bind_address: Option<std::net::SocketAddr>,
+    pub observability: ObservabilityConfig,
+    pub debug: bool,
+}
+
+/// Note: This struct and the impl below can be removed in favor of a derived impl for Deserialize once we have removed the `disable_observability` flag
+/// TODO (#797): Remove this once we have removed the `disable_observability` flag
 #[derive(Debug, Default, Deserialize)]
 #[serde(deny_unknown_fields)]
-pub struct GatewayConfig {
+pub struct UninitializedGatewayConfig {
     pub bind_address: Option<std::net::SocketAddr>,
     #[serde(default)]
     pub disable_observability: bool,
     #[serde(default)]
+    pub observability: ObservabilityConfig,
+    #[serde(default)]
     pub debug: bool,
 }
 
+impl TryFrom<UninitializedGatewayConfig> for GatewayConfig {
+    type Error = Error;
+    fn try_from(config: UninitializedGatewayConfig) -> Result<Self, Self::Error> {
+        let enabled = match (config.disable_observability, config.observability.enabled) {
+            (true, Some(_)) => {
+                return Err(Error::new(ErrorDetails::Config {
+                    message: "Configuration flag `gateway.disable_observability` and `gateway.observability.enabled` are mutually exclusive. We are deprecating `gateway.disable_observability` in favor of `gateway.observability.enabled`. See https://github.com/tensorzero/tensorzero/issues/797 on GitHub for details.".to_string(),
+                }));
+            }
+            (true, None) => {
+                tracing::warn!("Deprecation Warning: The configuration flag `gateway.disable_observability.enabled` is deprecated in favor of `gateway.observability.enabled`. See https://github.com/tensorzero/tensorzero/issues/797 on GitHub for details.");
+                Some(false)
+            }
+            (false, Some(enabled)) => Some(enabled),
+            (false, None) => None,
+        };
+        Ok(Self {
+            bind_address: config.bind_address,
+            observability: ObservabilityConfig { enabled },
+            debug: config.debug,
+        })
+    }
+}
+
+#[derive(Debug, Default, Deserialize)]
+#[serde(deny_unknown_fields)]
+pub struct ObservabilityConfig {
+    #[serde(default)]
+    pub enabled: Option<bool>,
+}
+
 #[derive(Debug, Deserialize)]
 #[serde(deny_unknown_fields)]
 pub struct MetricConfig {
@@ -101,7 +143,7 @@ impl<'c> Config<'c> {
     fn load_from_toml(table: toml::Table, base_path: PathBuf) -> Result<Config<'c>, Error> {
         let config = UninitializedConfig::try_from(table)?;
 
-        let gateway = config.gateway.unwrap_or_default();
+        let gateway = config.gateway.unwrap_or_default().try_into()?;
 
         let templates = TemplateConfig::new();
 
@@ -292,7 +334,7 @@ impl<'c> Config<'c> {
 #[derive(Debug, Deserialize)]
 #[serde(deny_unknown_fields)]
 struct UninitializedConfig {
-    pub gateway: Option<GatewayConfig>,
+    pub gateway: Option<UninitializedGatewayConfig>,
     #[serde(default)]
     pub models: ModelTable, // model name => model config
     #[serde(default)]
diff --git a/tensorzero_internal/src/gateway_util.rs b/tensorzero_internal/src/gateway_util.rs
index ff4e50244..1990e1a69 100644
--- a/tensorzero_internal/src/gateway_util.rs
+++ b/tensorzero_internal/src/gateway_util.rs
@@ -20,7 +20,13 @@ pub type AppState = axum::extract::State<AppStateData>;
 
 impl AppStateData {
     pub async fn new(config: Arc<Config<'static>>) -> Result<Self, Error> {
-        let clickhouse_url = std::env::var("CLICKHOUSE_URL").ok();
+        let clickhouse_url = std::env::var("TENSORZERO_CLICKHOUSE_URL")
+            .ok()
+            .or_else(|| {
+                std::env::var("CLICKHOUSE_URL").ok().inspect(|_| {
+                    tracing::warn!("Deprecation Warning: The environment variable \"CLICKHOUSE_URL\" has been renamed to \"TENSORZERO_CLICKHOUSE_URL\" and will be removed in a future version. Please update your environment to use \"TENSORZERO_CLICKHOUSE_URL\" instead.");
+                })
+            });
         let clickhouse_connection_info = setup_clickhouse(&config, clickhouse_url).await?;
 
         let http_client = Client::new();
@@ -37,20 +43,23 @@ async fn setup_clickhouse(
     config: &Config<'static>,
     clickhouse_url: Option<String>,
 ) -> Result<ClickHouseConnectionInfo, Error> {
-    if config.gateway.disable_observability {
-        Ok(ClickHouseConnectionInfo::new_disabled())
-    } else {
-        let clickhouse_url = clickhouse_url.ok_or_else(|| {
-            Error::new(ErrorDetails::AppState {
-                message: "Missing environment variable CLICKHOUSE_URL".to_string(),
-            })
-        })?;
-
-        // If the ClickHouse URL is malformed, we will propagate the error upwards, and avoid starting the server
-        // If the ClickHouse URL is healthy, we will return a Production connection info
-        // If the ClickHouse URL is unhealthy, we will log an error and return a Production connection info
-        // (since the gateway and ClickHouse might be started simultaneously)
-        ClickHouseConnectionInfo::new(&clickhouse_url).await
+    match (config.gateway.observability.enabled, clickhouse_url) {
+        // Observability disabled by config
+        (Some(false), _) => Ok(ClickHouseConnectionInfo::new_disabled()),
+        // Observability enabled but no ClickHouse URL
+        (Some(true), None) => Err(ErrorDetails::AppState {
+            message: "Missing environment variable TENSORZERO_CLICKHOUSE_URL".to_string(),
+        }
+        .into()),
+        // Observability enabled and ClickHouse URL provided
+        (Some(true), Some(clickhouse_url)) => ClickHouseConnectionInfo::new(&clickhouse_url).await,
+        // Observability default and no ClickHouse URL
+        (None, None) => {
+            tracing::warn!("Observability not explicitly specified in config and no ClickHouse URL provided. Disabling ClickHouse.");
+            Ok(ClickHouseConnectionInfo::new_disabled())
+        }
+        // Observability default and ClickHouse URL provided
+        (None, Some(clickhouse_url)) => ClickHouseConnectionInfo::new(&clickhouse_url).await,
     }
 }
 
@@ -100,18 +109,19 @@ where
 
 #[cfg(test)]
 mod tests {
-    use secrecy::ExposeSecret;
     use tracing_test::traced_test;
 
     use super::*;
-    use crate::config_parser::GatewayConfig;
+    use crate::config_parser::{GatewayConfig, ObservabilityConfig};
 
     #[tokio::test]
     #[traced_test]
     async fn test_setup_clickhouse() {
         // Disabled observability
         let gateway_config = GatewayConfig {
-            disable_observability: true,
+            observability: ObservabilityConfig {
+                enabled: Some(false),
+            },
             bind_address: None,
             debug: false,
         };
@@ -126,11 +136,36 @@ mod tests {
             clickhouse_connection_info,
             ClickHouseConnectionInfo::Disabled
         ));
-        assert!(!logs_contain("Missing environment variable CLICKHOUSE_URL"));
+        assert!(!logs_contain(
+            "Missing environment variable TENSORZERO_CLICKHOUSE_URL"
+        ));
+
+        // Default observability and no ClickHouse URL
+        let gateway_config = GatewayConfig {
+            observability: ObservabilityConfig { enabled: None },
+            ..Default::default()
+        };
+        let config = Box::leak(Box::new(Config {
+            gateway: gateway_config,
+            ..Default::default()
+        }));
+        let clickhouse_connection_info = setup_clickhouse(config, None).await.unwrap();
+        assert!(matches!(
+            clickhouse_connection_info,
+            ClickHouseConnectionInfo::Disabled
+        ));
+        assert!(!logs_contain(
+            "Missing environment variable TENSORZERO_CLICKHOUSE_URL"
+        ));
+
+        // We do not test the case where a ClickHouse URL is provided but observability is default,
+        // as this would require a working ClickHouse and we don't have one in unit tests.
 
         // Observability enabled but ClickHouse URL is missing
         let gateway_config = GatewayConfig {
-            disable_observability: false,
+            observability: ObservabilityConfig {
+                enabled: Some(true),
+            },
             bind_address: None,
             debug: false,
         };
@@ -140,13 +175,16 @@ mod tests {
             ..Default::default()
         }));
 
-        setup_clickhouse(config, None)
-            .await
-            .expect_err("Missing environment variable CLICKHOUSE_URL");
+        let err = setup_clickhouse(config, None).await.unwrap_err();
+        assert!(err
+            .to_string()
+            .contains("Missing environment variable TENSORZERO_CLICKHOUSE_URL"));
 
         // Bad URL
         let gateway_config = GatewayConfig {
-            disable_observability: false,
+            observability: ObservabilityConfig {
+                enabled: Some(true),
+            },
             bind_address: None,
             debug: false,
         };
@@ -156,12 +194,14 @@ mod tests {
         }));
         setup_clickhouse(config, Some("bad_url".to_string()))
             .await
-            .expect_err("Missing environment variable CLICKHOUSE_URL");
+            .expect_err("ClickHouse setup should fail given a bad URL");
         assert!(logs_contain("Invalid ClickHouse database URL"));
 
         // Sensible URL that doesn't point to ClickHouse
         let gateway_config = GatewayConfig {
-            disable_observability: false,
+            observability: ObservabilityConfig {
+                enabled: Some(true),
+            },
             bind_address: None,
             debug: false,
         };
@@ -169,22 +209,15 @@ mod tests {
             gateway: gateway_config,
             ..Default::default()
         }));
-        let clickhouse_connection_info =
-            setup_clickhouse(config, Some("https://tensorzero.com:8123".to_string()))
-                .await
-                .unwrap();
-        // If the ClickHouse URL is well-formed but just not pointing at a healthy ClickHouse,
-        // we will log a connection error and still return a Production connection info
-        // so that we start logging errors on writes
-        match clickhouse_connection_info {
-            ClickHouseConnectionInfo::Production { database_url, .. } => {
-                let database_url = database_url.expose_secret();
-                assert!(database_url.starts_with("https://tensorzero.com:8123"));
-            }
-            _ => panic!("Expected production ClickHouse connection info"),
-        }
+        setup_clickhouse(config, Some("https://tensorzero.com:8123".to_string()))
+            .await
+            .expect_err(
+                "ClickHouse setup should fail given a URL that doesn't point to ClickHouse",
+            );
         assert!(logs_contain(
             "Error connecting to ClickHouse: ClickHouse is not healthy"
         ));
+        // We do not test the case where a ClickHouse URL is provided and observability is on,
+        // as this would require a working ClickHouse and we don't have one in unit tests.
     }
 }
diff --git a/ui/.env.example b/ui/.env.example
index 2c0ff2311..6554ec741 100644
--- a/ui/.env.example
+++ b/ui/.env.example
@@ -1,4 +1,4 @@
 OPENAI_API_KEY="example"
 FIREWORKS_API_KEY="example"
 FIREWORKS_ACCOUNT_ID="example"
-CLICKHOUSE_URL="http://host.docker.internal:8123/tensorzero" # or the URL of a remote ClickHouse instance
+TENSORZERO_CLICKHOUSE_URL="http://host.docker.internal:8123/tensorzero" # or the URL of a remote ClickHouse instance
diff --git a/ui/app/utils/clickhouse/common.ts b/ui/app/utils/clickhouse/common.ts
index 0116d3dd9..436f4760f 100644
--- a/ui/app/utils/clickhouse/common.ts
+++ b/ui/app/utils/clickhouse/common.ts
@@ -4,7 +4,17 @@ import type { FunctionConfig } from "../config/function";
 import { createClient } from "@clickhouse/client";
 
 export const clickhouseClient = createClient({
-  url: process.env.CLICKHOUSE_URL,
+  url:
+    process.env.TENSORZERO_CLICKHOUSE_URL ??
+    (() => {
+      if (process.env.CLICKHOUSE_URL) {
+        console.warn(
+          'Deprecation Warning: The environment variable "CLICKHOUSE_URL" has been renamed to "TENSORZERO_CLICKHOUSE_URL" and will be removed in a future version. Please update your environment to use "TENSORZERO_CLICKHOUSE_URL" instead.',
+        );
+        return process.env.CLICKHOUSE_URL;
+      }
+      return undefined;
+    })(),
 });
 
 export async function checkClickHouseConnection(): Promise<boolean> {
diff --git a/ui/fixtures/docker-compose.yml b/ui/fixtures/docker-compose.yml
index 98cd1536a..c3beb8db8 100644
--- a/ui/fixtures/docker-compose.yml
+++ b/ui/fixtures/docker-compose.yml
@@ -35,7 +35,7 @@ services:
     volumes:
       - ./config:/app/config:ro
     environment:
-      - CLICKHOUSE_URL=http://tensorzero-clickhouse-ui-tests:8123/tensorzero
+      - TENSORZERO_CLICKHOUSE_URL=http://tensorzero-clickhouse-ui-tests:8123/tensorzero
     env_file:
       - ${ENV_FILE:-../.env}
     ports:


Test patch:
diff --git a/recipes/tests/test_clickhouse_queries.py b/recipes/tests/test_clickhouse_queries.py
index 41faf7d20..db61c3757 100644
--- a/recipes/tests/test_clickhouse_queries.py
+++ b/recipes/tests/test_clickhouse_queries.py
@@ -17,8 +17,10 @@ def test_double_feedback_query() -> None:
     This test checks that the query works as expected.
     """
 
-    assert "CLICKHOUSE_URL" in os.environ, "CLICKHOUSE_URL environment variable not set"
-    client = get_client(dsn=os.environ["CLICKHOUSE_URL"])
+    assert "TENSORZERO_CLICKHOUSE_URL" in os.environ, (
+        "TENSORZERO_CLICKHOUSE_URL environment variable not set"
+    )
+    client = get_client(dsn=os.environ["TENSORZERO_CLICKHOUSE_URL"])
     # Insert an Inference we can use to assign feedback to
     inference = {
         "id": str(uuid7()),
diff --git a/tensorzero_internal/tests/e2e/README.md b/tensorzero_internal/tests/e2e/README.md
index 4b55cda79..1a43211d1 100644
--- a/tensorzero_internal/tests/e2e/README.md
+++ b/tensorzero_internal/tests/e2e/README.md
@@ -8,7 +8,7 @@
   docker compose -f gateway/tests/e2e/docker-compose.yml up -d --build --force-recreate --remove-orphans  --wait
   ```
 
-- Set the `CLICKHOUSE_URL` environment variable to the URL of the ClickHouse container (e.g. `CLICKHOUSE_URL=http://localhost:8123`).
+- Set the `TENSORZERO_CLICKHOUSE_URL` environment variable to the URL of the ClickHouse container (e.g. `TENSORZERO_CLICKHOUSE_URL=http://localhost:8123`).
 
 - Spin up the gateway: `cargo run-e2e` or `cargo watch-e2e`
 
diff --git a/tensorzero_internal/tests/e2e/common.rs b/tensorzero_internal/tests/e2e/common.rs
index bd5fc9d59..641e147a3 100644
--- a/tensorzero_internal/tests/e2e/common.rs
+++ b/tensorzero_internal/tests/e2e/common.rs
@@ -4,7 +4,7 @@ use tensorzero_internal::clickhouse::ClickHouseConnectionInfo;
 use uuid::Uuid;
 
 lazy_static::lazy_static! {
-    pub static ref CLICKHOUSE_URL: String = std::env::var("CLICKHOUSE_URL").expect("Environment variable CLICKHOUSE_URL must be set");
+    pub static ref CLICKHOUSE_URL: String = std::env::var("TENSORZERO_CLICKHOUSE_URL").expect("Environment variable TENSORZERO_CLICKHOUSE_URL must be set");
     static ref GATEWAY_URL: String = std::env::var("GATEWAY_URL").unwrap_or("http://localhost:3000".to_string());
 }