This guide walks you through setting up a local Grafana instance with MotherDuck-DuckDB integration, allowing you to create powerful visualizations and dashboards from your MotherDuck data.
Prerequisites
Before you begin, ensure you have:
Docker installed (Installation guide )
A MotherDuck token set as an environment variable: motherduck_token
The repository cloned locally
Quick Start
The setup process is automated through a setup script that handles plugin installation, Docker configuration, and Grafana deployment.
Set your MotherDuck token
Export your MotherDuck token as an environment variable: export motherduck_token = "your_token_here"
The setup script validates this environment variable and will fail if it’s not set.
Run the setup script
Execute the automated setup script: The script performs the following actions:
Detects your OS (macOS, Linux, or Windows)
Downloads the latest MotherDuck-DuckDB Grafana plugin from GitHub
Checks for existing Grafana containers on port 3000 and removes them
Validates port 3000 availability
Starts a new Grafana Docker container with the plugin mounted
Access Grafana
Once the script completes, access Grafana at: The default credentials are admin/admin. You’ll be prompted to change the password on first login.
Setup Script Details
The setup.sh script automates the entire deployment process:
#!/bin/bash
set -e
# Constants
PLUGIN_REPO = "https://api.github.com/repos/motherduckdb/grafana-duckdb-datasource/releases/latest"
CURRENT_DIR = "$( cd "$( dirname "${ BASH_SOURCE [0]}")" && pwd )"
PROVISIONING_DIR = " $CURRENT_DIR /provisioning"
# Detect OS and set plugin directory
case "$( uname -s )" in
Darwin )
PLUGIN_DIR = " $CURRENT_DIR /plugins/motherduck-duckdb-datasource"
;;
Linux )
PLUGIN_DIR = " $CURRENT_DIR /plugins/motherduck-duckdb-datasource"
;;
CYGWIN * | MINGW * | MSYS * )
echo "⚠ Windows detected. Please manually unzip the plugin."
exit 1
;;
esac
#!/bin/bash
set -e
# Constants
PLUGIN_REPO = "https://api.github.com/repos/motherduckdb/grafana-duckdb-datasource/releases/latest"
CURRENT_DIR = "$( cd "$( dirname "${ BASH_SOURCE [0]}")" && pwd )"
PROVISIONING_DIR = " $CURRENT_DIR /provisioning"
# Detect OS and set plugin directory
case "$( uname -s )" in
Darwin )
PLUGIN_DIR = " $CURRENT_DIR /plugins/motherduck-duckdb-datasource"
;;
Linux )
PLUGIN_DIR = " $CURRENT_DIR /plugins/motherduck-duckdb-datasource"
;;
CYGWIN * | MINGW * | MSYS * )
echo "⚠ Windows detected. Please manually unzip the plugin into your Grafana plugin folder."
exit 1
;;
*)
echo "❌ Unsupported OS. Please set PLUGIN_DIR manually."
exit 1
;;
esac
mkdir -p " $PLUGIN_DIR "
# Fetch latest release info
echo "📡 Fetching latest release info..."
ZIP_URL = $( curl -s $PLUGIN_REPO | grep browser_download_url | grep motherduck-duckdb-datasource | grep ".zip" | cut -d '"' -f4 | head -n 1 )
if [ -z " $ZIP_URL " ]; then
echo "❌ Failed to fetch zip URL."
exit 1
fi
# Download and extract plugin
ZIP_FILE = $( basename " $ZIP_URL " )
echo "⬇ Downloading plugin from $ZIP_URL ..."
curl -L -o " $ZIP_FILE " " $ZIP_URL "
unzip -o " $ZIP_FILE " -d " $PLUGIN_DIR "
rm -f " $ZIP_FILE "
echo "✅ Plugin extracted to $PLUGIN_DIR and zip removed"
# Check and stop existing Grafana container
CONTAINER_ID = $( docker ps --filter "publish=3000" --filter "name=grafana" --format "{{.ID}}" )
if [ -n " $CONTAINER_ID " ]; then
echo "🛑 Stopping Grafana Docker container running on port 3000..."
docker stop " $CONTAINER_ID "
docker rm " $CONTAINER_ID "
echo "✅ Stopped and removed Grafana container running on port 3000."
else
echo "ℹ No Grafana container running on port 3000."
fi
# Validate port 3000 availability
if lsof -Pi :3000 -sTCP:LISTEN -t > /dev/null ; then
echo "❌ Port 3000 is already in use. Please free up the port and try again."
exit 1
fi
# Validate environment variable
if ! printenv motherduck_token > /dev/null 2>&1 ; then
echo "❌ Required environment variable 'motherduck_token' is not set." >&2
exit 1
fi
MOTHERDUCK_TOKEN_VALUE = "$( printenv motherduck_token)"
# Start Grafana container
echo "🚀 Starting Grafana Docker container..."
docker run -d \
--name=grafana \
-p 3000:3000 \
-v " $PLUGIN_DIR :/var/lib/grafana/plugins/motherduck-duckdb-datasource" \
-v " $PROVISIONING_DIR :/etc/grafana/provisioning" \
-e "GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=motherduck-duckdb-datasource" \
-e "motherduck_token=${ MOTHERDUCK_TOKEN_VALUE }" \
grafana/grafana:latest-ubuntu
# Wait for container to start
for i in { 1..5} ; do
if docker ps --filter "name=grafana" --filter "status=running" | grep -q grafana ; then
echo "✅ Grafana Docker container is running."
break
fi
echo "⏳ Waiting for Grafana Docker container to start... ( $i /5)"
sleep 1
done
if ! docker ps --filter "name=grafana" --filter "status=running" | grep -q grafana ; then
echo "❌ Grafana Docker container failed to start after 5 seconds."
exit 1
fi
echo "🔗 Grafana is now running at http://localhost:3000"
Data Source Configuration
The setup script automatically configures the MotherDuck data source through provisioning. Here’s the configuration file:
apiVersion : 1
datasources :
- name : MotherDuck-Sample-Data
type : motherduck-duckdb-datasource
access : proxy
isDefault : true
editable : true
jsonData :
database : "" # Leave blank for MotherDuck
initSql : "ATTACH IF NOT EXISTS 'md:sample_data'" # you can have multiple initsql.
secureJsonData :
token : $__env{motherduck_token}
The initSql field allows you to automatically attach databases when the data source is initialized. You can add multiple databases by including additional ATTACH statements.
Creating Your First Dashboard
Navigate to dashboards
From the Grafana home page, click on the “Dashboards” menu item in the left sidebar.
Create new dashboard
Click the “New” button and select “New Dashboard”.
Add a visualization
Click “Add visualization” and select MotherDuck-DuckDB as your data source.
Configure query settings
Important: Set the format to time series at the top of the query builder before writing your query.
Switch from builder to code mode if you’d like to enter SQL manually.
Write your query
Enter your SQL query. For example: SELECT created_date, COUNT ( status )
FROM sample_data . nyc .service_requests
GROUP BY created_date LIMIT 1000
Save the dashboard
Configure your visualization settings and click “Apply” to add it to your dashboard.
Example Queries
Here are some example queries you can use with the sample data:
NYC Service Requests
Time Series Analysis
Status Distribution
SELECT created_date, COUNT ( status )
FROM sample_data . nyc .service_requests
GROUP BY created_date
LIMIT 1000
Sharing Dashboards
If you’ve created a dashboard you want to share with others:
Export the dashboard
Click the “Share” or “Export” button on your dashboard. Alternatively, go to dashboard settings and find the “JSON Model” section.
Save the JSON file
Copy the JSON and create a file under provisioning/dashboards/json/: provisioning/dashboards/json/my-dashboard.json
Commit and share
Commit the file to your repository: git add provisioning/dashboards/json/my-dashboard.json
git commit -m "Add my custom dashboard"
git push
Create a pull request
Open a pull request to share your dashboard with the team.
Troubleshooting
Port 3000 is already in use
The script checks for port availability. If port 3000 is in use:
Stop any existing services on port 3000
Or modify the script to use a different port:
If the plugin doesn’t appear in Grafana:
Check that the plugin was downloaded correctly
Verify the Docker volume mount is correct
Ensure unsigned plugins are allowed in the Docker command:
-e "GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=motherduck-duckdb-datasource"
Cannot connect to MotherDuck
If Grafana cannot connect to MotherDuck:
Verify your motherduck_token environment variable is set
Check that the token is valid and not expired
Look at the Grafana logs:
The pre-provisioned dashboards for NYC services and rideshare data are available in provisioning/dashboards/json/ and will automatically load when Grafana starts.