Deployment guide

TimescaleDB

Overview

TimescaleDB Community is the open-source PostgreSQL extension purpose-built for time-series data. This image runs it on PostgreSQL 16 with password authentication enabled and unique credentials generated on the first boot of every instance. TimescaleDB Community is distributed under the Apache 2.0 licence, and this image does not ship the Timescale License (TSL) edition.

This is a headless image. You administer it over SSH with the psql shell, and applications connect with any standard PostgreSQL driver.

What you get:

  • PostgreSQL 16, installed from the official PostgreSQL Global Development Group (PGDG) repository
  • TimescaleDB 2 Community Edition, installed from the official Timescale package repository as a shared_preload_libraries extension
  • scram-sha-256 password authentication switched on for both local and remote connections
  • A dedicated SecureAnyCloud application database, created at build time with the TimescaleDB extension already enabled, so hypertables, continuous aggregates and retention policies are available immediately
  • A one-shot first-boot service that rotates the postgres superuser password and the SecureAnyCloud application role password to fresh random values, unique to that instance, and writes them to /root/timescaledb-credentials.txt, a file that only the root user can read. No shared or default database credentials ship in the image
  • PostgreSQL data, including hypertable chunks, under /var/lib/postgresql, mounted as a dedicated EBS volume separate from the operating system disk, so storage can be grown, snapshotted and backed up independently of the root disk
  • A small nginx page on port 80 so the AWS Marketplace HTTP health check can confirm the instance is reachable. The page does not proxy to PostgreSQL. The database listens directly on port 5432 and is reached with psql or any standard PostgreSQL driver
  • 24/7 SecureAnyCloud support

Prerequisites

Before you deploy this image, make sure you have:

  • An Amazon Web Services account where you can launch EC2 instances
  • IAM permissions to launch instances, create security groups and subscribe to AWS Marketplace products
  • An EC2 key pair in the target Region for SSH access to the instance
  • A VPC and subnet in the target Region, with a security group that allows inbound port 22 from your management network and, if you connect remotely, port 5432 from your application tier
  • The AWS CLI (version 2) installed locally, if you plan to deploy from the command line

Recommended instance type: m5.large (2 vCPU, 8 GB RAM) or larger. PostgreSQL with TimescaleDB benefits from generous RAM for the shared buffer cache and from fast attached storage. The included timescaledb-tune utility sizes memory settings for the live host the first time you run it.

1

Launch the instance from AWS Marketplace

Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images, choose AWS Marketplace AMIs and search for TimescaleDB. Select the SecureAnyCloud listing and choose Select, then choose Continue on the subscription summary.

Pick an instance type of m5.large or larger. Choose your EC2 key pair under Key pair (login). Under Network settings, select your VPC and subnet, then create or select a security group that allows inbound port 22 from your management network. Add port 5432 if you plan to connect from outside the instance.

Leave the root volume at the default size or larger. The PostgreSQL data volume is attached automatically from the image.

Select Launch instance. First-boot initialisation rotates the database passwords and re-enables the TimescaleDB extension. It takes a minute or two after the instance state becomes Running and the status checks pass.

2

Launch the instance from the AWS CLI

Run the following command to launch an instance from the SecureAnyCloud TimescaleDB Marketplace AMI into an existing subnet and security group. Replace <ami-id> with the AMI ID shown on the Marketplace listing and <key-name> with your EC2 key pair name. Replace <subnet-id> with your subnet ID and <security-group-id> with a security group that opens inbound port 22.

aws ec2 run-instances \
  --image-id <ami-id> \
  --instance-type m5.large \
  --key-name <key-name> \
  --subnet-id <subnet-id> \
  --security-group-ids <security-group-id> \
  --metadata-options HttpTokens=required \
  --block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":30,"VolumeType":"gp3"}}]' \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=timescaledb-01}]'

On success, the command prints a JSON document. Note the instance ID. When the instance is running, retrieve its public address with aws ec2 describe-instances --instance-ids <instance-id> --query "Reservations[].Instances[].PublicIpAddress" --output text.

3

Connect over SSH

Connect over SSH with the key pair you selected and the public IP address from step 2. The SSH login user depends on the operating system of the AMI variant you launched.

For the TimescaleDB Community 2 on Ubuntu 24.04 variant, the SSH login user is ubuntu.

ssh <login-user>@<public-ip>

Wait until the instance has passed both EC2 status checks before you connect. The first-boot service runs before the SSH daemon is fully usable, so the database is initialised by the time you can log in.

4

Retrieve the generated passwords

The first-boot service generates fresh postgres and SecureAnyCloud passwords for this instance. It writes them, with sample connection commands, to /root/timescaledb-credentials.txt, which only the root user can read. Display the file from your SSH session:

sudo cat /root/timescaledb-credentials.txt

The file looks like this, with passwords unique to your instance:

# TimescaleDB Community on PostgreSQL 16 — generated on first boot by
# timescaledb-firstboot.service. These passwords are unique to this instance. port=5432 default_database=sac default_role=sac postgres.user=postgres postgres.password=<TIMESCALEDB_POSTGRES_PASSWORD> sac.user=sac sac.password=<TIMESCALEDB_APP_PASSWORD> sample_connect_app=PGPASSWORD='<TIMESCALEDB_APP_PASSWORD>' psql -h 127.0.0.1 -U sac -d sac

Reserve the postgres superuser for database administration. For normal application traffic, use the SecureAnyCloud role against the SecureAnyCloud database.

5

Confirm the service and the listener

PostgreSQL runs under systemd as the postgresql service and starts automatically on boot. Confirm that it is active:

systemctl is-active postgresql

The command prints active. Next, confirm that PostgreSQL is bound on its default port:

ss -tln | grep 5432

You should see a listening socket on every interface (*:5432).

Finally, check the nginx health-check page on port 80. It should answer 200:

curl -sI http://127.0.0.1/ | head -1
6

Connect with psql and confirm the TimescaleDB extension

Open a local interactive psql session as the SecureAnyCloud role against the SecureAnyCloud database. Replace <password> with the value from /root/timescaledb-credentials.txt:

PGPASSWORD='<password>' psql -h 127.0.0.1 -U sac -d sac

Confirm that the TimescaleDB extension is enabled in this database. The screenshot below shows a complete session that opens psql, prints the server version and the TimescaleDB extension version, and exits.

To verify the extension non-interactively from your SSH session, query pg_extension:

PGPASSWORD='<password>' psql -h 127.0.0.1 -U sac -d sac \
  -c "SELECT extname, extversion FROM pg_extension WHERE extname = 'timescaledb';"

The query returns the extension name and version:

extname | extversion -------------+------------ timescaledb | 2.18.2 (1 row)
7

Create a hypertable

A hypertable is a PostgreSQL table that TimescaleDB partitions automatically across time. Run the following block inside psql as the SecureAnyCloud role. It creates a metrics table, converts it into a hypertable by time, inserts a handful of rows and counts them. It mirrors the session in the screenshot above and produces the screenshot below.

CREATE TABLE metrics ( time TIMESTAMPTZ NOT NULL, sensor_id INT NOT NULL, temperature DOUBLE PRECISION ); SELECT create_hypertable('metrics', by_range('time')); INSERT INTO metrics VALUES (NOW(), 1, 23.5), (NOW() - INTERVAL '1 hour', 1, 22.8), (NOW() - INTERVAL '2 hours', 2, 24.1); SELECT COUNT(*) FROM metrics;

create_hypertable returns the hypertable identifier and creates an initial chunk per time range. Later inserts land in the chunk that matches their time value.

8

Run a time-series query

The signature TimescaleDB function is time_bucket. It groups time-stamped rows into fixed intervals, so you can roll metrics up by minute, hour, day or any interval. Run the following query against the metrics table from the previous step.

SELECT time_bucket('1 hour', time) AS bucket, COUNT(*) AS samples, AVG(temperature)::numeric(5,2) AS avg_temp FROM metrics GROUP BY bucket ORDER BY bucket;

The query returns one row per hour bucket, with the sample count and the average temperature for that bucket. The time_bucket function works on any TimescaleDB hypertable and on any standard PostgreSQL table with a TIMESTAMPTZ column.

9

Add a retention policy

Time-series data ages out quickly. TimescaleDB's add_retention_policy schedules a background job that drops hypertable chunks older than a chosen interval, which frees storage automatically. Add a retention policy that drops chunks older than thirty days:

SELECT add_retention_policy('metrics', INTERVAL '30 days');

List the active TimescaleDB jobs to confirm that the policy is registered:

SELECT job_id, application_name, schedule_interval, hypertable_name FROM timescaledb_information.jobs WHERE proc_name = 'policy_retention';

The query returns a row for the retention policy on the metrics hypertable. Type \q to leave the psql shell.

To drop the retention policy later, call remove_retention_policy('metrics'). To inspect the chunks the policy will act on, query show_chunks('metrics', older_than => INTERVAL '30 days').

10

The PostgreSQL data volume

PostgreSQL data, including TimescaleDB chunks, is stored under /var/lib/postgresql, a dedicated EBS volume separate from the operating system disk. Confirm the mount:

findmnt /var/lib/postgresql

The output shows that /var/lib/postgresql is its own ext4 filesystem on a separate device:

TARGET SOURCE FSTYPE OPTIONS /var/lib/postgresql /dev/nvme1n1 ext4 rw,relatime

Because the data directory is on its own volume, you can snapshot it with Amazon EBS on its own schedule and grow it independently of the root volume. Check the available space at any time with:

df -h /var/lib/postgresql
11

Manage the PostgreSQL service

systemd manages PostgreSQL, and the service starts automatically on boot.

Check the service status:

systemctl status postgresql --no-pager

Stop, start and restart the service when needed:

sudo systemctl stop postgresql
sudo systemctl start postgresql
sudo systemctl restart postgresql

PostgreSQL log files live under /var/log/postgresql. The main cluster log is /var/log/postgresql/postgresql-16-main.log. Review it first when you diagnose a startup or runtime problem:

sudo tail -f /var/log/postgresql/postgresql-16-main.log

When you change instance type, re-run the memory and concurrency tuning by invoking timescaledb-tune, then restart the service:

sudo timescaledb-tune --quiet --yes sudo systemctl restart postgresql
12

Backups

The data to back up on a PostgreSQL instance with TimescaleDB is the entire SecureAnyCloud database. The pg_dump utility writes a logical backup that any PostgreSQL server with the TimescaleDB extension installed can restore. From an SSH session, take a compressed dump:

PGPASSWORD='<postgres-password>' pg_dump -h 127.0.0.1 -U postgres -F c -f /var/backups/sac-$(date +%F).dump sac

Archive the dump file to durable storage, for example an Amazon S3 bucket. To restore on a fresh instance, run:

PGPASSWORD='<postgres-password>' pg_restore -h 127.0.0.1 -U postgres -d sac /var/backups/sac-<date>.dump

Because /var/lib/postgresql is a dedicated EBS volume, you can also snapshot the volume for a point-in-time copy of the whole cluster. Stop the PostgreSQL service first.

13

Connect from a remote application

By default, the security group on the instance allows inbound traffic on port 22 for SSH and port 80 for the Marketplace health check. To connect from a remote application, add an inbound rule for TCP port 5432 from your application tier, either in the AWS console or with:

aws ec2 authorize-security-group-ingress \
  --group-id <security-group-id> \
  --protocol tcp --port 5432 \
  --cidr <application-cidr>

Replace <application-cidr> with the CIDR of the network that will connect to the database. Avoid 0.0.0.0/0 in production.

The pg_hba.conf on the image accepts remote scram-sha-256 authenticated connections. Use the SecureAnyCloud role and password from /root/timescaledb-credentials.txt.

Screenshots

Creating a TimescaleDB hypertable from a standard PostgreSQL table with create_hypertable().

Running a time_bucket() aggregate over a TimescaleDB hypertable for time-series rollups.

Adding a TimescaleDB add_retention_policy() to drop hypertable chunks older than a given interval.

Support

SecureAnyCloud provides 24/7/365 expert technical support for this image, with a guaranteed response within 24 hours and a one-hour average for critical issues. Contact sales@secureanycloud.com.

For general TimescaleDB and PostgreSQL administration questions, see the official documentation at https://docs.timescale.com/ and https://www.postgresql.org/docs/16/.

Deploy on AWS

Launch TimescaleDB Community on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS