How to Install TimescaleDB on Ubuntu 24.04 — Self-Hosted Time-Series Database on Your VPS
Time-series workloads break ordinary relational databases. When your application ingests millions of sensor readings, financial ticks, application metrics, or IoT events per day, a plain PostgreSQL table quickly hits index bloat, vacuum churn, and query plans that degrade as the table grows. TimescaleDB solves this by adding automatic time-based partitioning, columnar compression, continuous aggregates, and retention policies on top of PostgreSQL — all while keeping full SQL compatibility and the entire PostgreSQL ecosystem (extensions, clients, ORMs, backup tools).
This guide walks you through installing TimescaleDB on an Ubuntu 24.04 LTS VPS, starting from a fresh SSH connection and finishing with a production-ready instance running hypertables, continuous aggregates, compression, retention policies, and automated backups.
Self-host and keep control. Deploy TimescaleDB on a CloudCore Professional VPS for EUR 19.99 per month — unmetered bandwidth, 100 GB NVMe, and enough headroom to process millions of rows per day.
Table of Contents
What is TimescaleDB?
TimescaleDB is an open-source PostgreSQL extension purpose-built for time-series data. Instead of replacing PostgreSQL, it installs as an extension and adds a new object type called a hypertable that looks and behaves like a regular SQL table but is transparently partitioned into chunks by time. Writes and reads use ordinary SQL — INSERT, SELECT, JOIN, GROUP BY — so your existing PostgreSQL drivers, ORMs, migration tools, and GUIs continue to work unchanged.
On top of hypertables, TimescaleDB layers four features that matter at scale:
- Continuous aggregates — materialized views that stay incrementally up to date as new data arrives, so dashboards that scan days or months of data run in milliseconds instead of minutes.
- Columnar compression — per-chunk conversion from row-oriented storage to a compressed columnar format, typically shrinking storage by 90-95 percent on metric-style workloads.
- Retention policies — automated jobs that drop chunks older than a threshold, replacing complicated
DELETEjobs that would otherwise generate huge amounts of WAL and vacuum work. - Job scheduler — a built-in background worker that runs the policies above plus any user-defined procedures on a cron-like schedule, directly inside PostgreSQL.
Why Self-Host TimescaleDB Instead of Using Timescale Cloud?
Timescale offers a managed service called Timescale Cloud that is pleasant to use but expensive once your data volume is real. A self-hosted install on a VPS you control has several concrete advantages:
- Flat, predictable cost. A CloudCore Professional VPS at EUR 19.99 per month delivers 6 vCPU, 12 GB RAM, 100 GB NVMe SSD, and unmetered bandwidth. A comparable Timescale Cloud instance (4 CPU / 16 GB / 100 GB storage) starts around USD 200-350 per month once storage, backups, and egress are included.
- No per-GB storage surcharges. Cloud vendors bill for stored bytes even after compression. With a self-hosted VPS, compressed chunks sit on your included NVMe and cost you nothing beyond the flat plan fee.
- No egress fees. Pulling metrics into Grafana, exporting datasets to a data lake, or feeding an ML pipeline does not trigger per-GB outbound bandwidth charges.
- Full control of extensions and versions. Install PostGIS, pgvector, or any other PostgreSQL extension alongside TimescaleDB without waiting for the managed service to support it.
- Data residency and compliance. You decide exactly where the server runs. For GDPR, HIPAA, or internal policies that require EU-only data handling, pinning your VPS to a specific region is straightforward.
- Co-location with your application. Running TimescaleDB on the same VPS (or the same internal network) as your application eliminates the 20-100 ms round trip inherent in cross-provider managed databases.
Cost Comparison: Self-Hosted vs. Timescale Cloud
| Scenario | Timescale Cloud | AWS Timestream | Self-Hosted on CloudCore Professional |
|---|---|---|---|
| Monthly base cost | USD 150-350+ | Pay-per-query + storage | EUR 19.99 (flat) |
| Storage | GB-metered, billed | GB-metered, billed | 100 GB NVMe included |
| Egress | Metered | Metered | Unmetered |
| Backups | Extra GB charges | Included (limited) | Your own pgBackRest, free |
| Extensions (PostGIS, pgvector) | Limited | Not supported | Any PostgreSQL extension |
| Multi-tenant isolation | Per-service billing | N/A | All tenants on one server |
| Typical cost at 100 GB + 10K inserts/sec | ~USD 400/mo | ~USD 500+/mo | EUR 19.99/mo |
Prerequisites
Before starting, make sure you have:
- A VPS running Ubuntu 24.04 LTS (noble) with root or sudo access
- SSH access to the server
- At least 4 GB of RAM for small workloads, 8-12 GB recommended for production
- At least 20 GB of free disk space (SSD/NVMe strongly preferred — time-series workloads are I/O-heavy)
Recommended Plan: CloudCore Professional>
For a production TimescaleDB instance handling millions of rows per day, we recommend the CloudCore Professional plan:>
- 6 vCPU cores
- 12 GB RAM
- 100 GB NVMe SSD
- Unmetered bandwidth
- EUR 19.99 / month>
This gives you enough RAM for PostgreSQL shared buffers, the TimescaleDB chunk cache, and room for Grafana or a metrics collector on the same host.
Connect to your server:
ssh root@your-server-ipStep 1: Update System Packages
Refresh the package index and apply any pending security updates:
sudo apt update && sudo apt upgrade -yIf the kernel was updated, reboot before proceeding:
sudo rebootReconnect once the server is back up, then install utilities we will need for the rest of the install:
sudo apt install -y curl gnupg2 lsb-release ca-certificates wget apt-transport-httpsStep 2: Install PostgreSQL 16
TimescaleDB 2.x supports PostgreSQL 15, 16, and 17. We will use PostgreSQL 16 because it is the most widely tested with the current TimescaleDB release and still receives upstream patches through 2028. If you already run a PostgreSQL instance, you can skip to Step 3.
Add the official PostgreSQL Global Development Group (PGDG) APT repository:
sudo install -d /usr/share/postgresql-common/pgdg sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \ --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \ https://apt.postgresql.org/pub/repos/apt noble-pgdg main" | \ sudo tee /etc/apt/sources.list.d/pgdg.list
Update the index and install PostgreSQL 16:
sudo apt update
sudo apt install -y postgresql-16 postgresql-client-16 postgresql-contrib-16Verify the service is running:
sudo systemctl status postgresqlExpected output (abbreviated):
● postgresql.service - PostgreSQL RDBMS
Loaded: loaded (/lib/systemd/system/postgresql.service; enabled; preset: enabled)
Active: active (exited) since Thu 2026-04-16 10:05:00 UTC; 30s agoCheck the server version:
sudo -u postgres psql -c "SELECT version();"Expected output:
PostgreSQL 16.8 on x86_64-pc-linux-gnu, compiled by gcc...Step 3: Add the TimescaleDB APT Repository
Timescale publishes packages through packagecloud. Import the signing key and add the repository:
curl -fsSL https://packagecloud.io/timescale/timescaledb/gpgkey | \ sudo gpg --dearmor -o /usr/share/keyrings/timescaledb.gpg
echo "deb [signed-by=/usr/share/keyrings/timescaledb.gpg] \ https://packagecloud.io/timescale/timescaledb/ubuntu/ $(lsb_release -cs) main" | \ sudo tee /etc/apt/sources.list.d/timescaledb.list
Refresh the index:
sudo apt updateYou should see the new repository hit successfully. If apt update complains about a missing key, re-run the curl ... gpgkey command above.
Step 4: Install the TimescaleDB Extension
Install the TimescaleDB package matching your PostgreSQL version:
sudo apt install -y timescaledb-2-postgresql-16This installs the extension shared libraries under /usr/lib/postgresql/16/lib/ and the SQL definitions under /usr/share/postgresql/16/extension/.
Verify the files are present:
ls /usr/lib/postgresql/16/lib/timescaledb* 2>/dev/null | headExpected output:
/usr/lib/postgresql/16/lib/timescaledb-2.17.2.so
/usr/lib/postgresql/16/lib/timescaledb-tsl-2.17.2.so
/usr/lib/postgresql/16/lib/timescaledb.soStep 5: Run timescaledb-tune
TimescaleDB ships with a small Go program called timescaledb-tune that rewrites postgresql.conf with values appropriate for time-series workloads on your specific hardware (shared_buffers, work_mem, effective_cache_size, autovacuum settings, WAL tuning, etc.). Running it is the single biggest performance win you will get from this install.
Run it:
sudo timescaledb-tune --pg-config=/usr/bin/pg_configThe tuner detects your CPU count and RAM, then asks a few questions. Accept the defaults unless you have a specific reason otherwise:
Using postgresql.conf at this path: /etc/postgresql/16/main/postgresql.confIs this correct? [(y)es/(n)o]: y
shared_preload_libraries needs to be updated Current: #shared_preload_libraries = '' Recommended: shared_preload_libraries = 'timescaledb' Is this okay? [(y)es/(n)o]: y
Tuning based on 6 CPUs and 12.00 GB of memory... Recommendations: shared_buffers = 3GB effective_cache_size = 9GB maintenance_work_mem = 1023MB work_mem = 10485kB timescaledb.max_background_workers = 8 max_worker_processes = 19 max_parallel_workers_per_gather = 3 max_parallel_workers = 6 wal_buffers = 16MB min_wal_size = 512MB default_statistics_target = 500 random_page_cost = 1.1 checkpoint_completion_target = 0.9 max_locks_per_transaction = 64 autovacuum_max_workers = 10 autovacuum_naptime = 10 effective_io_concurrency = 256 Is this okay? [(y)es/(s)kip/(q)uit]: y
Restart PostgreSQL to apply the changes:
sudo systemctl restart postgresqlConfirm TimescaleDB is loaded:
sudo -u postgres psql -c "SHOW shared_preload_libraries;"Expected output:
shared_preload_libraries
--------------------------
timescaledb
(1 row)Step 6: Enable the Extension in a Database
Create a database for your time-series workload and enable the extension inside it:
sudo -u postgres createdb metrics
sudo -u postgres psql -d metrics -c "CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;"Expected output:
WARNING:
Welcome to TimescaleDB 2.17.2!
...
CREATE EXTENSIONThe CASCADE keyword also installs required dependencies such as the postgres_fdw foreign data wrapper if not already present.
Verify the extension version and configured workers:
sudo -u postgres psql -d metrics -c "\dx"Expected output:
List of installed extensions
Name | Version | Schema | Description
-------------+---------+------------+--------------------------------------------------------------------
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
timescaledb | 2.17.2 | public | Enables scalable inserts and complex queries for time-series data
(2 rows)Create an Application User
Avoid using the postgres superuser for your app. Create a dedicated user:
sudo -u postgres psql <<'EOF'
CREATE USER metrics_app WITH PASSWORD 'change-me-please';
GRANT ALL PRIVILEGES ON DATABASE metrics TO metrics_app;
\c metrics
GRANT ALL ON SCHEMA public TO metrics_app;
EOFStep 7: Create Your First Hypertable
A hypertable is a regular PostgreSQL table that you convert with a single function call. Let's model a typical IoT sensor workload:
sudo -u postgres psql -d metrics <<'EOF' CREATE TABLE sensor_readings ( time TIMESTAMPTZ NOT NULL, device_id TEXT NOT NULL, temperature DOUBLE PRECISION, humidity DOUBLE PRECISION, battery DOUBLE PRECISION );SELECT create_hypertable('sensor_readings', 'time', chunk_time_interval => INTERVAL '1 day');
CREATE INDEX ON sensor_readings (device_id, time DESC); EOF
What just happened:
create_hypertable('sensor_readings', 'time')converted the table into a hypertable partitioned by thetimecolumn.chunk_time_interval => INTERVAL '1 day'tells TimescaleDB to create a new chunk each day. For very high write rates, use smaller intervals (1 hour); for low-volume workloads, use larger (1 week). A good rule of thumb: aim for chunks that fit in about 25 percent of yourshared_buffers.- The secondary index supports fast lookups of a specific device's most recent readings.
Insert Some Data
INSERT INTO sensor_readings (time, device_id, temperature, humidity, battery)
SELECT
NOW() - (s || ' minutes')::INTERVAL,
'dev-' || (s % 100),
20 + random() * 10,
40 + random() * 30,
100 - (s * 0.01)
FROM generate_series(1, 100000) s;Query the latest reading per device — a classic time-series query:
SELECT DISTINCT ON (device_id) device_id, time, temperature
FROM sensor_readings
ORDER BY device_id, time DESC;Inspect Chunks
Every hypertable is a collection of chunks. List them:
SELECT chunk_name, range_start, range_end, is_compressed
FROM timescaledb_information.chunks
WHERE hypertable_name = 'sensor_readings'
ORDER BY range_start;You will see one chunk per day covered by your inserts. Each chunk is a real PostgreSQL table with its own indexes, which is what makes queries scale.
Step 8: Continuous Aggregates
Dashboards almost never need raw samples — they need averages, min/max, or percentiles over time buckets. A continuous aggregate is a materialized view that TimescaleDB keeps incrementally up to date as new rows arrive.
Create a 1-hour rollup of the sensor data:
CREATE MATERIALIZED VIEW sensor_readings_1h
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 hour', time) AS bucket,
device_id,
AVG(temperature) AS avg_temp,
MIN(temperature) AS min_temp,
MAX(temperature) AS max_temp,
AVG(humidity) AS avg_humidity,
COUNT(*) AS sample_count
FROM sensor_readings
GROUP BY bucket, device_id
WITH NO DATA;WITH NO DATA creates the view but skips the initial backfill — important on very large tables because the initial refresh can take a long time.
Attach a refresh policy so the aggregate stays current:
SELECT add_continuous_aggregate_policy('sensor_readings_1h',
start_offset => INTERVAL '3 days',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '30 minutes');This tells TimescaleDB: every 30 minutes, refresh any bucket between 3 days ago and 1 hour ago. The 1-hour end_offset ensures the current (incomplete) bucket is not prematurely materialized.
Trigger an initial refresh manually:
CALL refresh_continuous_aggregate('sensor_readings_1h', NULL, NULL);Now dashboards can hit sensor_readings_1h instead of scanning every raw row, and queries over months of data will return in milliseconds. This is the same pattern used by Grafana dashboards backed by TimescaleDB.
Step 9: Retention Policies
Time-series tables grow without bound unless you drop old data. A retention policy is a scheduled job that drops entire chunks older than a threshold — much cheaper than a DELETE, because dropping a chunk is a metadata operation that does not touch vacuum, WAL, or indexes.
Keep 90 days of raw readings:
SELECT add_retention_policy('sensor_readings', INTERVAL '90 days');Keep 2 years of the 1-hour rollup:
SELECT add_retention_policy('sensor_readings_1h', INTERVAL '2 years');List active retention jobs:
SELECT job_id, application_name, schedule_interval, config
FROM timescaledb_information.jobs
WHERE proc_name = 'policy_retention';Retention jobs run on the TimescaleDB background worker pool at a default interval of 1 day. To change the schedule, use alter_job(job_id, schedule_interval => INTERVAL '6 hours').
Step 10: Compression Policies
Hypertable compression converts older chunks from row storage to a compressed columnar format. Compressed chunks remain fully queryable — SELECT, WHERE, and aggregates all work — but storage typically shrinks by 90-95 percent and scans get faster because less I/O is needed.
Enable compression on the hypertable and define how rows should be segmented:
ALTER TABLE sensor_readings SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'device_id',
timescaledb.compress_orderby = 'time DESC'
);The segmentby column should be the one you filter by most often (here, device_id). The orderby column almost always should be time DESC so most-recent queries stay fast within a chunk.
Add a policy that compresses chunks older than 7 days:
SELECT add_compression_policy('sensor_readings', INTERVAL '7 days');Verify compression is configured:
SELECT hypertable_name, attname, segmentby_column_index, orderby_column_index
FROM timescaledb_information.compression_settings;After the policy runs (or once you trigger it manually with CALL run_job(<job_id>)), inspect the space savings:
SELECT
pg_size_pretty(before_compression_total_bytes) AS before,
pg_size_pretty(after_compression_total_bytes) AS after,
ROUND(
100.0 * (before_compression_total_bytes - after_compression_total_bytes)
/ NULLIF(before_compression_total_bytes, 0), 1
) AS pct_saved
FROM hypertable_compression_stats('sensor_readings');Typical output on a metric workload:
before | after | pct_saved
---------+--------+-----------
420 MB | 23 MB | 94.5For even denser time-series metrics you might consider alternatives like VictoriaMetrics or InfluxDB, but you give up full SQL, joins, and PostgreSQL's ecosystem. For most teams, TimescaleDB's compression is close enough to purpose-built TSDBs while keeping the familiar relational model.
Step 11: Monitoring with pg_stat
PostgreSQL ships with a family of pg_stat_* views that give you everything you need for monitoring. Combined with TimescaleDB's own timescaledb_information schema, you can build a complete picture of server health.
Enable pg_stat_statements for per-query metrics. Edit /etc/postgresql/16/main/postgresql.conf:
sudo sed -i "s/^#shared_preload_libraries./shared_preload_libraries = 'timescaledb,pg_stat_statements'/" \ /etc/postgresql/16/main/postgresql.confsudo systemctl restart postgresql
sudo -u postgres psql -d metrics -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"
Useful Monitoring Queries
Top 10 slowest queries:
SELECT
substring(query, 1, 80) AS query,
calls,
ROUND(mean_exec_time::numeric, 2) AS mean_ms,
ROUND(total_exec_time::numeric, 2) AS total_ms
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;Cache hit ratio (should be > 99 percent for hot data):
SELECT
ROUND(100.0 * SUM(blks_hit) / NULLIF(SUM(blks_hit) + SUM(blks_read), 0), 2) AS cache_hit_pct
FROM pg_stat_database
WHERE datname = 'metrics';TimescaleDB background job status:
SELECT job_id, application_name, last_run_status, last_run_started_at, next_start
FROM timescaledb_information.job_stats
ORDER BY last_run_started_at DESC NULLS LAST;Hypertable size breakdown:
SELECT
hypertable_name,
pg_size_pretty(hypertable_size(format('%I.%I', hypertable_schema, hypertable_name)::regclass)) AS total_size
FROM timescaledb_information.hypertables;Pipe these into Grafana for visualization, or export Prometheus metrics using postgres_exporter alongside TimescaleDB.
Step 12: Backups with pg_dump and pgBackRest
Option A: pg_dump for Small Databases
For development or small production databases (< 20 GB), the built-in pg_dump works identically for TimescaleDB:
sudo -u postgres pg_dump -Fc -d metrics -f /var/backups/metrics-$(date +%F).dumpThe -Fc flag produces a custom-format compressed archive that pg_restore can load selectively.
Restore into a fresh database:
sudo -u postgres createdb metrics_restored
sudo -u postgres psql -d metrics_restored -c "CREATE EXTENSION IF NOT EXISTS timescaledb;"
sudo -u postgres pg_restore -d metrics_restored /var/backups/metrics-2026-04-16.dumpAutomate with cron. Edit the postgres user's crontab:
sudo -u postgres crontab -eAdd a nightly dump at 02:00:
0 2 pg_dump -Fc -d metrics -f /var/backups/metrics-$(date +\%F).dump && find /var/backups -name 'metrics-.dump' -mtime +7 -deleteOption B: pgBackRest for Production
For production workloads you want incremental backups, point-in-time recovery (PITR), and offsite storage. pgBackRest delivers all three and is the de facto standard for PostgreSQL operations.
Install:
sudo apt install -y pgbackrestCreate a config file at /etc/pgbackrest/pgbackrest.conf:
[global] repo1-path=/var/lib/pgbackrest repo1-retention-full=2 repo1-retention-diff=6 process-max=4 log-level-console=info log-level-file=debug start-fast=y compress-type=zst compress-level=3
[metrics] pg1-path=/var/lib/postgresql/16/main pg1-port=5432 pg1-user=postgres
Prepare the repository and initialize the stanza:
sudo mkdir -p /var/lib/pgbackrest sudo chown postgres:postgres /var/lib/pgbackrest
sudo -u postgres pgbackrest --stanza=metrics stanza-create
Enable WAL archiving by appending to postgresql.conf:
echo "archive_mode = on" | sudo tee -a /etc/postgresql/16/main/postgresql.conf echo "archive_command = 'pgbackrest --stanza=metrics archive-push %p'" | sudo tee -a /etc/postgresql/16/main/postgresql.conf echo "max_wal_senders = 3" | sudo tee -a /etc/postgresql/16/main/postgresql.conf echo "wal_level = replica" | sudo tee -a /etc/postgresql/16/main/postgresql.conf
sudo systemctl restart postgresql
Take a full backup:
sudo -u postgres pgbackrest --stanza=metrics --type=full backupSubsequent runs can use --type=incr or --type=diff. Add a cron job:
# Sunday — full backup
0 1 0 pgbackrest --stanza=metrics --type=full backupMon-Sat — incremental
0 1 1-6 pgbackrest --stanza=metrics --type=incr backupFor offsite storage, configure repo1-type=s3 and point at any S3-compatible object store (AWS S3, Backblaze B2, Wasabi, MinIO). See the pgBackRest docs for full configuration.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
ERROR: could not access file "timescaledb" | Extension not in shared_preload_libraries | Re-run sudo timescaledb-tune, ensure shared_preload_libraries = 'timescaledb' in postgresql.conf, restart PostgreSQL. |
FATAL: database files are incompatible with server after upgrade | PostgreSQL major version mismatch | Run pg_upgrade with the correct old/new bin paths; do not mix PostgreSQL 15 data dirs with 16 binaries. |
| Inserts slow down over time | Chunk size too small, index bloat | Check SELECT * FROM chunks_detailed_size(...); increase chunk_time_interval with SELECT set_chunk_time_interval(...). |
| Continuous aggregate not updating | Refresh policy not active or job failing | Check timescaledb_information.job_stats — look at last_run_status; re-run with CALL run_job(<id>). |
| Compression not saving space | Wrong segmentby column or highly random data | Choose a segmentby with moderate cardinality; confirm column values actually repeat within a chunk. |
ERROR: permission denied for schema _timescaledb_internal | App user lacks rights | Grant: GRANT USAGE ON SCHEMA _timescaledb_internal TO metrics_app; — usually not needed if you only touch the hypertable. |
| High WAL volume | Compression/retention jobs running at peak time | Schedule jobs for off-peak: SELECT alter_job(<id>, next_start => '2026-04-16 03:00:00+00'); |
Viewing Logs
PostgreSQL logs land in /var/log/postgresql/:
sudo tail -f /var/log/postgresql/postgresql-16-main.logTimescaleDB messages, including background job output, go to the same file.
FAQ
Is TimescaleDB free and open source?
TimescaleDB Community Edition is free and open source under the Timescale License (TSL) and Apache 2.0 for the core. It includes hypertables, continuous aggregates, compression, retention policies, and native replication. The proprietary features previously reserved for enterprise (such as multi-node distributed hypertables) have been opened up or consolidated, so self-hosted installations can use the full feature set at no cost.
How does self-hosted TimescaleDB compare to Timescale Cloud on cost?
Timescale Cloud charges per compute unit, storage GB, and backup GB, with typical small production clusters costing USD 150-400 per month. A CloudCore Professional VPS at EUR 19.99 per month with 100 GB NVMe and unmetered bandwidth can host a TimescaleDB instance that handles millions of rows per day with compression. Once your workload exceeds a few hundred GB or 5-10K inserts per second, self-hosting saves thousands of dollars per year versus managed cloud.
Do I need to install PostgreSQL separately before TimescaleDB?
Yes. TimescaleDB is a PostgreSQL extension, not a standalone database. You install a supported PostgreSQL version first (15, 16, or 17), then add the TimescaleDB apt repository and install the matching timescaledb-2-postgresql-XX package. The extension is loaded via shared_preload_libraries and activated in each database with CREATE EXTENSION timescaledb.
What is a hypertable and why is it faster than a regular PostgreSQL table?
A hypertable is a virtual table that TimescaleDB automatically partitions into smaller chunks by time (and optionally by a space dimension such as device_id). Each chunk is a real PostgreSQL table with its own indexes. Queries that include a time predicate only scan the relevant chunks, so performance stays flat as the table grows to billions of rows. Inserts land in the newest chunk, which fits in memory and avoids index bloat.
How much compression can I expect?
TimescaleDB's columnar compression typically achieves 90-95 percent size reduction on metric-style workloads (numeric values with repeating labels). A 100 GB hypertable usually compresses to 5-15 GB. Compression is applied per chunk on a schedule, and compressed chunks remain queryable — you do not need to decompress before running SELECT.
Can I use TimescaleDB with Grafana?
Yes. Grafana has a first-class PostgreSQL data source that works directly with TimescaleDB. Point Grafana at your TimescaleDB host on port 5432, enable the TimescaleDB toggle in the data source settings, and Grafana will use macros like $__timeGroup and $__timeFilter that generate efficient time_bucket() queries against your hypertables.
How do I back up a TimescaleDB database?
For small or development databases, pg_dump with the --format=custom flag works exactly as it does for PostgreSQL. For production, use pgBackRest or WAL-G, which support incremental backups, point-in-time recovery, and offsite storage to S3-compatible object storage. Both are TimescaleDB-aware and handle hypertable chunks correctly.
Next Steps
Now that TimescaleDB is running on your VPS, here are practical follow-ups:
- Connect Grafana for dashboards — Install Grafana on the same VPS and point it at
localhost:5432. Enable the TimescaleDB toggle in the data source settings for optimized queries. - Benchmark your write path — Use Timescale's tsbs tool to benchmark inserts and queries against realistic IoT, DevOps, and IoT workloads. Tune
chunk_time_intervalandwork_membased on the results. - Set up streaming replication — For high availability, configure a standby replica with
pg_basebackupandprimary_conninfo. TimescaleDB works transparently with PostgreSQL streaming replication. - Try pgvector alongside — For hybrid time-series + vector search (e.g., anomaly detection on embeddings), install pgvector in the same database. Full SQL joins between hypertables and vector columns just work.
- Compare with alternatives — If your workload is pure metric ingest without joins, benchmark against VictoriaMetrics and InfluxDB to see whether the SQL flexibility of TimescaleDB is worth the slightly higher storage overhead for your case.
- Read the upstream docs — Bookmark docs.timescale.com for detailed references on hyperfunctions (percentile approximation, gap-filling, LTTB downsampling) and advanced topics like multi-node distributed hypertables.
Ready to deploy TimescaleDB in production?>
The CloudCore Professional VPS gives you the right balance of CPU, RAM, and NVMe storage for a self-hosted time-series database — at a flat EUR 19.99 per month with unmetered bandwidth. Provision in under 60 seconds, run this guide end to end, and start ingesting metrics the same day.>
- 6 vCPU cores
- 12 GB RAM
- 100 GB NVMe SSD
- Unmetered bandwidth
- Full root access, any Linux distribution>
Launch your CloudCore Professional VPS and keep every byte of your time-series data on infrastructure you control.