diff --git a/examples/vtadmin/Makefile b/examples/vtadmin/Makefile
new file mode 100644
index 00000000000..e41aea7cd27
--- /dev/null
+++ b/examples/vtadmin/Makefile
@@ -0,0 +1,31 @@
+# Makefile for VTAdmin demo cluster
+
+.PHONY: up down restart logs clean reset
+
+# Base command with all required files and environment variables
+COMPOSE_CMD = docker compose -f ../compose/docker-compose.yml -f docker-compose.yml --env-file ../compose/template.env
+
+# Start the cluster
+up:
+ $(COMPOSE_CMD) up -d --force-recreate
+
+# Stop the cluster
+down:
+ $(COMPOSE_CMD) down --remove-orphans
+
+# Restart running services
+restart:
+ $(COMPOSE_CMD) restart
+
+# Perform a full reset (down with volume cleanup, then up)
+reset:
+ $(COMPOSE_CMD) down --remove-orphans
+ $(COMPOSE_CMD) up -d --force-recreate
+
+# Stream logs from all services
+logs:
+ $(COMPOSE_CMD) logs -f
+
+# Clean up all resources including volumes
+clean:
+ $(COMPOSE_CMD) down -v --remove-orphans
diff --git a/examples/vtadmin/README.md b/examples/vtadmin/README.md
new file mode 100644
index 00000000000..1add40ab068
--- /dev/null
+++ b/examples/vtadmin/README.md
@@ -0,0 +1,58 @@
+# VTAdmin Demo
+
+This example provides a fully functional local Vitess cluster with **VTAdmin**, **Grafana**, and **Percona Monitoring and Management (PMM)** pre-configured and integrated.
+
+It demonstrates how VTAdmin can serve as a single pane of glass for your database infrastructure, providing context-aware deep links to your monitoring dashboards.
+
+## Quick Start
+
+1. **Start the cluster**:
+ ```bash
+ cd examples/vtadmin
+ make up
+ ```
+2. **Access VTAdmin**:
+ Open **http://localhost:5173** in your browser.
+
+## Features
+
+- **Unified Interface**: View cluster topology, tablet health, and gates.
+- **Integrated Monitoring**:
+ - **Vitess Metrics**: Deep links to Grafana dashboards for clusters, tablets, and gates.
+ - **MySQL Metrics**: Deep links to PMM for database instance analysis.
+- **Pre-configured Stack**: Includes Prometheus, Grafana, and PMM running alongside Vitess.
+
+## Service Endpoints
+
+| Service | URL | Credentials |
+|---------|-----|-------------|
+| **VTAdmin** | http://localhost:5173 | - |
+| **Grafana** | http://localhost:3000 | default |
+| **PMM** | http://localhost:8888 | default |
+| **Prometheus** | http://localhost:9090 | - |
+
+## Configuration
+
+The dashboard links in VTAdmin are configured via environment variables in `docker-compose.yml`. You can modify these to point to your own external monitoring infrastructure:
+
+```yaml
+vtadmin-web:
+ environment:
+ VITE_VITESS_MONITORING_CLUSTER_TEMPLATE: "http://your-grafana/..."
+ VITE_MYSQL_MONITORING_TEMPLATE: "http://your-pmm/..."
+```
+
+See `web/vtadmin/README.md` for all available environment variables.
+
+## Common Commands
+
+### Cluster Control
+```bash
+make up # Start cluster with current configuration
+make restart # Restart all services
+make reset # Full teardown and fresh start (removes volumes)
+make down # Stop all services
+make clean # Stop and remove all data volumes
+make logs # Stream logs from all services
+```
+
diff --git a/examples/vtadmin/config/discovery.json b/examples/vtadmin/config/discovery.json
new file mode 100644
index 00000000000..d998d4b114b
--- /dev/null
+++ b/examples/vtadmin/config/discovery.json
@@ -0,0 +1,25 @@
+{
+ "clusters": {
+ "local": {
+ "name": "local",
+ "discovery": "staticfile",
+ "discovery-staticfile-path": "/app/discovery.json"
+ }
+ },
+ "vtgates": [
+ {
+ "host": {
+ "hostname": "vtgate:15999"
+ },
+ "tags": ["cell:test"]
+ }
+ ],
+ "vtctlds": [
+ {
+ "host": {
+ "hostname": "vtctld:15999"
+ },
+ "tags": ["cell:test"]
+ }
+ ]
+}
diff --git a/examples/vtadmin/config/grafana-datasource-prometheus.yaml b/examples/vtadmin/config/grafana-datasource-prometheus.yaml
new file mode 100644
index 00000000000..0b0a57dde1a
--- /dev/null
+++ b/examples/vtadmin/config/grafana-datasource-prometheus.yaml
@@ -0,0 +1,15 @@
+# Grafana datasource configuration
+#
+# Automatically provisions Prometheus as a datasource for Grafana.
+# Used by the Grafana container to connect to Prometheus metrics.
+#
+# Access: http://localhost:3000 (admin/admin)
+
+apiVersion: 1
+
+datasources:
+ - name: Prometheus
+ type: prometheus
+ access: proxy
+ url: http://prometheus:9090
+ isDefault: true
diff --git a/examples/vtadmin/config/prometheus.yml b/examples/vtadmin/config/prometheus.yml
new file mode 100644
index 00000000000..ae9009635d0
--- /dev/null
+++ b/examples/vtadmin/config/prometheus.yml
@@ -0,0 +1,17 @@
+# Prometheus configuration for VTAdmin demo cluster
+#
+# Collects metrics from all Vitess components:
+# - vtctld: Topology server
+# - vtgate: Query router
+# - vttablets: Database tablets
+#
+# Scrape interval: 10 seconds
+# Metrics accessible at: http://localhost:9090
+
+global:
+ scrape_interval: 10s
+
+scrape_configs:
+ - job_name: 'vitess'
+ static_configs:
+ - targets: ['vtctld:8080', 'vtgate:8080', 'vttablet101:8080', 'vttablet102:8080', 'vttablet201:8080', 'vttablet202:8080', 'vttablet301:8080', 'vttablet302:8080']
diff --git a/examples/vtadmin/docker-compose.yml b/examples/vtadmin/docker-compose.yml
new file mode 100644
index 00000000000..6469435e71a
--- /dev/null
+++ b/examples/vtadmin/docker-compose.yml
@@ -0,0 +1,160 @@
+# VTAdmin Demo - Docker Compose Overrides
+#
+# This file extends the base Vitess cluster (../compose/docker-compose.yml) with:
+# - VTAdmin web UI (port 5173)
+# - Grafana dashboards for Vitess metrics (port 3000)
+# - PMM monitoring for MySQL instances (port 8888)
+# - Prometheus metrics collection (port 9090)
+#
+# Usage:
+# make up # Start cluster
+# make logs # View logs
+# make reset # Full cleanup and restart
+#
+# See README.md for detailed documentation.
+
+services:
+ vtadmin-web:
+ image: node:22
+ working_dir: /app
+ volumes:
+ - ${PWD}/../../web/vtadmin:/app
+ environment:
+ VITE_VTADMIN_API_ADDRESS: http://localhost:14200
+ # VITE_VITESS_MONITORING_DASHBOARD_TITLE: Grafana
+ # VITE_MYSQL_MONITORING_DASHBOARD_TITLE: PMM
+ VITE_VITESS_MONITORING_CLUSTER_TEMPLATE: http://localhost:3000/d/vitess_summary/vitess-summary
+ VITE_VITESS_MONITORING_VTTABLET_TEMPLATE: http://localhost:3000/d/vitess_summary/vitess-summary?var-alias={alias}
+ VITE_VITESS_MONITORING_VTGATE_TEMPLATE: http://localhost:3000/d/vitess_summary/vitess-summary
+ VITE_MYSQL_MONITORING_TEMPLATE: http://localhost:8888/graph/d/mysql-instance-overview/mysql-instances-overview?var-service_name={hostname}-mysql
+ command:
+ - /bin/sh
+ - -c
+ - npm install && npm run start -- --host
+ ports:
+ - "5173:5173"
+ depends_on:
+ - vtadmin-api
+
+ vtadmin-api:
+ image: vitess/lite:${VITESS_TAG:-latest}
+ command:
+ - vtadmin
+ - --addr=:14200
+ - --http-origin=*
+ - --http-tablet-url-tmpl=http://{{ .Tablet.Hostname }}:80
+ - --cluster-config=/app/discovery.json
+ - --no-rbac
+ volumes:
+ - ${PWD}/config/discovery.json:/app/discovery.json
+ ports:
+ - "14200:14200"
+ depends_on:
+ - vtctld
+
+ vttablet101:
+ volumes:
+ - ${PWD}/scripts/vttablet-up.sh:/script/vttablet-up.sh
+
+ vttablet102:
+ volumes:
+ - ${PWD}/scripts/vttablet-up.sh:/script/vttablet-up.sh
+
+ vttablet201:
+ volumes:
+ - ${PWD}/scripts/vttablet-up.sh:/script/vttablet-up.sh
+
+ vttablet202:
+ volumes:
+ - ${PWD}/scripts/vttablet-up.sh:/script/vttablet-up.sh
+
+ vttablet301:
+ volumes:
+ - ${PWD}/scripts/vttablet-up.sh:/script/vttablet-up.sh
+
+ vttablet302:
+ volumes:
+ - ${PWD}/scripts/vttablet-up.sh:/script/vttablet-up.sh
+
+ cluster-init:
+ image: vitess/lite:${VITESS_TAG:-latest}
+ command:
+ - sh
+ - -c
+ - /script/init_cluster.sh
+ volumes:
+ - ${PWD}/scripts/init_cluster.sh:/script/init_cluster.sh
+ depends_on:
+ - vtctld
+ - vttablet101
+ - vttablet102
+ - vttablet201
+ - vttablet202
+ - vttablet301
+ - vttablet302
+
+ pmm-server:
+ image: percona/pmm-server:2
+ ports:
+ - "8888:80"
+ - "8889:443"
+ volumes:
+ - pmm-data:/srv
+
+ pmm-setup:
+ image: percona/pmm-client:2
+ entrypoint: /bin/sh
+ depends_on:
+ - pmm-server
+ - vtgate
+ - vttablet101
+ - vttablet102
+ - vttablet201
+ - vttablet202
+ - vttablet301
+ - vttablet302
+ command:
+ - -c
+ - |
+ echo "Waiting for PMM Server..."
+ until curl -k -s https://admin:admin@pmm-server/v1/ready; do sleep 5; done
+ echo "PMM Server is ready."
+
+ # Register PMM Client
+ pmm-agent setup --config-file=/usr/local/percona/pmm2/config/pmm-agent.yaml --server-address=pmm-server:443 --server-insecure-tls --server-username=admin --server-password=admin
+
+ # Start pmm-agent in background
+ pmm-agent --config-file=/usr/local/percona/pmm2/config/pmm-agent.yaml &
+
+ # Wait for agent to be ready
+ sleep 10
+
+ # Add vttablet MySQLs
+ for i in 101 102 201 202 301 302; do
+ echo "Adding vttablet$$i MySQL..."
+ pmm-admin add mysql --username=pmm --password=pmm --host=vttablet$$i --port=3306 --service-name=vttablet$$i-mysql --query-source=perfschema || true
+ done
+
+ echo "Setup complete."
+
+ # Keep container running
+ wait
+
+ prometheus:
+ image: prom/prometheus
+ ports:
+ - "9090:9090"
+ volumes:
+ - ${PWD}/config/prometheus.yml:/etc/prometheus/prometheus.yml
+
+ grafana:
+ image: grafana/grafana:latest
+ ports:
+ - "3000:3000"
+ volumes:
+ - ${PWD}/config/grafana-datasource-prometheus.yaml:/etc/grafana/provisioning/datasources/prometheus.yaml
+ depends_on:
+ - prometheus
+
+volumes:
+ pmm-data:
diff --git a/examples/vtadmin/scripts/init_cluster.sh b/examples/vtadmin/scripts/init_cluster.sh
new file mode 100644
index 00000000000..a255766bce0
--- /dev/null
+++ b/examples/vtadmin/scripts/init_cluster.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# Copyright 2025 The Vitess Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -u
+
+echo "Waiting for vtctld..."
+until vtctldclient --server vtctld:15999 GetKeyspaces; do
+ sleep 1
+done
+
+echo "Waiting for tablets..."
+# We expect 6 tablets
+while [ $(vtctldclient --server vtctld:15999 GetTablets | wc -l) -lt 6 ]; do
+ sleep 1
+done
+
+echo "Initializing shards..."
+
+# Initialize test_keyspace/-80
+echo "Initializing test_keyspace/-80..."
+vtctldclient --server vtctld:15999 PlannedReparentShard --new-primary test-0000000101 test_keyspace/-80 || echo "Failed to init test_keyspace/-80 or already initialized"
+
+# Initialize test_keyspace/80-
+echo "Initializing test_keyspace/80-..."
+vtctldclient --server vtctld:15999 PlannedReparentShard --new-primary test-0000000201 test_keyspace/80- || echo "Failed to init test_keyspace/80- or already initialized"
+
+# Initialize lookup_keyspace/-
+echo "Initializing lookup_keyspace/-..."
+vtctldclient --server vtctld:15999 PlannedReparentShard --new-primary test-0000000301 lookup_keyspace/- || echo "Failed to init lookup_keyspace/- or already initialized"
+
+echo "Cluster initialization complete."
diff --git a/examples/vtadmin/scripts/vttablet-up.sh b/examples/vtadmin/scripts/vttablet-up.sh
new file mode 100644
index 00000000000..a8ce749451c
--- /dev/null
+++ b/examples/vtadmin/scripts/vttablet-up.sh
@@ -0,0 +1,198 @@
+#!/bin/bash
+
+# Copyright 2025 The Vitess Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -u
+export VTROOT=/vt
+export VTDATAROOT=/vt/vtdataroot
+
+keyspace=${KEYSPACE:-'test_keyspace'}
+shard=${SHARD:-'0'}
+grpc_port=${GRPC_PORT:-'15999'}
+web_port=${WEB_PORT:-'8080'}
+role=${ROLE:-'replica'}
+vthost=${VTHOST:-`hostname -i`}
+sleeptime=${SLEEPTIME:-'0'}
+uid=$1
+external=${EXTERNAL_DB:-0}
+
+# If DB is not explicitly set, we default to behaviour of prefixing with vt_
+# If there is an external db, the db_nmae will always match the keyspace name
+[ $external = 0 ] && db_name=${DB:-"vt_$keyspace"} || db_name=${DB:-"$keyspace"}
+db_charset=${DB_CHARSET:-''}
+tablet_hostname=''
+
+# Use IPs to simplify connections when testing in docker.
+# Otherwise, blank hostname means the tablet auto-detects FQDN.
+# This is now set further up
+
+printf -v alias '%s-%010d' $CELL $uid
+printf -v tablet_dir 'vt_%010d' $uid
+
+tablet_role=$role
+tablet_type='replica'
+
+# Make every 3rd tablet rdonly
+if (( $uid % 100 % 3 == 0 )) ; then
+ tablet_type='rdonly'
+fi
+
+# Consider every tablet with %d00 as external primary
+if [ $external = 1 ] && (( $uid % 100 == 0 )) ; then
+ tablet_type='replica'
+ tablet_role='externalprimary'
+ keyspace="ext_$keyspace"
+fi
+
+# Copy config directory
+cp -R /script/config $VTROOT
+init_db_sql_file="$VTROOT/config/init_db.sql"
+
+# Prepend SET sql_log_bin = 0; to init_db.sql to prevent Errant GTIDs during initialization
+sed -i '1s/^/SET sql_log_bin = 0;\n/' $init_db_sql_file
+
+# Clear in-place edits of init_db_sql_file if any exist
+sed -i '/##\[CUSTOM_SQL/{:a;N;/END\]##/!ba};//d' $init_db_sql_file
+
+echo "##[CUSTOM_SQL_START]##" >> $init_db_sql_file
+
+if [ "$external" = "1" ]; then
+ # We need a common user for the unmanaged and managed tablets else tools like orchestrator will not function correctly
+ echo "Creating matching user for managed tablets..."
+ echo "CREATE USER IF NOT EXISTS '$DB_USER'@'%' IDENTIFIED BY '$DB_PASS';" >> $init_db_sql_file
+ echo "GRANT ALL ON *.* TO '$DB_USER'@'%';" >> $init_db_sql_file
+fi
+echo "##[CUSTOM_SQL_END]##" >> $init_db_sql_file
+
+echo "##[CUSTOM_SQL_END]##" >> $init_db_sql_file
+
+mkdir -p $VTDATAROOT/backups
+
+
+export KEYSPACE=$keyspace
+export SHARD=$shard
+export TABLET_ID=$alias
+export TABLET_DIR=$tablet_dir
+export MYSQL_PORT=3306
+export TABLET_ROLE=$tablet_role
+export DB_PORT=${DB_PORT:-3306}
+export DB_HOST=${DB_HOST:-""}
+export DB_NAME=$db_name
+
+# Delete socket files before running mysqlctld if exists.
+# This is the primary reason for unhealthy state on restart.
+# https://github.com/vitessio/vitess/pull/5115/files
+echo "Removing $VTDATAROOT/$tablet_dir/{mysql.sock,mysql.sock.lock}..."
+rm -rf $VTDATAROOT/$tablet_dir/{mysql.sock,mysql.sock.lock}
+
+# Create mysql instances
+# Do not create mysql instance for primary if connecting to external mysql database
+#TODO: Remove underscore(_) flags in v25, replace them with dashed(-) notation
+if [[ $tablet_role != "externalprimary" ]]; then
+ echo "Initing mysql for tablet: $uid role: $role external: $external.. "
+ $VTROOT/bin/mysqlctld \
+ --init-db-sql-file=$init_db_sql_file \
+ --logtostderr=true \
+ --tablet-uid=$uid \
+ &
+
+ # Wait for MySQL to be ready
+ echo "Waiting for MySQL to be ready on $VTDATAROOT/$tablet_dir/mysql.sock..."
+ set -x
+ for i in {1..60}; do
+ if mysql -u root --socket=$VTDATAROOT/$tablet_dir/mysql.sock -e "SELECT 1" >/dev/null 2>&1; then
+ set +x
+ echo "MySQL is ready!"
+ set -x
+
+ # Ensure the database exists
+ echo "Ensuring database $db_name exists..."
+ mysql -u root --socket=$VTDATAROOT/$tablet_dir/mysql.sock -e "SET sql_log_bin = 0; SET GLOBAL super_read_only=0; SET GLOBAL read_only=0; CREATE DATABASE IF NOT EXISTS $db_name" || echo "Failed to create DB"
+
+ # Create PMM user
+ echo "Creating PMM user..."
+ mysql -u root --socket=$VTDATAROOT/$tablet_dir/mysql.sock -e "SET sql_log_bin = 0; SET GLOBAL super_read_only=0; SET GLOBAL read_only=0; CREATE USER IF NOT EXISTS 'pmm'@'%' IDENTIFIED BY 'pmm'; GRANT SELECT, PROCESS, REPLICATION CLIENT, RELOAD ON *.* TO 'pmm'@'%'; GRANT SELECT, UPDATE, DELETE, DROP ON performance_schema.* TO 'pmm'@'%'; FLUSH PRIVILEGES;" || echo "Failed to create PMM user"
+
+ # Verify PMM user
+ echo "Verifying PMM user..."
+ mysql -u root --socket=$VTDATAROOT/$tablet_dir/mysql.sock -e "SELECT user, host FROM mysql.user WHERE user='pmm'" || echo "Failed to verify PMM user"
+
+ set +x
+ break
+ fi
+ if [ $i -eq 60 ]; then
+ echo "ERROR: MySQL failed to start after 60 attempts"
+ exit 1
+ fi
+ echo "Attempt $i/60: MySQL not ready yet, waiting..."
+ sleep 2
+ done
+fi
+
+sleep $sleeptime
+
+# Create the cell
+# https://vitess.io/blog/2020-04-27-life-of-a-cluster/
+$VTROOT/bin/vtctldclient --server vtctld:$GRPC_PORT AddCellInfo --root vitess/$CELL --server-address consul1:8500 $CELL || true
+
+#Populate external db conditional args
+if [ $tablet_role = "externalprimary" ]; then
+ echo "Setting external db args for primary: $DB_NAME"
+ external_db_args="--db-host $DB_HOST \
+ --db-port $DB_PORT \
+ --init-db-name-override $DB_NAME \
+ --init-tablet-type $tablet_type \
+ --mycnf-server-id $uid \
+ --db-app-user $DB_USER \
+ --db-app-password $DB_PASS \
+ --db-allprivs-user $DB_USER \
+ --db-allprivs-password $DB_PASS \
+ --db-appdebug-user $DB_USER \
+ --db-appdebug-password $DB_PASS \
+ --db-dba-user $DB_USER \
+ --db-dba-password $DB_PASS \
+ --db-filtered-user $DB_USER \
+ --db-filtered-password $DB_PASS \
+ --db-repl-user $DB_USER \
+ --db-repl-password $DB_PASS \
+ --enable-replication-reporter=false \
+ --enforce-strict-trans-tables=false \
+ --track-schema-versions=true \
+ --vreplication-tablet-type=primary \
+ --watch-replication-stream=true"
+else
+ external_db_args="--init-db-name-override $DB_NAME \
+ --init-tablet-type $tablet_type \
+ --enable-replication-reporter=true \
+ --restore-from-backup"
+fi
+
+#TODO: Remove underscore(_) flags in v25, replace them with dashed(-) notation
+echo "Starting vttablet..."
+exec $VTROOT/bin/vttablet \
+ $TOPOLOGY_FLAGS \
+ --logtostderr=true \
+ --tablet-path $alias \
+ --tablet-hostname "$vthost" \
+ --health-check-interval 5s \
+ --port $web_port \
+ --grpc-port $grpc_port \
+ --service-map 'grpc-queryservice,grpc-tabletmanager,grpc-updatestream' \
+ --init-keyspace $keyspace \
+ --init-shard $shard \
+ --backup-storage-implementation file \
+ --file-backup-storage-root $VTDATAROOT/backups \
+ --queryserver-config-schema-reload-time 60s \
+ $external_db_args
diff --git a/web/vtadmin/README.md b/web/vtadmin/README.md
index 629fe116711..ae8dfc32c4d 100644
--- a/web/vtadmin/README.md
+++ b/web/vtadmin/README.md
@@ -18,6 +18,31 @@ Scripts for common and not-so-common tasks. These are always run from the `vites
| `npm run lint:fix` | Run all of the linters and fix errors (where possible) in place. Note that this will overwrite your files so you may want to consider committing your work beforehand! |
| `npm run build` | Generates a build of vtadmin-web for production and outputs the files to the `vitess/web/vtadmin/build` folder. In most cases, you won't need to run this locally, but it _can_ be useful for debugging production-specific issues. See the vite documentation about [testing the build locally](https://vitejs.dev/guide/static-deploy.html#testing-the-app-locally) for more information. |
+## Dashboards
+
+VTAdmin can be configured to link to external dashboards (e.g., Grafana, PMM) for clusters, gates and tablets. These links are configured via environment variables.
+
+The variables accept template strings with placeholders that are replaced with the relevant entity's data.
+
+| Variable | Description | Supported Placeholders |
+|---|---|---|
+| `VITE_VITESS_MONITORING_CLUSTER_TEMPLATE` | URL template for cluster metrics. | `{cluster}`, `{cluster_id}`, `{id}` |
+| `VITE_VITESS_MONITORING_VTTABLET_TEMPLATE` | URL template for tablet metrics. | `{cluster}`, `{keyspace}`, `{shard}`, `{alias}`, `{hostname}`, `{type}`, `{cell}` |
+| `VITE_VITESS_MONITORING_VTGATE_TEMPLATE` | URL template for VTGate metrics. | `{cluster}`, `{cluster_id}`, `{cell}`, `{hostname}`, `{pool}` |
+| `VITE_MYSQL_MONITORING_TEMPLATE` | URL template for MySQL metrics (e.g., PMM). Adds a "Metrics" column to Tablets and Gates views. | `{cluster}`, `{keyspace}`, `{shard}`, `{alias}`, `{hostname}`, `{type}`, `{cell}`, `{pool}` |
+| `VITE_VITESS_MONITORING_DASHBOARD_TITLE` | Title for the Vitess monitoring column/link. Defaults to "Vt Monitoring Dashboard". | N/A |
+| `VITE_MYSQL_MONITORING_DASHBOARD_TITLE` | Title for the MySQL monitoring column/link. Defaults to "DB Monitoring Dashboard". | N/A |
+
+### Example Configuration
+
+Create a `.env.local` file in `web/vtadmin/` to test locally:
+
+```bash
+VITE_VITESS_MONITORING_CLUSTER_TEMPLATE="https://grafana.example.com/d/cluster?var-cluster={cluster}"
+VITE_VITESS_MONITORING_VTTABLET_TEMPLATE="https://grafana.example.com/d/tablet?var-alias={alias}"
+VITE_MYSQL_MONITORING_TEMPLATE="https://pmm.example.com/graph/d/mysql?var-host={hostname}"
+```
+
## Toolchain
- [React](https://reactjs.org/)
diff --git a/web/vtadmin/src/components/routes/Clusters.tsx b/web/vtadmin/src/components/routes/Clusters.tsx
index c9ccf64772a..01e06acd6eb 100644
--- a/web/vtadmin/src/components/routes/Clusters.tsx
+++ b/web/vtadmin/src/components/routes/Clusters.tsx
@@ -25,6 +25,8 @@ import { WorkspaceHeader } from '../layout/WorkspaceHeader';
import { WorkspaceTitle } from '../layout/WorkspaceTitle';
import { QueryLoadingPlaceholder } from '../placeholders/QueryLoadingPlaceholder';
import ClusterRow from './clusters/ClusterRow';
+import { getVTClusterMonitoringTemplate, getVitessMonitoringDashboardTitle } from '../../util/env';
+
export const Clusters = () => {
useDocumentTitle('Clusters');
const clustersQuery = useClusters();
@@ -36,6 +38,11 @@ export const Clusters = () => {
const renderRows = (rows: pb.Cluster[]) =>
rows.map((cluster, idx) =>