Skip to content
Stand with Ukraine flag

Database Layer

ThingsBoard uses a two-tier database architecture: PostgreSQL handles all entities and attributes, while time-series data goes to a configurable backend — PostgreSQL or Cassandra. PostgreSQL is always required. Cassandra is an optional addition for high-throughput time-series workloads.

Data TypeStorageConfigurable?
Entitiesdevices, assets, users, customers, dashboards, rule chains, device profiles, tenant profilesPostgreSQLNo — always PostgreSQL
Attributes — server, client, and shared attributes for all entitiesPostgreSQLNo — always PostgreSQL
Relations — entity-to-entity relationships (parent-child, contains, manages)PostgreSQLNo — always PostgreSQL
Alarms — alarm records, acknowledgements, clear eventsPostgreSQLNo — always PostgreSQL
Time-series — telemetry key-value pairs with timestampsPostgreSQL or CassandraYes — DATABASE_TS_TYPE
Events — audit logs, rule engine debug events, lifecycle eventsPostgreSQLNo — always PostgreSQL (optionally a dedicated PostgreSQL instance)

Set the time-series backend with DATABASE_TS_TYPE:

BackendDATABASE_TS_TYPEWrite ThroughputStorage EfficiencyBest for
PostgreSQLsqlUp to 5–10K data points/secBaselineSmall deployments, simplicity
Cassandracassandra~1M data points/sec~5× better than PostgreSQLHigh-throughput production, large-scale IoT

The latest time-series value cache can use a separate backend from the full time-series history. Set DATABASE_TS_LATEST_TYPE independently (defaults to sql).

Cassandra is roughly 5× more storage-efficient than PostgreSQL for time-series data:

ScenarioPostgreSQL (×2 replication)Cassandra (RF=3)
1 TB raw telemetry~2 TB total~600 GB total

PostgreSQL stores all entities, attributes, relations, alarms, and events. When DATABASE_TS_TYPE=sql, it also stores time-series data. This section covers tuning PostgreSQL for both roles.

ThingsBoard uses HikariCP to manage PostgreSQL connections:

VariableDefaultDescription
SPRING_DATASOURCE_URLjdbc:postgresql://localhost:5432/thingsboardDatabase connection URL
SPRING_DATASOURCE_MAXIMUM_POOL_SIZE16Maximum connections in the pool

ThingsBoard batches writes to maximize PostgreSQL throughput. Instead of inserting one row at a time, it collects data points into batches and flushes them periodically.

Time-series batching (when DATABASE_TS_TYPE=sql):

VariableDefaultDescription
SQL_TS_BATCH_SIZE10000Maximum data points per batch
SQL_TS_BATCH_MAX_DELAY_MS100Maximum wait time (ms) before flushing an incomplete batch
SQL_TS_BATCH_THREADS3Number of threads processing batches
SQL_TS_CALLBACK_THREAD_POOL_SIZE12Thread pool for write callbacks

Latest time-series values (used by dashboard widgets):

VariableDefaultDescription
SQL_TS_LATEST_BATCH_SIZE1000Maximum entries per batch
SQL_TS_LATEST_BATCH_MAX_DELAY_MS50Maximum flush delay (ms)
SQL_TS_LATEST_BATCH_THREADS3Batch processing threads

Attribute batching:

VariableDefaultDescription
SQL_ATTRIBUTES_BATCH_SIZE1000Maximum attributes per batch
SQL_ATTRIBUTES_BATCH_MAX_DELAY_MS50Maximum flush delay (ms)
SQL_ATTRIBUTES_BATCH_THREADS3Batch processing threads

PostgreSQL time-series tables are partitioned by time to keep queries fast and enable efficient data expiration. Set the partition granularity with SQL_POSTGRES_TS_KV_PARTITIONING:

ValuePartition sizeBest for
DAYS1 dayHigh write volume — more partitions but faster drops
MONTHS1 month (default)Most deployments
YEARS1 yearVery low write volume
INDEFINITENo partitioningNot recommended for production

Event tables (audit logs, rule engine debug events, lifecycle events) also use time-based partitioning:

VariableDefaultDescription
SQL_EVENTS_REGULAR_PARTITION_SIZE_HOURS168 (7 days)Regular events partition size
SQL_EVENTS_DEBUG_PARTITION_SIZE_HOURS1Debug events partition size (smaller for faster cleanup)

For high-traffic deployments, you can offload events (audit logs, rule engine debug events, lifecycle events) to a separate PostgreSQL database. This prevents event write spikes from affecting entity queries on the main database.

Enable with SPRING_DEDICATED_EVENTS_DATASOURCE_ENABLED=true, then configure:

VariableDefaultDescription
SPRING_EVENTS_DATASOURCE_URLjdbc:postgresql://localhost:5432/thingsboard_eventsEvents database connection URL
SPRING_EVENTS_DATASOURCE_USERNAMEpostgresEvents database username
SPRING_EVENTS_DATASOURCE_PASSWORDpostgresEvents database password
SPRING_EVENTS_DATASOURCE_MAXIMUM_POOL_SIZE4Connection pool size for events DB

When using Cassandra for time-series storage (DATABASE_TS_TYPE=cassandra), these are the most important configuration options.

VariableDefaultDescription
CASSANDRA_READ_CONSISTENCY_LEVELONERead consistency. ONE = read from nearest replica (fastest). QUORUM = majority of replicas (stronger consistency).
CASSANDRA_WRITE_CONSISTENCY_LEVELONEWrite consistency. ONE = acknowledge after one replica writes (fastest). QUORUM = acknowledge after majority writes.

For production with RF=3, consider ONE for reads and ONE for writes to maximize throughput. Use QUORUM when data consistency is more important than speed.

VariableDefaultDescription
CASSANDRA_QUERY_CONCURRENT_LIMIT1000Maximum concurrent Cassandra queries. Prevents overwhelming the cluster.
CASSANDRA_QUERY_BUFFER_SIZE200000Query buffer size. Queries exceeding the concurrent limit are buffered here.
CASSANDRA_QUERY_DISPATCHER_THREADS2Threads dispatching queries from the buffer
CASSANDRA_QUERY_CALLBACK_THREADS4Threads processing query results
CASSANDRA_QUERY_RESULT_PROCESSING_THREADS50Threads processing result sets
VariableDefaultDescription
CASSANDRA_URL127.0.0.1:9042Contact point(s) for the Cassandra cluster
CASSANDRA_LOCAL_DATACENTERdatacenter1Local datacenter name for DCAware routing
CASSANDRA_MAX_REQUESTS_PER_CONNECTION_LOCAL32768Maximum concurrent requests per local connection
CASSANDRA_COMPRESSIONnoneProtocol compression (reduces network bandwidth)

ThingsBoard automatically deletes old records based on a configured Time-to-Live (TTL). By default TTL is disabled — records are kept forever. Configuration depends on the storage backend.

PostgreSQL applies a single global TTL to all time-series data across all tenants and entities. A background cleanup job runs periodically and removes expired records.

VariableDefaultDescription
SQL_TTL_TS_ENABLEDtrueEnable automatic TTL cleanup
SQL_TTL_TS_EXECUTION_INTERVAL86400000 (1 day)How often the cleanup job runs, in milliseconds
SQL_TTL_TS_TS_KEY_VALUE_TTL0 (disabled)Retention period in seconds; 0 means records never expire

Cassandra supports granular TTL at three levels, from broadest to most specific:

  1. System level — the maximum TTL cap for the entire platform, set via TS_KV_TTL. No entity can have a longer retention than this value.

  2. Tenant level — a maximum TTL per tenant, configured in the tenant profile. Overrides the system default for that tenant.

  3. Entity level — a specific TTL per entity. Two options let you configure it:

    • Rule Chain: The Save Time Series rule node in your rule chain writes raw telemetry to the database. Its Default TTL field determines how long each record lives, and the TTL message-metadata property can override it on a per-message basis. Set a shorter TTL here when raw readings are only useful for a short operational window. See Save Time Series, Advanced settings for the full option set.
    • Calculated Fields: Any calculated field can carry its own TTL for the values it produces. The TTL property is exposed on the field when Output type is set to Time series and Output strategy is set to Process right away. See Calculated Fields, Output strategy for details.

    Together these enable multi-level retention. Raw telemetry can expire quickly, while derived values produced by calculated fields live on independently, each with the TTL that matches its own analytical purpose. Read more in the Efficient Aggregation and Data Retention in ThingsBoard blog post.

VariableDefaultDescription
TS_KV_TTL0 (disabled)Maximum system-level TTL in seconds; 0 means records never expire
TS_KV_PARTITIONINGMONTHSTime-series partition granularity: DAYS, MONTHS, YEARS, INDEFINITE

These TTL settings apply to operational data stored in PostgreSQL, regardless of the time-series backend:

VariableDefaultDescription
SQL_TTL_EVENTS_EVENTS_TTL0 (disabled)Events TTL in seconds
SQL_TTL_EVENTS_DEBUG_EVENTS_TTL604800 (7 days)Debug events TTL in seconds
SQL_TTL_EDGE_EVENTS_TTL2628000 (30 days)Edge events TTL in seconds
SQL_TTL_AUDIT_LOGS_SECS0 (disabled)Audit log TTL in seconds
SQL_TTL_NOTIFICATIONS_SECS2592000 (30 days)Notifications TTL in seconds

For the complete list of database environment variables, see Configuration Reference.