Storage ID Usage
Use this guide when you want to control where table data is stored, pick storage per table, and understand user-table routing behavior.
For MinIO-specific S3-compatible setup, see MinIO (S3-Compatible).
Create storage backends first
Before using STORAGE_ID in CREATE TABLE, the storage must exist in system.storages.
Filesystem storage
CREATE STORAGE local_archive TYPE filesystem NAME 'Local Archive' DESCRIPTION 'Filesystem storage for archived tables' PATH './data/storage/local-archive' SHARED_TABLES_TEMPLATE 'shared/{namespace}/{tableName}' USER_TABLES_TEMPLATE 'users/{namespace}/{tableName}/{userId}';PATH is supported for filesystem storage. BASE_DIRECTORY is also accepted.
S3 storage
CREATE STORAGE s3_prod TYPE s3 NAME 'Production S3' DESCRIPTION 'S3 bucket for production data' BUCKET 'my-kalamdb-prod-bucket' REGION 'us-east-1' SHARED_TABLES_TEMPLATE 'shared/{namespace}/{tableName}' USER_TABLES_TEMPLATE 'users/{namespace}/{tableName}/{userId}';For S3, you can use either:
BUCKET 'bucket-name'(optionally withREGION)BASE_DIRECTORY 's3://bucket/prefix'
Useful checks:
SHOW STORAGES;SELECT storage_id, storage_type, storage_name FROM system.storages;Use STORAGE_ID in CREATE TABLE
KalamDB accepts STORAGE_ID in WITH (...) table options.
Shared table with explicit storage
CREATE TABLE app.config ( key TEXT PRIMARY KEY, value TEXT) WITH ( TYPE = 'SHARED', STORAGE_ID = 's3_prod');User table with explicit storage and flush policy
CREATE TABLE app.messages ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), content TEXT, created_at TIMESTAMP DEFAULT NOW()) WITH ( TYPE = 'USER', STORAGE_ID = 'local_archive', FLUSH_POLICY = 'rows:5000');If STORAGE_ID is omitted
If omitted, KalamDB resolves storage to local by default.
CREATE TABLE app.metrics ( id BIGINT PRIMARY KEY, value DOUBLE) WITH (TYPE = 'SHARED');Verify table storage metadata
Inspect table metadata in system.schemas:
SELECT namespace_id, table_name, table_type, storage_id, use_user_storageFROM system.schemasWHERE namespace_id = 'app' AND is_latest = trueORDER BY table_name;Per-user storage routing (USE_USER_STORAGE)
For USER tables, you can enable per-user storage routing metadata:
CREATE TABLE app.geo_events ( event_id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), payload JSON, created_at TIMESTAMP DEFAULT NOW()) WITH ( TYPE = 'USER', STORAGE_ID = 's3_prod', USE_USER_STORAGE = true);Rules:
USE_USER_STORAGEis valid only forTYPE = 'USER'STORAGE_IDremains fallback storage for the table- User preference fields exist in
system.users(storage_mode,storage_id)
Changing storage per user (current status)
Current SQL behavior:
- DML against
system.*tables is blocked ALTER USERcurrently supports onlySET PASSWORD,SET ROLE, andSET EMAIL
So there is no public SQL command yet to directly update a user’s storage_mode or storage_id.
Current developer workflow:
- Set table-level routing with
STORAGE_ID(optionallyUSE_USER_STORAGE) - For user-level overrides, use internal/admin backend flows
Common failures
Unknown storage ID
CREATE TABLE app.bad_example ( id INT PRIMARY KEY) WITH (TYPE = 'SHARED', STORAGE_ID = 'does_not_exist');Expected error pattern:
Storage 'does_not_exist' does not existInvalid USE_USER_STORAGE on non-user table
CREATE TABLE app.bad_shared ( id INT PRIMARY KEY) WITH (TYPE = 'SHARED', USE_USER_STORAGE = true);Expected error pattern:
USE_USER_STORAGE is only supported for USER tables