TimescaleDB
Tested with: TimescaleDB 18 on Databasezy · zb CLI 0.1
TimescaleDB is a PostgreSQL extension for time-series data. It adds hypertables, which partition data by time automatically, while you keep plain SQL, joins and every PostgreSQL driver.
Overview
Section titled “Overview”Each instance is a PostgreSQL cluster managed by CloudNativePG with an image that includes TimescaleDB, and the
timescaledb extension is created in the app database for you. Everything on the PostgreSQL page
about users, TLS and connections applies.
Databasezy runs the Apache-2.0 edition of TimescaleDB. It includes hypertables, automatic chunking, time_bucket
and manual chunk management (show_chunks, drop_chunks). It does not include the features under the Timescale
License (TSL); see Licence below.
| Status | Available |
|---|---|
| Category | Time-series |
| Versions | 18, 17, 16 (newest is the default) |
| Protocol and port | PostgreSQL wire protocol on 5432 |
| Runtime | Operator-backed (CloudNativePG) |
| Backups | barman-cloud base backups + WAL |
| Point-in-time recovery | Yes |
| Pause | Scale to zero |
| Free plan | Yes (size f0) |
| Features | Built-in transaction-mode pooler, Extensions, Read replicas, High availability (standby) |
| Licence | Apache-2.0 (Apache edition) |
When to use it
Section titled “When to use it”- Time series that you want next to relational data in one database: metrics with customer tables, IoT readings with device metadata.
- Teams that already use PostgreSQL tools, ORMs and drivers and want time partitioning without a new query language.
Pick QuestDB or InfluxDB 3 for very high ingest rates, and ClickHouse for large analytical scans. If you depend on TimescaleDB compression or continuous aggregates, note that they are not in the Apache edition.
Create an instance
Section titled “Create an instance”- Open the portal and choose New instance.
- Pick TimescaleDB and a version (18, 17, 16).
- Choose a size and region. On the Free plan the size is f0. Secure placement is listed only after your organization has signed the BAA.
- Check the hourly price and monthly estimate, then confirm. The Connect tab fills in when the instance is ready.
zb instances create --engine timescaledb --engine-version 18 \ --size s1 --region us-east --name timescaledb-demo --wait# On the Free plan, use --size f0
# Reveal the credentials once and store them in your secret managerzb instances credentials reveal timescaledb-democurl -sS https://api.databasezy.com/v1/orgs/$ZB_ORG/projects/$ZB_PROJECT/instances \ -H "Authorization: Bearer $ZB_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "engine": "timescaledb", "engine_version": "18", "size": "s1", "region": "us-east", "name": "timescaledb-demo"}'
The response is 201 with the instance in status requested, or 202 when a
team approval policy applies. See the REST API reference.
Connect
Section titled “Connect”Endpoint and credentials
Section titled “Endpoint and credentials”| Host | ts-<id>.<region>.databasezy.com, for example ts-7f3k.us-east.databasezy.com |
|---|---|
Port 5432 | PostgreSQL wire protocol |
| TLS | Required on every port; plaintext is refused. Verify the server: sslmode=verify-full with the CA bundle. |
| Credentials | Username app and a generated password, presented as SCRAM password authentication. Shown once at creation; reveal or rotate it from the Connect tab. |
Connect exactly as you would to PostgreSQL, with sslmode=verify-full and the CA bundle for libpq-based drivers.
Drivers and clients
Section titled “Drivers and clients”TimescaleDB listens on port 5432 (PostgreSQL wire protocol). Versions: 18, 17, 16. Replace the example host with the one on your instance's Connect tab; credentials are shown once at creation.
postgresql://app:<password>@ts-7f3k.us-east.databasezy.com:5432/app?sslmode=verify-full&sslrootcert=databasezy-ca.pemimport { readFileSync } from "node:fs";import { Pool } from "pg";
const host = "ts-7f3k.us-east.databasezy.com";export const pool = new Pool({ host, port: 5432, database: "app", user: "app", password: process.env.ZB_PASSWORD, ssl: { ca: readFileSync("databasezy-ca.pem", "utf8"), rejectUnauthorized: true, // verify chain servername: host, // verify hostname (verify-full) }, max: 10, // keep well under the size's max_connections idleTimeoutMillis: 30_000,});
const { rows } = await pool.query("select version()");console.log(rows[0]);Tested with: TimescaleDB 18 · node-postgres (pg) 8.13
import osfrom psycopg_pool import ConnectionPool
conninfo = ( "host=ts-7f3k.us-east.databasezy.com port=5432 dbname=app user=app " f"password={os.environ['ZB_PASSWORD']} " "sslmode=verify-full sslrootcert=databasezy-ca.pem")pool = ConnectionPool(conninfo, min_size=1, max_size=10)
with pool.connection() as conn: print(conn.execute("select version()").fetchone())Tested with: TimescaleDB 18 · psycopg 3.2 · psycopg_pool 3.2
package main
import ( "context" "fmt" "os"
"github.com/jackc/pgx/v5/pgxpool")
func main() { ctx := context.Background() dsn := fmt.Sprintf( "postgres://app:%s@ts-7f3k.us-east.databasezy.com:5432/app?sslmode=verify-full&sslrootcert=databasezy-ca.pem&pool_max_conns=10", os.Getenv("ZB_PASSWORD"), ) pool, err := pgxpool.New(ctx, dsn) if err != nil { panic(err) } defer pool.Close()
var v string if err := pool.QueryRow(ctx, "select version()").Scan(&v); err != nil { panic(err) } fmt.Println(v)}Tested with: TimescaleDB 18 · pgx v5.7
use sqlx::postgres::{PgConnectOptions, PgPoolOptions, PgSslMode};
#[tokio::main]async fn main() -> Result<(), sqlx::Error> { let opts = PgConnectOptions::new() .host("ts-7f3k.us-east.databasezy.com") .port(5432) .database("app") .username("app") .password(&std::env::var("ZB_PASSWORD").expect("ZB_PASSWORD")) .ssl_mode(PgSslMode::VerifyFull) .ssl_root_cert("databasezy-ca.pem");
let pool = PgPoolOptions::new().max_connections(10).connect_with(opts).await?; let (v,): (String,) = sqlx::query_as("select version()").fetch_one(&pool).await?; println!("{v}"); Ok(())}Tested with: TimescaleDB 18 · sqlx 0.8 (runtime-tokio, tls-rustls)
import com.zaxxer.hikari.HikariConfig;import com.zaxxer.hikari.HikariDataSource;
public final class Db { public static HikariDataSource open() { var cfg = new HikariConfig(); cfg.setJdbcUrl("jdbc:postgresql://ts-7f3k.us-east.databasezy.com:5432/app" + "?sslmode=verify-full&sslrootcert=databasezy-ca.pem"); cfg.setUsername("app"); cfg.setPassword(System.getenv("ZB_PASSWORD")); cfg.setMaximumPoolSize(10); return new HikariDataSource(cfg); }}Tested with: TimescaleDB 18 · PostgreSQL JDBC 42.7 · HikariCP 6.2
using Npgsql;
var cs = new NpgsqlConnectionStringBuilder{ Host = "ts-7f3k.us-east.databasezy.com", Port = 5432, Database = "app", Username = "app", Password = Environment.GetEnvironmentVariable("ZB_PASSWORD"), SslMode = SslMode.VerifyFull, RootCertificate = "databasezy-ca.pem", MaxPoolSize = 10,};await using var dataSource = NpgsqlDataSource.Create(cs);await using var cmd = dataSource.CreateCommand("select version()");Console.WriteLine(await cmd.ExecuteScalarAsync());Tested with: TimescaleDB 18 · Npgsql 9.0
<?php$dsn = "pgsql:host=ts-7f3k.us-east.databasezy.com;port=5432;dbname=app;" . "sslmode=verify-full;sslrootcert=databasezy-ca.pem";$pdo = new PDO($dsn, "app", getenv("ZB_PASSWORD"), [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_PERSISTENT => true, // reuse connections across requests]);echo $pdo->query("select version()")->fetchColumn();Tested with: TimescaleDB 18 · PHP 8.3 · PDO pgsql
require "pg"
conn = PG.connect( host: "ts-7f3k.us-east.databasezy.com", port: 5432, dbname: "app", user: "app", password: ENV.fetch("ZB_PASSWORD"), sslmode: "verify-full", sslrootcert: "databasezy-ca.pem")puts conn.exec("select version()").firstTested with: TimescaleDB 18 · pg 1.5 · Ruby 3.3
# Connection URI (copy from Instance → Connect in the portal)export DATABASE_URL='postgresql://app:<password>@ts-7f3k.us-east.databasezy.com:5432/app?sslmode=verify-full&sslrootcert=databasezy-ca.pem'
psql "$DATABASE_URL"# or with zb (spawns psql with TLS flags and a one-time credential)zb connect timescaledb-7f3kTested with: TimescaleDB 18 · psql 17
The PostgreSQL connection guide covers TLS, pooling and the allow-list in more depth, and the ORM guides (Prisma, Drizzle, Django, SQLAlchemy, Rails) apply unchanged.
Create a hypertable from a regular table with:
create table readings (time timestamptz not null, device_id text, temperature double precision);select create_hypertable('readings', by_range('time'));See Connecting to Databasezy for the CA bundle, the IP allow-list and credential rotation, which work the same for every engine.
Migrate in
Section titled “Migrate in”| Source | How | Continuous sync | Guide status |
|---|---|---|---|
| Self-hosted server | Connection string, Local tools (zb migrate --from local) | No | Available |
| Local files and dumps | File upload, Local tools (zb migrate --from local) | No | Available |
| Docker container | Local tools (zb migrate --from local) | No | Available |
| Another Databasezy instance | Instance to instance | No | Coming soon · phase 2 |
Without a migration, for example from a source Databasezy cannot reach: create your tables and hypertables on
Databasezy first (your schema migrations, or the statements below), then stream the rows of each table with \copy.
Reading the source through select also returns rows from compressed chunks, so nothing needs decompressing first.
psql "$TARGET_URL" \ -c "create table readings (time timestamptz not null, device_id text, temperature double precision);" \ -c "select create_hypertable('readings', by_range('time'));"psql "$SOURCE_URL" -c "\copy (select * from readings) to pstdout" \ | psql "$TARGET_URL" -c "\copy readings from pstdin"To turn PostgreSQL tables into hypertables, copy them the same way (from a PostgreSQL source, pg_dump --no-owner
of plain tables restores as the generated user), then run
select create_hypertable('<table>', by_range('<time column>'), migrate_data => true); per table. The table is
locked while existing rows move into chunks.
How migrations work explains preflight, verification and cutover, and engine conversions covers moves between compatible engines.
Backups and restore
Section titled “Backups and restore”CloudNativePG takes barman-cloud base backups and archives the write-ahead log continuously, as for PostgreSQL; point-in-time recovery replays that log to the moment you choose.
TimescaleDB backups use barman-cloud base backups + WAL. They run inside the instance's namespace, stream straight to the cell's object storage and are checksummed on upload. How often they run and how long they are kept follows your plan's backup policy. A backup is always taken before a resize or a version upgrade.
Point-in-time recovery is supported: you can restore to any moment inside the PITR window of your plan or the PITR add-on. Restores create a new instance by default and leave the original untouched; an in-place restore asks you to type the instance name and takes a pre-change backup first.
zb backups create timescaledb-demo --label before-release # manual snapshotzb backups list timescaledb-demozb backups restore timescaledb-demo <backup-id> --name timescaledb-demo-restore
# Point in time (RFC 3339) inside the PITR windowzb backups pitr-window timescaledb-demozb backups restore timescaledb-demo <backup-id> --at "2026-09-27T08:15:00Z" --name timescaledb-demo-pitrPause and scale to zero
Section titled “Pause and scale to zero”TimescaleDB can scale to zero. A paused instance has no running pods and bills no compute; its storage and backups are kept and billed as usual. Connections are refused until you resume it. On the Free plan an instance pauses by itself after 15 minutes without connections and wakes on the next one; the gateway holds that connection for up to 30 seconds while it starts.
zb instances pause timescaledb-demozb instances resume timescaledb-demoSee Pause and resume for schedules, wake times and billing while paused.
Limits, versions and lifecycle
Section titled “Limits, versions and lifecycle”Versions and lifecycle
Section titled “Versions and lifecycle”- Supported versions:
18,17,16. New instances default to18; pick another at creation with --engine-version or in the portal. - Minor and patch releases are applied for you in the maintenance window, always after a pre-change backup.
- New majors are added within 60 days of the upstream release. A major reaches end of life on Databasezy six months after upstream ends support, with notices 90, 30 and 7 days ahead.
- Moving between majors is a new instance plus an instance-to-instance migration, so you can test the new version before cutting over.
Sizes and limits
Section titled “Sizes and limits”TimescaleDB runs on every size, including the Free plan's f0. The size sets the CPU, memory, storage ceiling and connection limit; the gateway refuses connections over the limit with a protocol error. Storage grows in steps up to the ceiling, and you can resize at any time.
| Size | vCPU | Memory | Max storage | Max connections | ≈ $ / month |
|---|---|---|---|---|---|
f0 | 0.063 | 512 MiB | 1 GB | 20 | Free |
s0 | 0.25 | 1 GiB | 20 GB | 60 | $10 |
s1 | 0.5 | 2 GiB | 50 GB | 100 | $15 |
s2 | 1 | 4 GiB | 200 GB | 200 | $60 |
m2 | 2 | 8 GiB | 500 GB | 400 | $110 |
m4 | 4 | 16 GiB | 1 TB | 800 | $210 |
l8 | 8 | 32 GiB | 4 TB | 1,500 | $410 |
l16 | 16 | 64 GiB | 8 TB | 3,000 | $960 |
xl32 | 32 | 128 GiB | 16 TB | 5,000 | $1,870 |
Full details, including hourly prices and burst CPU, are in the size catalogue; plan quotas are in limits and quotas.
Hypertables follow PostgreSQL’s limits plus chunk sizing: choose a chunk interval so that recent chunks fit in memory
(the default is seven days). Without TSL retention policies, drop old data with drop_chunks() from your own
scheduler. Apart from TimescaleDB, the image has the same extensions as PostgreSQL: the generated user can create the
trusted ones listed under PostgreSQL extensions; PostGIS and pg_cron are not
included.
Licence
Section titled “Licence”Databasezy runs TimescaleDB under the Apache-2.0 (Apache edition) licence, as listed in the engine catalogue. Your data and schemas are yours whatever the server's licence; the licence governs the server software we run.
Security
Section titled “Security”- TLS on every connection. TLS 1.2 is the minimum and TLS 1.3 is preferred; plaintext is never
offered. Verify the server, not just the encryption: use
sslmode=verify-full. See TLS and the CA bundle. - IP allow-list. The gateway checks the client address before authentication, on every plan.
Manage it under Network → Allow-list in the portal or with
PUT /v1/orgs/{org}/instances/{id}/network. - Credentials. Generated inside the cell, shown once, never stored by the control plane. Rotate them with an overlap window so nothing breaks.
- Secure hosting. Secure placement (HIPAA-ready) is a per-instance option once your organization has signed the BAA: dedicated nodes, customer-managed keys and immutable backups. See Secure hosting and the BAA.
- Staff access. Databasezy staff cannot read your data without a grant you issue. See data confidentiality.
Is this a separate database from PostgreSQL?
Section titled “Is this a separate database from PostgreSQL?”No. It is PostgreSQL with the TimescaleDB extension preinstalled. Your PostgreSQL drivers, ORMs and tools work unchanged.
Can I use compression or continuous aggregates?
Section titled “Can I use compression or continuous aggregates?”No. They are Timescale License features, and Databasezy runs the Apache-2.0 edition. Use regular materialized views refreshed by your own scheduler for rollups.
Can I add TimescaleDB to my existing PostgreSQL instance?
Section titled “Can I add TimescaleDB to my existing PostgreSQL instance?”No. It ships as its own engine. Migrate the data into a TimescaleDB instance with zb migrate --from <postgres-instance> --to <timescale-instance>.
Is TimescaleDB on the Free plan?
Section titled “Is TimescaleDB on the Free plan?”Yes, on the f0 size.