Skip to main content

Command Palette

Search for a command to run...

How to Spin Up TimescaleDB with Docker Compose

Updated
5 min read
How to Spin Up TimescaleDB with Docker Compose

Hello, I'm Ganesh. I'm working on FreeDevTools online, currently building one place for all dev tools, cheat codes, and TLDRs — a free, open-source hub where developers can quickly find and use tools without any hassle of searching all over the internet.

Ever find yourself needing a powerful time-series database for a new project, but you dread the complex setup? Whether you're building an IoT platform, a financial tracker, or a monitoring service, getting your database running shouldn't be a roadblock.

TimescaleDB is a fantastic extension for PostgreSQL that makes handling time-series data a breeze. And when you combine it with Docker Compose, you can go from zero to a fully running, persistent, and auto-configured database in just a couple of minutes.

In this post, we'll walk through the minimal, clean setup to get a TimescaleDB instance running, complete with your own custom schema, all in one go.

What You'll Need

  1. Docker and Docker Compose installed on your machine.

  2. A new project folder to work in.

Step 1: The Project Structure

A clean setup keeps things simple. All we need are three files. Let's create a project directory and inside it, a folder to hold our database-related files.

my-project/
├── docker-compose.yml
└── timescale-db/
    ├── Dockerfile
    └── schema.sql
  • docker-compose.yml: This is our orchestra conductor. It will define and run our database service.

  • timescale-db/Dockerfile: This simple file tells Docker what image to use.

  • timescale-db/schema.sql: This is our startup script. The database will run this file one time when it's first created.

Step 2: The Dockerfile

This is the easiest part. We're not building a complex image; we're just telling Docker to use the official TimescaleDB image, which is built on PostgreSQL.

Inside timescale-db/Dockerfile, add this single line:

FROM timescale/timescaledb:latest-pg16

This tells Docker to use the latest TimescaleDB image that's compatible with PostgreSQL 16. Using a specific tag like latest-pg16 is generally better than just latest because it prevents your setup from breaking unexpectedly when a new major version is released.

Step 3: The Initialization Script (schema.sql)

This is where you define your tables. The official PostgreSQL/TimescaleDB images are smart: any .sql file you put in the /docker-entrypoint-initdb.d/ directory inside the container will be executed on the first run to initialize the database.

Here’s a simple example for timescale-db/schema.sql. We'll create a table for sensor data and immediately turn it into a TimescaleDB hypertable, which is the magic that makes it so fast.

-- Create our main table for sensor readings
CREATE TABLE sensor_data (
    time TIMESTAMPTZ NOT NULL,
    device_id TEXT,
    metric_name TEXT,
    metric_value DOUBLE PRECISION,
    PRIMARY KEY (time, device_id, metric_name)
);

-- Turn our table into a TimescaleDB hypertable
-- This partitions the data by 'time' for massive performance gains
SELECT create_hypertable('sensor_data', 'time');

-- We can even add some indexes for faster queries
CREATE INDEX idx_device_metric ON sensor_data (device_id, metric_name, time DESC);
CREATE INDEX idx_metric_time ON sensor_data (metric_name, time DESC);

You can put any tables, roles, or permissions you need in this file.

Step 4: The docker-compose.yml File

This is the heart of our setup. It ties everything together. This file tells Docker Compose how to run the service, what environment variables to use, and what volumes to mount.

Place this in your root docker-compose.yml:

version: '3.8'

services:
  db:
    # 1. We tell Compose to build the image from our 'timescale-db' folder
    build: ./timescale-db
    container_name: timescale-db
    ports:
      # 2. Expose the PostgreSQL port 5432 to our local machine
      - "5432:5432"
    environment:
      # 3. Set the credentials for the database
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=timescaledb
      - POSTGRES_USER=pguser
    volumes:
      # 4. This is the magic! Mount our schema.sql into the init directory
      - ./timescale-db/schema.sql:/docker-entrypoint-initdb.d/schema.sql
      # 5. Mount a named volume for persistent data
      - db-data:/var/lib/postgresql/data

volumes:
  # 6. Define the named volume to ensure our data survives restarts
  db-data:
    name: timescale_db_data

Let's break down the important bits:

  • build: ./timescale-db: This tells Compose to look in the timescale-db folder and use the Dockerfile there.

  • environment: We define our database name, user, and password. The entrypoint script will use these to create the user and grant permissions.

  • volumes: This is the most critical part.

    • The first line mounts our local schema.sql file into the container's init directory.

    • The second line mounts a named volume called db-data to the place where PostgreSQL stores its data. This ensures that even if you stop and remove the container, your data remains safe.

Step 5: How to Run It

Now for the easy part. Open your terminal in the my-project directory and run:

docker compose up -d

The -d flag runs it in "detached" mode, so it runs in the background.

Your TimescaleDB instance is now running! The first time you run this, it will:

  1. Pull the timescale/timescaledb image.

  2. Start the container.

  3. Run your schema.sql script to create the timescaledb database and your sensor_data hypertable.

You can check that it's running with docker compose ps.

Step 6: How to Connect to It

You have two main ways to connect, depending on what you're doing.

1. From Your Local Machine (e.g., DBeaver, TablePlus, or psql)

You can connect directly from your host computer using the port we exposed (5432).

  • Host: localhost

  • Port: 5432

  • Database: timescaledb

  • User: pguser

  • Password: password

If you have psql (the PostgreSQL command-line tool) installed, you can connect with:

psql "postgres://pguser:password@localhost:5432/timescaledb"

2. From Another Docker Container (e.g., Your Backend Service)

If you add your Go backend or MQTT broker to this same docker-compose.yml file, you do not connect using localhost.

Instead, you use the service name from the compose file.

  • Host: db

  • Port: 5432 (the internal port, not the exposed one)

What's Next?

And that's it! You now have a persistent, auto-initialized TimescaleDB instance ready to receive data, all managed neatly with Docker Compose. You can now focus on building your application, knowing your database setup is solid, repeatable, and easy to share.

FreeDevTools

I’ve been building for FreeDevTools.

A collection of UI/UX-focused tools crafted to simplify workflows, save time, and reduce friction in searching tools/materials.

Any feedback or contributions are welcome!

It’s online, open-source, and ready for anyone to use.

👉 Check it out: FreeDevTools
⭐ Star it on GitHub: freedevtools

More from this blog

Ganesh Kumar

23 posts