This guide provides a comprehensive, step-by-step process for integrating Tinybird analytics with a Ghost blog installed via Ghost-CLI on Ubuntu. While Ghost's official Tinybird integration is currently only available for Docker installations, this guide creates a custom solution that achieves the same functionality for Ghost-CLI installations.
Ghost CMS offers built-in analytics for membership and newsletter metrics, but lacks native website traffic analytics for Ghost-CLI installations. Tinybird is a real-time analytics platform that Ghost has partnered with for Docker deployments.
This guide bridges that gap for Ghost-CLI users by creating a custom proxy service that forwards analytics data from your Ghost blog to Tinybird.
What This Integration Provides
- Privacy-friendly analytics: No third-party cookies
- Real-time data collection: Instant pageview tracking
- Full data ownership: All data stored in your Tinybird workspace
- SQL access: Query your analytics data with SQL
- Scalable solution: Handles high-traffic websites efficiently
Prerequisites
On Your Ubuntu Server
- Ghost installed via Ghost-CLI (not Docker)
- Node.js and npm installed
- Nginx as reverse proxy
- Root or sudo access
- Ghost running on the default port 2368
On Your Local Machine
- Web browser for Tinybird setup
- SSH client (Terminal, PowerShell, or PuTTY)
- Text editor for saving credentials
Part 1: Set Up Tinybird Account [Local Machine - Browser]
Step 1: Create Tinybird Account
- Navigate to https://www.tinybird.co
- Sign up for a free account
- Verify your email address
Step 2: Create Workspace
- When prompted to create a workspace, choose a name (e.g., "ghost_analytics")
- Important: Select "Tinybird Classic" (not Tinybird Forward)
- Choose your region (note this for later - e.g., "us-east-1")
- For "day-to-day work", select any option (e.g., "Data Engineering")
- Click "Start"
Step 3: Create the Analytics Data Source
- In the left sidebar, click "Data Sources"
- Click "Create Data Source" or "+"
- Choose "Write schema"
- Name it exactly:
analytics_events
- In the code editor, paste:
sql
SCHEMA >
`timestamp` DateTime `json:$.timestamp`,
`event` String `json:$.event`,
`url` String `json:$.url`,
`referrer` String `json:$.referrer`,
`user_agent` String `json:$.user_agent`,
`title` String `json:$.title`,
`path` String `json:$.path`,
`target` Nullable(String) `json:$.target`,
`text` Nullable(String) `json:$.text`,
`duration` Nullable(Int32) `json:$.duration`
ENGINE "MergeTree"
ENGINE_SORTING_KEY "timestamp"
ENGINE_TTL ""
ENGINE_PARTITION_KEY ""
- Click "Next" then "Create Data Source"
Step 4: Gather Credentials
- Get API URL:
- Based on your region selection:
- US East (us-east-1):
https://api.us-east.aws.tinybird.co
- EU:
https://api.eu.tinybird.co
- Default:
https://api.tinybird.co
- Get Tokens:
- Go to "Tokens" in the left sidebar
- Copy the existing admin token
- Click "Create Token"
- Name it "Ghost Tracker Token"
- Add Data Source scope for
analytics_events
with "Append" permission - Copy this token
- Get Workspace ID:
- Click "Settings" in the left sidebar
- Copy the Workspace ID
Save these four values in a text file:
API URL: https://api.us-east.aws.tinybird.co (your actual URL)
Admin Token: p.ey... (your admin token)
Tracker Token: p.ey... (your tracker token)
Workspace ID: ws_... (your workspace ID)
Part 2: Configure Ghost Server [Ubuntu Server - SSH]
Step 1: Connect to Your Server
bash
ssh root@YOUR_SERVER_IP
Step 2: Configure Ghost with Tinybird
Navigate to your Ghost installation:
bash
cd /var/www/ghost
# Or: cd /var/www/yourdomain.com
Add Tinybird configuration (replace with your actual values):
bash
ghost config set analytics:tinybird:enabled true
ghost config set analytics:tinybird:apiUrl "YOUR_API_URL"
ghost config set analytics:tinybird:adminToken "YOUR_ADMIN_TOKEN"
ghost config set analytics:tinybird:workspaceId "YOUR_WORKSPACE_ID"
ghost config set analytics:tinybird:trackerToken "YOUR_TRACKER_TOKEN"
ghost restart
Part 3: Create Analytics Proxy Service [Ubuntu Server - SSH]
Step 1: Set Up Project Directory
bash
sudo mkdir -p /var/www/ghost-analytics
cd /var/www/ghost-analytics
Step 2: Create Package Configuration
bash
sudo nano package.json
Paste exactly:
json
{
"name": "ghost-analytics-proxy",
"version": "1.0.0",
"main": "server.js",
"dependencies": {
"express": "^4.18.0",
"http-proxy-middleware": "^2.0.0",
"node-fetch": "^3.3.0"
}
}
Save with Ctrl+X, Y, Enter.
Step 3: Install Dependencies
bash
sudo npm install
Step 4: Create the Proxy Server
bash
sudo nano server.js
Paste:
javascript
const express = require('express');
const app = express();
const TINYBIRD_API_URL = process.env.TINYBIRD_API_URL;
const TINYBIRD_TRACKER_TOKEN = process.env.TINYBIRD_TRACKER_TOKEN;
const PORT = process.env.PORT || 3001;
app.use(express.json());
// Health check
app.get('/health', (req, res) => {
res.status(200).json({ status: 'healthy' });
});
// Proxy analytics to Tinybird
app.post('/analytics', async (req, res) => {
try {
const fetch = (await import('node-fetch')).default;
const url = `${TINYBIRD_API_URL}/v0/events?name=analytics_events`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${TINYBIRD_TRACKER_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(req.body)
});
const text = await response.text();
console.log('Tinybird response:', response.status);
res.status(200).json({ success: true });
} catch (error) {
console.error('Proxy error:', error);
res.status(200).json({ success: true });
}
});
app.listen(PORT, '127.0.0.1', () => {
console.log(`Analytics proxy running on port ${PORT}`);
});
Save the file.
Step 5: Create Systemd Service
bash
sudo nano /etc/systemd/system/ghost-analytics.service
Paste (replace YOUR_API_URL and YOUR_TRACKER_TOKEN with actual values):
ini
[Unit]
Description=Ghost Analytics Proxy for Tinybird
After=network.target
[Service]
Type=simple
WorkingDirectory=/var/www/ghost-analytics
Environment="NODE_ENV=production"
Environment="PORT=3001"
Environment="TINYBIRD_API_URL=YOUR_API_URL"
Environment="TINYBIRD_TRACKER_TOKEN=YOUR_TRACKER_TOKEN"
ExecStart=/usr/bin/node server.js
Restart=always
User=ghost
Group=ghost
[Install]
WantedBy=multi-user.target
Save the file.
Step 6: Start the Service
bash
sudo chown -R ghost:ghost /var/www/ghost-analytics
sudo systemctl daemon-reload
sudo systemctl enable ghost-analytics
sudo systemctl start ghost-analytics
sudo systemctl status ghost-analytics
You should see "Active: active (running)".
Part 4: Configure Nginx [Ubuntu Server - SSH]
Step 1: Edit Nginx Configuration
Find your site's SSL configuration:
bash
ls /etc/nginx/sites-available/
sudo nano /etc/nginx/sites-available/YOUR_DOMAIN-ssl.conf
Step 2: Add Analytics Location Block
Inside the server
block (after the location /
block), add:
nginx
location /analytics {
proxy_pass http://127.0.0.1:3001/analytics;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Step 3: Test and Reload
bash
sudo nginx -t
sudo systemctl reload nginx
Part 5: Add Tracking Script to Ghost Theme [Ubuntu Server - SSH]
Step 1: Navigate to Your Theme
bash
cd /var/www/ghost/content/themes/YOUR_THEME_NAME
sudo nano default.hbs
Step 2: Add Tracking Script
Find the </body>
tag and add this directly before it:
html
<script>
(function() {
function sendEvent(eventType, properties) {
const event = {
timestamp: new Date().toISOString(),
event: eventType,
url: window.location.href,
referrer: document.referrer,
user_agent: navigator.userAgent,
...properties
};
fetch('/analytics', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(event)
}).catch(err => console.error('Analytics error:', err));
}
sendEvent('pageview', {
title: document.title,
path: window.location.pathname
});
})();
</script>
</body>
Save the file.
Step 3: Restart Ghost
bash
cd /var/www/ghost
ghost restart
Part 6: Verification and Testing
Step 1: Test the Analytics Endpoint
From your server:
bash
curl -X POST https://YOUR_DOMAIN/analytics \
-H "Content-Type: application/json" \
-d '{"event": "test", "timestamp": "'$(date -Iseconds)'"}'
Step 2: Check Browser Network Tab
- Visit your blog
- Open Developer Tools (F12)
- Go to Network tab
- Filter by "Fetch/XHR"
- Look for POST requests to
/analytics
- should return 200 status
Step 3: Verify Data in Tinybird
- Go to Tinybird dashboard
- Click "Data Sources"
- Click on
analytics_events
- You should see incoming events
Troubleshooting Guide
Issue: Wrong API Region Error
Symptom: 404 error with message about wrong region
Solution: Update API URL in systemd service:
bash
sudo nano /etc/systemd/system/ghost-analytics.service
# Update TINYBIRD_API_URL with correct regional URL
sudo systemctl daemon-reload
sudo systemctl restart ghost-analytics
Issue: No Analytics Requests in Browser
Symptom: No /analytics
requests in Network tab
Solution: Verify tracking script is present:
bash
curl -s https://YOUR_DOMAIN | grep -A 5 "sendEvent"
Issue: 404 Error on Analytics Endpoint
Symptom: /analytics
returns 404
Solution: Check Nginx configuration:
bash
grep -A 5 "location /analytics" /etc/nginx/sites-available/*-ssl.conf
sudo nginx -t
sudo systemctl reload nginx
Issue: Service Not Starting
Symptom: ghost-analytics service fails
Solution: Check logs and permissions:
bash
sudo journalctl -u ghost-analytics -n 50
sudo chown -R ghost:ghost /var/www/ghost-analytics
Maintenance
View Service Status
bash
sudo systemctl status ghost-analytics
View Service Logs
bash
sudo journalctl -u ghost-analytics -f
Restart Service After Changes
bash
sudo systemctl restart ghost-analytics
Update Node Dependencies
bash
cd /var/www/ghost-analytics
sudo npm update
sudo systemctl restart ghost-analytics
Viewing Your Analytics
Analytics data is accessible through Tinybird's interface:
- Basic Queries: Use Tinybird's SQL interface
- Create Pipes: Build reusable queries and API endpoints
- Export Data: Download or stream to other services
- Build Dashboards: Use Tinybird's visualization tools or connect to external BI tools
Example query for daily pageviews:
sql
SELECT
toDate(timestamp) as date,
count() as pageviews,
count(DISTINCT user_agent) as unique_visitors
FROM analytics_events
WHERE event = 'pageview'
GROUP BY date
ORDER BY date DESC
Important Notes
- Not Official Integration: This is a custom solution for Ghost-CLI installations
- Data Location: Analytics data is viewed in Tinybird, not Ghost Admin
- Privacy Compliant: No third-party cookies are used
- Scalable: Tinybird handles millions of events efficiently
- Cost: Tinybird's free tier includes 10GB storage and 1000 API requests/day
Conclusion
You now have a complete, privacy-friendly analytics system for your Ghost blog. While the data doesn't appear in Ghost Admin (that's only for Docker installations), you have full access to your analytics through Tinybird's powerful interface with SQL querying capabilities.
Member discussion