TimescaleDB
TimescaleDB is a PostgreSQL extension for time-series workloads. It turns a regular table into a hypertable: rows are partitioned into time-based chunks automatically, while applications keep using ordinary SQL, indexes and transactions.
pgcli installs the Pigsty TSL build of TimescaleDB 2.31.0. Hypertables,
time_bucket, chunk management, columnstore/compression, continuous aggregates and retention policies are available. The TSL permits free internal use but prohibits offering a time-series database as a service.
Use cases
TimescaleDB fits append-heavy data where time is the main access dimension:
- IoT and device telemetry — per-device readings, latest values and time-window aggregates.
- Infrastructure and application monitoring — CPU, memory, latency and request metrics for dashboards and alerting.
- Industrial and energy metering — high-frequency sensor streams with a long retention window.
- Financial market data — ticks, quotes and trades; OHLC-style rollups with
time_bucketandfirst/last. - Product and event analytics — user events, sessions and API calls aggregated over recent time windows.
- Smart home and building automation — sensor history and state changes.
The typical shape is append-only rows keyed by (entity_id, time): queries
filter a recent time range, and old data is expired by dropping whole chunks.
It is a poor fit for OLTP workloads without a time dimension, key-value or document storage, and blob storage. The TSL build includes compression, continuous aggregates and retention policies, but the TSL does not allow offering a time-series database as a service.
Install
pgcli builds an extension image with the Pigsty TSL package, puts
timescaledb first in shared_preload_libraries, restarts PostgreSQL and runs
CREATE EXTENSION IF NOT EXISTS timescaledb in the instance’s default
database.
To use it from another database in the same instance:
Check the installed version and license:
The Pigsty package reports timescale.
Quick start
1. Create an instance
pg create writes the configuration and prints the admin password; pg start
brings up PostgreSQL and the backup container. The default database is
tsdb_db.
2. Install the extension
pgcli builds the extension image, preloads timescaledb, restarts PostgreSQL
and runs CREATE EXTENSION IF NOT EXISTS timescaledb in tsdb_db.
3. Create a hypertable
Connect to the instance:
Create a table and convert it to a hypertable:
Or create it in one step; the default chunk interval applies and can be changed afterwards:
Choose the chunk interval to fit the query and retention window. Existing chunks keep their interval when you change it later.
4. Write data
5. Daily operations
Append new rows, inspect chunks and jobs, and enable automatic maintenance:
Operational commands:
See Compression and columnstore, Retention policies, Continuous aggregates and Managing chunks for the full set.
InfluxDB-compatible API: the influx2tsdb addon
If existing tooling speaks InfluxDB rather than SQL, the influx2tsdb addon runs a managed, stateless proxy that serves the InfluxDB 1.x HTTP API (Line Protocol writes and InfluxQL queries) on top of a TimescaleDB database:
- each InfluxDB database becomes a PostgreSQL schema, and each
measurement becomes a hypertable with
timeas the partition column; CREATE / ALTER / DROP RETENTION POLICYis translated into TimescaleDB’s ownadd_retention_policy/add_compression_policyjobs — nopg_cronis needed, and chunk cleanup continues while the proxy is stopped;- the proxy holds no data, so backup, PITR, replication and HA remain ordinary PostgreSQL operations.
The trade-off is throughput: every request is translated to SQL, so pure time-series ingest and query load is slower than native InfluxDB. The proxy has no authentication or TLS; front it with nginx or isolate the port. See the addon page for flags, the data model, retention semantics and the test scripts.
Query patterns
First and last value per series:
Latest row per series:
Daily buckets in a specific timezone:
Fill gaps in a bucket sequence:
time_bucket_gapfill needs an explicit start and finish, either as the second
and third arguments or in the WHERE clause.
Compression and columnstore
Older chunks can be moved to the columnstore, which compresses them and speeds up analytical scans:
Check the compression status and the space used:
Retention policies
Expire chunks automatically once they fall outside the retention window:
These jobs run in TimescaleDB’s own background scheduler; pg_cron is not required.
The influx2tsdb addon uses the same mechanism: its
CREATE / ALTER / DROP RETENTION POLICY statements are translated into these jobs
automatically, and cleanup continues while the proxy is stopped.
Continuous aggregates
Pre-aggregate recent data and keep the rollup refreshed in the background:
Query the rollup like a normal table:
The policy refreshes only the recent window between start_offset and
end_offset; refresh older ranges manually after a backfill.
Managing chunks
Chunk intervals
Set the interval at creation, or change it later for new chunks:
For timestamptz columns, chunk boundaries align to UTC. Choose an interval
large enough that a chunk fits comfortably in memory; a very small interval
creates many chunks and adds planning overhead.
List chunks
older_than and newer_than filter by the chunk’s time range, not by its
creation time.
Chunk sizes
Split a chunk
Split one chunk at a timestamp inside its range; take the chunk name from
show_chunks('sensor'):
split_chunk is a procedure; use CALL, not SELECT.
Remove chunks
drop_chunks removes files instead of producing dead tuples. Avoid deleting
large time ranges with DELETE. For automatic expiration prefer a retention
policy; pg_cron is only needed for a custom schedule:
Manual columnstore conversion
With compression settings configured (see Compression and columnstore), convert a single chunk without waiting for the policy:
Both procedures take a chunk name, not the hypertable name.
Metadata views
jobs also lists the columnstore, retention and continuous-aggregate policies
created above, alongside the internal job-history task.
Feature availability
| Feature | Status |
|---|---|
Hypertables, time_bucket, first/last, show_chunks, drop_chunks, hypertable_size, metadata views |
✅ Apache |
Space partitioning with add_dimension / by_hash |
✅ Apache |
Columnstore and compression (ALTER TABLE ... SET (timescaledb.compress), CALL add_columnstore_policy) |
✅ TSL |
Continuous aggregates (CREATE MATERIALIZED VIEW ... WITH (timescaledb.continuous)) |
✅ TSL |
Retention policies (add_retention_policy) |
✅ TSL |
time_bucket_gapfill |
✅ TSL |
Rows marked ✅ Apache follow Apache-2.0. Rows marked ✅ TSL come from the Timescale License: free for internal use, but offering a time-series database as a service is not permitted.
Backup and restore
Logical backups and pg export/pg import work as usual. A physical restore
(pgBackRest, used by pg backup/pg restore) also needs TimescaleDB’s restore
hooks:
pg restore does not run these automatically, so run timescaledb_post_restore()
after the restore completes. Test the procedure before relying on it.
Permissions
CREATE EXTENSION requires a superuser; pg exec connects as the instance
admin. The owner of a table can convert it with create_hypertable and manage
its chunks. Application roles need only ordinary table privileges.
Troubleshooting
extension "timescaledb" is not available. The package is missing from the
instance image, or the extension was not created in the target database. Run
pg extension install timescaledb -i <instance> and create it in that database.
timescaledb is not preloaded. The extension is in
shared_preload_libraries but PostgreSQL was not restarted. pg extension install handles this; verify with SHOW shared_preload_libraries; and restart
the instance.
License errors for a function. The instance image still contains the
Apache build (an older pgcli release or a manually installed PGDG package).
Reinstall with pg extension install timescaledb -i <instance> --auto-restart
to switch to the Pigsty TSL package.
cannot create a unique index without the column "time". Every unique
index and primary key on a hypertable must include the partitioning column:
Too many chunks. Lower the chunk interval with
set_chunk_time_interval; existing chunks are not rewritten.
Old data does not disappear. Add a retention policy with
add_retention_policy, or run drop_chunks manually. Check
timescaledb_information.jobs for the policy job.
Function reference
| Function | Purpose | Status |
|---|---|---|
create_hypertable('t', by_range('time')) |
Convert a table to a hypertable | ✅ |
by_range('time', INTERVAL '1 day') |
Time partitioning with a chunk interval | ✅ |
set_chunk_time_interval('t', INTERVAL ...) |
Change the interval for new chunks | ✅ |
time_bucket('1 hour', time [, timezone]) |
Time-based aggregation buckets | ✅ |
first(value, time) / last(value, time) |
First/last value in a group | ✅ |
show_chunks('t' [, older_than/newer_than => ...]) / drop_chunks('t', ...) |
Inspect, filter and drop chunks | ✅ |
hypertable_size('t') / hypertable_detailed_size('t') |
Hypertable size and breakdown | ✅ |
add_dimension('t', by_hash('col', N)) |
Add space partitioning | ✅ |
add_retention_policy(...) / remove_retention_policy(...) |
Automatic chunk expiration | ✅ TSL |
CALL add_columnstore_policy(...) |
Columnstore/compression policy | ✅ TSL |
CALL convert_to_columnstore(chunk) / CALL convert_to_rowstore(chunk) |
Manual chunk storage conversion | ✅ TSL |
hypertable_columnstore_stats('t') / chunk_columnstore_stats('t') |
Columnstore statistics | ✅ TSL |
CREATE MATERIALIZED VIEW ... WITH (timescaledb.continuous) |
Continuous aggregates | ✅ TSL |
add_continuous_aggregate_policy(...) |
Continuous aggregate refresh policy | ✅ TSL |
time_bucket_gapfill(...) |
Fill missing buckets | ✅ TSL |