Set up TimescaleDB with PostgreSQL and configure Telegraf to collect system and application metrics. Create continuous aggregates and monitoring dashboards for comprehensive time-series analysis and alerting.
Prerequisites
- Root or sudo access
- 2GB RAM minimum
- 10GB available disk space
- Network access for package installation
What this solves
TimescaleDB extends PostgreSQL with time-series capabilities, making it ideal for storing metrics data from Telegraf. This combination gives you high-performance metric storage with SQL queries, continuous aggregates for real-time analytics, and automatic data retention policies. You'll get a complete monitoring stack that handles everything from system metrics to custom application data with enterprise-grade reliability.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you get the latest versions of all components.
sudo apt update && sudo apt upgrade -y
sudo apt install -y wget gnupg2 software-properties-common
Install PostgreSQL
TimescaleDB requires PostgreSQL as its foundation. Install the latest version with the official repository for better performance and security updates.
wget -qO- https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo gpg --dearmor -o /usr/share/keyrings/postgresql.gpg
echo "deb [signed-by=/usr/share/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | sudo tee /etc/apt/sources.list.d/postgresql.list
sudo apt update
sudo apt install -y postgresql-16 postgresql-client-16 postgresql-contrib-16
Start and enable PostgreSQL
Enable PostgreSQL to start on boot and verify it's running properly before adding TimescaleDB.
sudo systemctl enable --now postgresql
sudo systemctl status postgresql
Install TimescaleDB extension
Add the TimescaleDB repository and install the extension that provides time-series functionality to PostgreSQL.
echo "deb https://packagecloud.io/timescale/timescaledb/ubuntu/ $(lsb_release -c -s) main" | sudo tee /etc/apt/sources.list.d/timescaledb.list
wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/timescaledb.gpg
sudo apt update
sudo apt install -y timescaledb-2-postgresql-16
Configure PostgreSQL for TimescaleDB
Run the TimescaleDB tuning script to optimize PostgreSQL settings for time-series workloads and enable the extension.
sudo timescaledb-tune --quiet --yes
sudo systemctl restart postgresql
Create TimescaleDB database and user
Create a dedicated database for metrics and a user for Telegraf to connect with appropriate permissions.
sudo -u postgres psql -c "CREATE DATABASE telegraf;"
sudo -u postgres psql -c "CREATE USER telegraf WITH PASSWORD 'StrongTelegrafPassword123!';"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE telegraf TO telegraf;"
sudo -u postgres psql -d telegraf -c "CREATE EXTENSION IF NOT EXISTS timescaledb;"
sudo -u postgres psql -d telegraf -c "GRANT ALL ON SCHEMA public TO telegraf;"
Install Telegraf agent
Add the InfluxData repository and install Telegraf for collecting system and application metrics.
wget -q https://repos.influxdata.com/influxdata-archive_compat.key
echo '393e8779c89ac8d958f81f942f9ad7fb82a25e133faddaf92e15b16e6ac9ce4c influxdata-archive_compat.key' | sha256sum -c && cat influxdata-archive_compat.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg > /dev/null
echo 'deb [signed-by=/etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg] https://repos.influxdata.com/debian stable main' | sudo tee /etc/apt/sources.list.d/influxdata.list
sudo apt update && sudo apt install -y telegraf
Configure Telegraf for TimescaleDB output
Create a custom Telegraf configuration that sends metrics to your TimescaleDB instance with optimal batching and error handling.
# Global Agent Configuration
[agent]
interval = "10s"
round_interval = true
metric_batch_size = 1000
metric_buffer_limit = 10000
collection_jitter = "0s"
flush_interval = "10s"
flush_jitter = "0s"
precision = "0s"
debug = false
quiet = false
logfile = "/var/log/telegraf/telegraf.log"
hostname = ""
omit_hostname = false
PostgreSQL/TimescaleDB Output Plugin
[[outputs.postgresql]]
connection = "host=localhost user=telegraf password=StrongTelegrafPassword123! dbname=telegraf sslmode=disable"
create_templates = [
'''CREATE TABLE IF NOT EXISTS {{.table}} ({{.columns}})''',
'''SELECT create_hypertable('''{{.table}}''','''time''',if_not_exists=>true)'''
]
schema = "public"
tags_as_foreign_keys = false
tag_table_suffix = "_tag"
foreign_tag_constraint = false
System Metrics Input Plugin
[[inputs.cpu]]
percpu = true
totalcpu = true
collect_cpu_time = false
report_active = false
[[inputs.disk]]
ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"]
[[inputs.diskio]]
[[inputs.kernel]]
[[inputs.mem]]
[[inputs.processes]]
[[inputs.swap]]
[[inputs.system]]
[[inputs.net]]
[[inputs.netstat]]
Docker Metrics (optional)
[[inputs.docker]]
endpoint = "unix:///var/run/docker.sock"
gather_services = false
container_names = []
source_tag = false
container_name_include = []
container_name_exclude = []
timeout = "5s"
perdevice = true
total = false
docker_label_include = []
docker_label_exclude = []
NGINX Metrics (optional)
[[inputs.nginx]]
urls = ["http://localhost/nginx_status"]
response_timeout = "5s"
Set proper permissions and ownership
Configure file ownership and permissions for Telegraf to read the configuration and write logs securely.
sudo chown telegraf:telegraf /etc/telegraf/telegraf.conf
sudo chmod 640 /etc/telegraf/telegraf.conf
sudo mkdir -p /var/log/telegraf
sudo chown telegraf:telegraf /var/log/telegraf
sudo chmod 755 /var/log/telegraf
Start and enable Telegraf
Enable Telegraf to start on boot and begin collecting metrics immediately.
sudo systemctl enable --now telegraf
sudo systemctl status telegraf
Create continuous aggregates for monitoring
Set up continuous aggregates in TimescaleDB to automatically compute hourly and daily statistics for faster dashboard queries.
sudo -u postgres psql -d telegraf << 'EOF'
-- Create continuous aggregate for hourly CPU usage
CREATE MATERIALIZED VIEW cpu_usage_hourly
WITH (timescaledb.continuous) AS
SELECT time_bucket('1 hour', "time") AS bucket,
host,
cpu,
avg(usage_idle) as avg_idle,
avg(usage_user) as avg_user,
avg(usage_system) as avg_system
FROM cpu
GROUP BY bucket, host, cpu;
-- Create continuous aggregate for hourly memory usage
CREATE MATERIALIZED VIEW mem_usage_hourly
WITH (timescaledb.continuous) AS
SELECT time_bucket('1 hour', "time") AS bucket,
host,
avg(used_percent) as avg_used_percent,
avg(available_percent) as avg_available_percent
FROM mem
GROUP BY bucket, host;
-- Create continuous aggregate for disk I/O statistics
CREATE MATERIALIZED VIEW disk_io_hourly
WITH (timescaledb.continuous) AS
SELECT time_bucket('1 hour', "time") AS bucket,
host,
name,
avg(read_bytes) as avg_read_bytes,
avg(write_bytes) as avg_write_bytes,
avg(iops_in_progress) as avg_iops
FROM diskio
GROUP BY bucket, host, name;
EOF
Set up refresh policies
Configure automatic refresh policies for continuous aggregates to keep them updated with fresh data.
sudo -u postgres psql -d telegraf << 'EOF'
-- Add refresh policies for continuous aggregates
SELECT add_continuous_aggregate_policy('cpu_usage_hourly',
start_offset => INTERVAL '3 hours',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '1 hour');
SELECT add_continuous_aggregate_policy('mem_usage_hourly',
start_offset => INTERVAL '3 hours',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '1 hour');
SELECT add_continuous_aggregate_policy('disk_io_hourly',
start_offset => INTERVAL '3 hours',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '1 hour');
EOF
Configure data retention policies
Set up automatic data retention to manage storage space by removing old raw metrics while keeping aggregated data longer.
sudo -u postgres psql -d telegraf << 'EOF'
-- Set retention policy for raw CPU data (30 days)
SELECT add_retention_policy('cpu', INTERVAL '30 days');
-- Set retention policy for raw memory data (30 days)
SELECT add_retention_policy('mem', INTERVAL '30 days');
-- Set retention policy for raw disk I/O data (30 days)
SELECT add_retention_policy('diskio', INTERVAL '30 days');
-- Set retention policy for raw system data (30 days)
SELECT add_retention_policy('system', INTERVAL '30 days');
-- Set retention policy for raw network data (30 days)
SELECT add_retention_policy('net', INTERVAL '30 days');
EOF
Verify your setup
Check that both TimescaleDB and Telegraf are running correctly and collecting metrics.
# Check Telegraf status
sudo systemctl status telegraf
Check PostgreSQL status
sudo systemctl status postgresql
Verify metrics are being collected
sudo -u postgres psql -d telegraf -c "SELECT COUNT(*) FROM cpu;"
sudo -u postgres psql -d telegraf -c "SELECT COUNT(*) FROM mem;"
Check continuous aggregates
sudo -u postgres psql -d telegraf -c "SELECT * FROM cpu_usage_hourly ORDER BY bucket DESC LIMIT 5;"
View available hypertables
sudo -u postgres psql -d telegraf -c "SELECT hypertable_name, num_chunks FROM timescaledb_information.hypertables;"
Check Telegraf logs
sudo tail -f /var/log/telegraf/telegraf.log
Create basic monitoring queries
Use these SQL queries to analyze your metrics data and create custom monitoring dashboards.
CPU utilization analysis
Query average CPU usage over time with breakdown by core and host.
-- Average CPU usage by host over last 24 hours
SELECT
time_bucket('1 hour', "time") AS hour,
host,
ROUND(100 - AVG(usage_idle), 2) as cpu_usage_percent
FROM cpu
WHERE "time" > NOW() - INTERVAL '24 hours'
AND cpu = 'cpu-total'
GROUP BY hour, host
ORDER BY hour DESC;
Memory usage trends
Monitor memory consumption patterns and identify potential issues.
-- Memory usage trends over last week
SELECT
time_bucket('6 hours', "time") AS period,
host,
ROUND(AVG(used_percent), 2) as avg_memory_used,
ROUND(MAX(used_percent), 2) as max_memory_used
FROM mem
WHERE "time" > NOW() - INTERVAL '7 days'
GROUP BY period, host
ORDER BY period DESC;
Disk I/O performance
Analyze disk performance and identify I/O bottlenecks across different storage devices.
-- Top disk I/O by device over last 4 hours
SELECT
time_bucket('30 minutes', "time") AS period,
host,
name as device,
ROUND(AVG(read_bytes)/1024/1024, 2) as avg_read_mb,
ROUND(AVG(write_bytes)/1024/1024, 2) as avg_write_mb
FROM diskio
WHERE "time" > NOW() - INTERVAL '4 hours'
GROUP BY period, host, name
ORDER BY period DESC, (AVG(read_bytes) + AVG(write_bytes)) DESC;
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Telegraf connection refused | PostgreSQL not accepting connections | Check sudo systemctl status postgresql and /etc/postgresql/16/main/pg_hba.conf |
| Permission denied for database | Incorrect user privileges | Grant privileges: sudo -u postgres psql -c "GRANT ALL ON SCHEMA public TO telegraf;" |
| TimescaleDB extension not found | Extension not enabled | Run: sudo -u postgres psql -d telegraf -c "CREATE EXTENSION timescaledb;" |
| No metrics in database | Telegraf configuration error | Check logs: sudo journalctl -u telegraf -f and verify config syntax |
| High memory usage | Too frequent metric collection | Increase interval in /etc/telegraf/telegraf.conf agent section |
| Continuous aggregates not updating | Refresh policy not working | Manual refresh: CALL refresh_continuous_aggregate('cpu_usage_hourly', NULL, NULL); |
Next steps
- Configure TimescaleDB automated data retention policies for efficient storage management
- Set up Grafana dashboards with TimescaleDB for advanced visualization
- Configure Telegraf custom metrics and input plugins for application monitoring
- Implement TimescaleDB alerting policies with automated notifications
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Global variables
TELEGRAF_DB_NAME="telegraf"
TELEGRAF_USER="telegraf"
TELEGRAF_PASS=""
# Usage message
usage() {
echo "Usage: $0 [--telegraf-password PASSWORD]"
echo " --telegraf-password: Password for telegraf database user (auto-generated if not provided)"
exit 1
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--telegraf-password)
TELEGRAF_PASS="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo -e "${RED}Unknown option: $1${NC}"
usage
;;
esac
done
# Generate password if not provided
if [[ -z "$TELEGRAF_PASS" ]]; then
TELEGRAF_PASS=$(openssl rand -base64 16 2>/dev/null || tr -dc A-Za-z0-9 </dev/urandom | head -c 16)
fi
# Check prerequisites
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root${NC}"
exit 1
fi
# Auto-detect distribution
if [[ ! -f /etc/os-release ]]; then
echo -e "${RED}/etc/os-release not found. Cannot detect distribution.${NC}"
exit 1
fi
source /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
PG_SERVICE="postgresql"
PG_USER="postgres"
PG_CONFIG_DIR="/etc/postgresql/16/main"
PG_DATA_DIR="/var/lib/postgresql/16/main"
TELEGRAF_CONFIG="/etc/telegraf/telegraf.conf"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
PG_SERVICE="postgresql-16"
PG_USER="postgres"
PG_CONFIG_DIR="/var/lib/pgsql/16/data"
PG_DATA_DIR="/var/lib/pgsql/16/data"
TELEGRAF_CONFIG="/etc/telegraf/telegraf.conf"
;;
fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
PG_SERVICE="postgresql"
PG_USER="postgres"
PG_CONFIG_DIR="/var/lib/pgsql/data"
PG_DATA_DIR="/var/lib/pgsql/data"
TELEGRAF_CONFIG="/etc/telegraf/telegraf.conf"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
PG_SERVICE="postgresql-16"
PG_USER="postgres"
PG_CONFIG_DIR="/var/lib/pgsql/16/data"
PG_DATA_DIR="/var/lib/pgsql/16/data"
TELEGRAF_CONFIG="/etc/telegraf/telegraf.conf"
;;
*)
echo -e "${RED}Unsupported distribution: $ID${NC}"
exit 1
;;
esac
# Cleanup function
cleanup() {
echo -e "${RED}Installation failed. Cleaning up...${NC}"
systemctl stop $PG_SERVICE 2>/dev/null || true
systemctl stop telegraf 2>/dev/null || true
}
trap cleanup ERR
echo -e "${GREEN}Starting TimescaleDB + Telegraf installation...${NC}"
echo -e "${YELLOW}[1/8] Updating system packages...${NC}"
$PKG_UPDATE
$PKG_INSTALL wget gnupg2 software-properties-common curl openssl
echo -e "${YELLOW}[2/8] Installing PostgreSQL 16...${NC}"
if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
wget -qO- https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /usr/share/keyrings/postgresql.gpg
echo "deb [signed-by=/usr/share/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/postgresql.list
$PKG_UPDATE
$PKG_INSTALL postgresql-16 postgresql-client-16 postgresql-contrib-16
else
$PKG_INSTALL https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
$PKG_INSTALL postgresql16-server postgresql16 postgresql16-contrib
/usr/pgsql-16/bin/postgresql-16-setup initdb
fi
echo -e "${YELLOW}[3/8] Starting PostgreSQL service...${NC}"
systemctl enable --now $PG_SERVICE
sleep 3
echo -e "${YELLOW}[4/8] Installing TimescaleDB extension...${NC}"
if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
echo "deb https://packagecloud.io/timescale/timescaledb/ubuntu/ $(lsb_release -c -s) main" > /etc/apt/sources.list.d/timescaledb.list
wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | gpg --dearmor -o /etc/apt/trusted.gpg.d/timescaledb.gpg
$PKG_UPDATE
$PKG_INSTALL timescaledb-2-postgresql-16
else
tee /etc/yum.repos.d/timescale_timescaledb.repo > /dev/null <<EOF
[timescale_timescaledb]
name=timescale_timescaledb
baseurl=https://packagecloud.io/timescale/timescaledb/el/9/\$basearch
repo_gpgcheck=1
gpgcheck=0
enabled=1
gpgkey=https://packagecloud.io/timescale/timescaledb/gpgkey
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
metadata_expire=300
EOF
$PKG_INSTALL timescaledb-2-postgresql-16
fi
echo -e "${YELLOW}[5/8] Configuring TimescaleDB...${NC}"
sudo -u $PG_USER timescaledb-tune --quiet --yes
systemctl restart $PG_SERVICE
sleep 3
echo -e "${YELLOW}[6/8] Setting up database and user...${NC}"
sudo -u $PG_USER psql -c "CREATE DATABASE $TELEGRAF_DB_NAME;" || true
sudo -u $PG_USER psql -d $TELEGRAF_DB_NAME -c "CREATE EXTENSION IF NOT EXISTS timescaledb;"
sudo -u $PG_USER psql -c "CREATE USER $TELEGRAF_USER WITH ENCRYPTED PASSWORD '$TELEGRAF_PASS';" || true
sudo -u $PG_USER psql -c "GRANT ALL PRIVILEGES ON DATABASE $TELEGRAF_DB_NAME TO $TELEGRAF_USER;"
sudo -u $PG_USER psql -d $TELEGRAF_DB_NAME -c "GRANT ALL ON SCHEMA public TO $TELEGRAF_USER;"
echo -e "${YELLOW}[7/8] Installing and configuring Telegraf...${NC}"
if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
wget -q https://repos.influxdata.com/influxdata-archive_compat.key
gpg --dearmor < influxdata-archive_compat.key | tee /etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg > /dev/null
echo 'deb [signed-by=/etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg] https://repos.influxdata.com/debian stable main' > /etc/apt/sources.list.d/influxdata.list
$PKG_UPDATE
$PKG_INSTALL telegraf
rm -f influxdata-archive_compat.key
else
cat > /etc/yum.repos.d/influxdata.repo <<EOF
[influxdata]
name = InfluxData Repository - RHEL
baseurl = https://repos.influxdata.com/rhel/\$releasever/\$basearch/stable/
enabled = 1
gpgcheck = 1
gpgkey = https://repos.influxdata.com/influxdata-archive_compat.key
EOF
$PKG_INSTALL telegraf
fi
# Configure Telegraf
cat > $TELEGRAF_CONFIG <<EOF
[global_tags]
[agent]
interval = "10s"
round_interval = true
metric_batch_size = 1000
metric_buffer_limit = 10000
collection_jitter = "0s"
flush_interval = "10s"
flush_jitter = "0s"
precision = ""
hostname = ""
omit_hostname = false
[[outputs.postgresql]]
connection = "host=localhost user=$TELEGRAF_USER password=$TELEGRAF_PASS dbname=$TELEGRAF_DB_NAME sslmode=disable"
create_templates = [
'''CREATE TABLE {{ .table }} ({{ .columns }})''',
'''SELECT create_hypertable('{{ .table }}', 'time', if_not_exists => true)''',
]
[[inputs.cpu]]
percpu = true
totalcpu = true
collect_cpu_time = false
report_active = false
[[inputs.disk]]
ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"]
[[inputs.diskio]]
[[inputs.kernel]]
[[inputs.mem]]
[[inputs.processes]]
[[inputs.swap]]
[[inputs.system]]
EOF
chown root:telegraf $TELEGRAF_CONFIG
chmod 640 $TELEGRAF_CONFIG
systemctl enable --now telegraf
echo -e "${YELLOW}[8/8] Verifying installation...${NC}"
sleep 5
# Test database connection
sudo -u $PG_USER psql -d $TELEGRAF_DB_NAME -c "SELECT version();" > /dev/null
echo -e "${GREEN}✓ PostgreSQL connection successful${NC}"
sudo -u $PG_USER psql -d $TELEGRAF_DB_NAME -c "SELECT default_version, installed_version FROM pg_available_extensions WHERE name='timescaledb';" > /dev/null
echo -e "${GREEN}✓ TimescaleDB extension loaded${NC}"
systemctl is-active --quiet telegraf
echo -e "${GREEN}✓ Telegraf service running${NC}"
# Wait for initial metrics
sleep 10
TABLE_COUNT=$(sudo -u $PG_USER psql -d $TELEGRAF_DB_NAME -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public' AND table_name IN ('cpu', 'mem', 'disk');" | xargs)
if [[ $TABLE_COUNT -gt 0 ]]; then
echo -e "${GREEN}✓ Metrics tables created${NC}"
else
echo -e "${YELLOW}⚠ Metrics tables not yet created (may take a few minutes)${NC}"
fi
echo -e "${GREEN}Installation completed successfully!${NC}"
echo ""
echo -e "${YELLOW}Connection details:${NC}"
echo "Database: $TELEGRAF_DB_NAME"
echo "User: $TELEGRAF_USER"
echo "Password: $TELEGRAF_PASS"
echo ""
echo -e "${YELLOW}Connect to database:${NC}"
echo "sudo -u postgres psql -d $TELEGRAF_DB_NAME"
echo ""
echo -e "${YELLOW}View metrics:${NC}"
echo "SELECT * FROM cpu LIMIT 5;"
Review the script before running. Execute with: bash install.sh