TimescaleDB for Self-Hosted Supabase: Time-Series Done Right

Supabase Cloud is deprecating TimescaleDB on Postgres 17. Learn how to run it on self-hosted Supabase for metrics, IoT, and time-series workloads.

Cover Image for TimescaleDB for Self-Hosted Supabase: Time-Series Done Right

If you store metrics, sensor readings, financial ticks, or any data that arrives in a steady stream over time, you've probably hit the wall where plain PostgreSQL tables start to creak. Queries that aggregate "the last 30 days by hour" slow to a crawl, your indexes balloon, and DELETE jobs to prune old rows lock everything up. TimescaleDB exists to solve exactly this — and as of 2026, self-hosting Supabase is increasingly the only way to keep using it.

Here's the honest situation, why it matters, and how to get TimescaleDB running on a self-hosted stack without breaking the rest of your Supabase services.

The 2026 reality: TimescaleDB is leaving Supabase Cloud

This is the part most tutorials skip, so let's be direct. Supabase has deprecated the timescaledb extension on Postgres 17. Existing Cloud projects on Postgres 15 keep it until that version reaches end-of-life — roughly mid-2026 — but it receives no further updates, and you must drop it before upgrading to Postgres 17. After that, it's gone from the platform entirely.

The official migration path Supabase suggests is moving Timescale hypertables to native Postgres partitioning via pg_partman. For some workloads that's a reasonable swap (we cover the mechanics in our guide to table partitioning with pg_partman). But native partitioning doesn't give you continuous aggregates, automatic compression, or the hypertable query planner optimizations that make Timescale fast in the first place. If your application genuinely depends on those features, you have two options: leave the Supabase ecosystem, or self-host.

Self-hosting wins here. As Supabase's own team has noted in community discussions, if your data model requires TimescaleDB, running your own instance is the supported route. This is a recurring theme — control over your extension stack is one of the strongest arguments in the broader self-hosted vs cloud comparison.

The catch: it's not in the standard image (PG17)

Don't expect CREATE EXTENSION timescaledb; to just work after pnpm supabase start. The standard supabase/postgres image bundles a curated set of extensions, and for Postgres 17 builds, TimescaleDB is not one of them. You'll need a custom Postgres image that layers TimescaleDB on top of Supabase's base.

This is a real trade-off, so name it plainly:

  • You're now maintaining a custom image. Every time Supabase ships a base image update (security patches, Postgres minors), you rebuild yours on top.
  • It's "not fully supported." You own the compatibility testing between Timescale and Supabase's extension set.
  • Licensing matters. TimescaleDB's core is Apache-2.0, but advanced features (compression policies, continuous aggregate refresh automation) live under the Timescale License (TSL). It's free to use, but it is not OSI open-source — confirm it fits your compliance posture before committing.

None of these are dealbreakers. They're just the maintenance reality of stepping outside the default stack, the same kind of ownership you take on whenever you run PostgreSQL extensions beyond the defaults.

Building a TimescaleDB-enabled Postgres image

The cleanest approach is a thin Dockerfile that extends Supabase's image and installs the matching TimescaleDB release for your Postgres major version.

# Dockerfile.timescale
ARG SUPABASE_PG_VERSION=15.8.1.060
FROM supabase/postgres:${SUPABASE_PG_VERSION}

# Install TimescaleDB build matching the Postgres major version
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
      timescaledb-2-postgresql-15 && \
    rm -rf /var/lib/apt/lists/*

# Preload the library so the planner hooks are active
RUN echo "shared_preload_libraries = 'timescaledb,pg_stat_statements'" \
      >> /usr/share/postgresql/postgresql.conf.sample

Version note: TimescaleDB tracks Postgres majors closely. Pin to a supabase/postgres tag on Postgres 15 if you want the smoothest path today — TimescaleDB's Postgres 17 support has historically lagged, which is precisely why Supabase paused bundling it. Always match the timescaledb-2-postgresql-XX package to your base image's major version.

Then point your Compose file at the custom build instead of the upstream image:

# docker-compose.override.yml
services:
  db:
    build:
      context: .
      dockerfile: Dockerfile.timescale
    # Ensure the preload library is active
    command:
      - postgres
      - -c
      - shared_preload_libraries=timescaledb,pg_stat_statements

Because shared_preload_libraries requires a restart to take effect, set it at the container level rather than runtime. If you're already customizing your stack, our notes on Docker Compose production best practices cover override files and rebuild hygiene in more depth.

Creating your first hypertable

With the image rebuilt and the database restarted, enable the extension and convert a regular table into a hypertable — Timescale's partitioned-by-time abstraction.

create extension if not exists timescaledb;

create table sensor_readings (
  time        timestamptz not null,
  device_id   uuid        not null,
  temperature double precision,
  humidity    double precision
);

-- Turn it into a hypertable, partitioned by time in 1-day chunks
select create_hypertable(
  'sensor_readings',
  by_range('time', interval '1 day')
);

From your application code, it's still just a Postgres table — insert and query it through the standard Supabase client. Crucially, Row Level Security still applies, so your existing access patterns carry over. If you're securing multi-tenant metrics, the same rules from our Row Level Security guide work unchanged on hypertables.

Continuous aggregates and compression

The two features worth the maintenance cost:

-- Pre-computed hourly rollups that refresh automatically
create materialized view sensor_hourly
with (timescaledb.continuous) as
select
  time_bucket('1 hour', time) as bucket,
  device_id,
  avg(temperature) as avg_temp,
  max(humidity)    as max_humidity
from sensor_readings
group by bucket, device_id;

-- Compress chunks older than 7 days (TSL feature)
alter table sensor_readings set (timescaledb.compress);
select add_compression_policy('sensor_readings', interval '7 days');

Continuous aggregates can turn a dashboard query that scanned millions of raw rows into a lookup against a few thousand pre-rolled buckets. Compression routinely cuts time-series storage by 90%+, which directly lowers your VPS disk costs — relevant if you're watching the numbers in the true cost of self-hosting.

Don't forget backups and upgrades

A hypertable is still Postgres data, but TimescaleDB adds operational wrinkles you need to plan for:

  • Backups: A logical pg_dump of a Timescale database needs the extension's pre/post hooks to restore chunk metadata correctly. Physical backups (the approach we recommend in the backup and restore guide) sidestep this and are the safer default for hypertables. Whichever you choose, test the restore before you rely on it.
  • Upgrades: Your custom image means upgrades are a two-axis problem — the Supabase base and the TimescaleDB version. Stage them in a non-production environment first, following the cautions in our version migration guide.

This is the unglamorous half of self-hosting, and it's exactly where a management layer earns its keep.

Where Supascale fits

Supascale doesn't make TimescaleDB "officially supported" — no one can, given it lives outside the default image. What it does is remove the operational tax around the parts that do break:

  • Automated S3 backups with one-click restore so your hypertable data — compressed chunks and all — has a tested recovery path instead of a cron job you hope still works.
  • Selective service deployment, so a metrics-heavy project runs only the services it needs without dragging along components you'll never use.
  • A management UI and full REST API for the day-two operations — restarts after a shared_preload_libraries change, monitoring, domain and SSL config — that a raw Compose stack leaves you to script by hand.

You still own your custom image and your extension choices. That's the point of self-hosting: no vendor decides your Postgres can't run TimescaleDB. Supascale just makes living with that decision a lot less painful. See the pricing page — it's a one-time purchase for unlimited projects, which is friendly to the "spin up a dedicated time-series instance" pattern.

The bottom line

TimescaleDB on self-hosted Supabase is a genuinely good fit for metrics, IoT, observability, and financial data — and as Cloud phases it out, self-hosting becomes the path that keeps it on the table. The cost is real: a custom image to maintain, an unsupported combination to test, and license terms to read. But in exchange you get hypertables, continuous aggregates, and compression inside the same Postgres that already powers your auth, storage, and realtime.

If time-series is core to your product and you don't want it dictated by someone else's deprecation schedule, this is the kind of control self-hosting was built for. Get started with Supascale and run the Postgres you want.

Further Reading