Amazon Redshift Serverless: Setup and Tuning
Chat2DB TeamRedshift Serverless removes the cluster from Amazon Redshift. There are no nodes to choose, no cluster to resize, and no charge when nothing is running. You define a capacity range, point queries at an endpoint, and pay for what you consume.
That is genuinely simpler than provisioned Redshift, but "serverless" does not mean "no configuration". Two settings — base capacity and usage limits — determine both your bill and your query latency.
The two objects you need
Redshift Serverless has exactly two concepts.
A namespace is the storage and identity layer: databases, tables, users, snapshots, IAM roles, encryption keys. Your data lives here.
A workgroup is the compute layer: capacity settings, VPC and subnet placement, security groups, endpoint. Queries run here.
They are separate so you can attach several workgroups to one namespace — for example a small workgroup for BI dashboards and a larger one for nightly ETL, both reading the same tables without competing.
aws redshift-serverless create-namespace \
--namespace-name analytics-ns \
--db-name analytics \
--admin-username admin \
--admin-user-password "$REDSHIFT_PASSWORD" \
--iam-roles arn:aws:iam::123456789012:role/RedshiftServerlessRole
aws redshift-serverless create-workgroup \
--workgroup-name analytics-wg \
--namespace-name analytics-ns \
--base-capacity 32 \
--max-capacity 128 \
--publicly-accessible false \
--subnet-ids subnet-aaa subnet-bbb subnet-ccc \
--security-group-ids sg-0123456789Note the three subnets. Redshift Serverless requires subnets in at least three availability zones with enough free IP addresses; this is the most common failure during first setup.
How billing works
Capacity is measured in Redshift Processing Units (RPUs). You are billed per RPU-second while queries run, with a 60-second minimum per session, and nothing at all while idle.
--base-capacity is the starting point (8 to 512 RPUs, in steps of 8). --max-capacity caps how far it will scale up under load.
The 60-second minimum is the detail that surprises people. A dashboard firing one 3-second query per minute never lets the workgroup go idle, so you are billed close to continuously. Batching those queries or accepting a slower dashboard refresh can cut the bill substantially.
Base capacity affects both cost and latency. A higher base gives more parallelism and faster cold queries but costs more per second. Too low, and queries queue and scaling kicks in — which itself takes time.
Practical starting points:
- Development or light BI: base 8–16 RPUs
- Mixed BI plus moderate ETL: base 32–64 RPUs
- Heavy concurrent ETL: base 64–128 RPUs
Start low and raise it based on measured queue time rather than guessing upward.
Put a usage limit in place first
Do this before anyone runs a query. Without a limit, a single bad query joining two large tables without a filter can scale to max capacity and stay there.
aws redshift-serverless create-usage-limit \
--resource-arn arn:aws:redshift-serverless:us-east-1:123456789012:workgroup/analytics-wg \
--usage-type serverless-compute \
--amount 500 \
--period monthly \
--breach-action deactivate--breach-action accepts log, emit-metric or deactivate. Use emit-metric with a CloudWatch alarm in production so you get paged rather than cut off, and deactivate in development where a hard stop is the safer default.
Loading data
Loading is unchanged from provisioned Redshift. COPY from S3 remains the fastest path:
COPY sales.fact_orders
FROM 's3://my-bucket/exports/orders/'
IAM_ROLE 'arn:aws:iam::123456789012:role/RedshiftServerlessRole'
FORMAT AS PARQUET;For CSV, be explicit about the awkward parts rather than relying on defaults:
COPY sales.fact_orders
FROM 's3://my-bucket/exports/orders.csv.gz'
IAM_ROLE 'arn:aws:iam::123456789012:role/RedshiftServerlessRole'
CSV
GZIP
IGNOREHEADER 1
DATEFORMAT 'auto'
TIMEFORMAT 'auto'
NULL AS '\\N'
MAXERROR 100;When a load fails, the error table tells you exactly which row and column:
SELECT starttime, filename, line_number, colname, type, raw_field_value, err_reason
FROM sys_load_error_detail
ORDER BY starttime DESC
LIMIT 20;Split large files into multiples of the slice count so every slice does equal work — many medium files load far faster than one enormous file.
Table design still matters
Serverless removes cluster management, not physical design. Sort keys and distribution keys have the same impact they always did.
CREATE TABLE sales.fact_orders (
order_id bigint,
customer_id bigint,
product_id bigint,
order_ts timestamp,
amount numeric(14,2),
region varchar(32)
)
DISTSTYLE KEY
DISTKEY (customer_id)
COMPOUND SORTKEY (order_ts, region);The sort key enables zone-map pruning: Redshift stores min/max values per block and skips blocks that cannot match. A filter on order_ts then reads a fraction of the table. Put the column you filter on most often first.
The distribution key controls how rows spread across slices. Choosing a column you join on frequently co-locates matching rows and avoids redistributing data across the network. Choosing a low-cardinality column instead creates skew, where one slice holds most rows and everything waits on it.
Let Redshift decide the encoding rather than guessing:
ANALYZE COMPRESSION sales.fact_orders;Auto table optimization adjusts sort and distribution keys over time based on observed queries, and it is on by default. It handles the common cases; explicit keys are still worth setting for tables you understand well.
Monitoring
The SYS_* views are the serverless equivalent of the older STL_* tables. Slowest recent queries:
SELECT query_id,
user_id,
elapsed_time / 1000000.0 AS seconds,
queue_time / 1000000.0 AS queue_seconds,
status,
LEFT(query_text, 120) AS sql_preview
FROM sys_query_history
WHERE start_time > DATEADD(hour, -24, GETDATE())
AND elapsed_time > 10000000 -- microseconds; 10 s
ORDER BY elapsed_time DESC
LIMIT 20;Consistently non-zero queue_seconds means base capacity is too low for your concurrency.
Track actual RPU consumption to see whether your capacity settings match reality:
SELECT DATE_TRUNC('hour', start_time) AS hour,
SUM(compute_seconds) AS rpu_seconds,
COUNT(*) AS queries
FROM sys_serverless_usage
WHERE start_time > DATEADD(day, -7, GETDATE())
GROUP BY 1
ORDER BY 1 DESC;Find the queries scanning the most data — usually the ones missing a sort key filter:
SELECT query_id,
SUM(bytes_scanned) / 1024 / 1024 / 1024 AS gb_scanned,
LEFT(MAX(query_text), 100) AS sql_preview
FROM sys_query_detail d
JOIN sys_query_history h USING (query_id)
WHERE h.start_time > DATEADD(day, -1, GETDATE())
GROUP BY query_id
ORDER BY gb_scanned DESC
LIMIT 20;Querying the lake with Spectrum
Redshift Spectrum works with Serverless, letting you query S3 data without loading it — useful for cold historical data you rarely touch.
CREATE EXTERNAL SCHEMA spectrum_schema
FROM DATA CATALOG
DATABASE 'my_glue_db'
IAM_ROLE 'arn:aws:iam::123456789012:role/RedshiftServerlessRole';
-- Join warehouse data to lake data in one query
SELECT d.customer_name,
SUM(a.amount) AS archived_revenue
FROM spectrum_schema.orders_archive a
JOIN sales.dim_customer d ON d.customer_id = a.customer_id
WHERE a.order_year = 2023
GROUP BY d.customer_name;Keep the external data partitioned and in Parquet. Spectrum bills by bytes scanned, so an unpartitioned CSV archive is expensive to query repeatedly.
Serverless or provisioned?
Serverless fits intermittent or unpredictable workloads, development environments, teams without a dedicated Redshift administrator, and anything where idle time is a meaningful fraction of the day.
Provisioned fits consistently high utilization — roughly 60% or more of the day at load — where reserved instance pricing beats per-second billing, workloads needing very fine-grained WLM control, or clusters above 512 RPU equivalent.
The rule of thumb: if the warehouse is busy less than half the time, serverless usually wins. Run both for a month against the same workload if the decision is close; the cost difference is easy to measure and hard to predict.
Connecting
The endpoint appears in the workgroup description:
aws redshift-serverless get-workgroup \
--workgroup-name analytics-wg \
--query 'workgroup.endpoint.address' --output textStandard PostgreSQL-protocol drivers work on port 5439. Any Postgres-compatible client connects, though Redshift-aware clients handle the dialect differences better. Chat2DB (opens in a new tab) supports Redshift with schema browsing and AI-assisted SQL generation, and runs in the browser at app.chat2db.ai (opens in a new tab).
Prefer temporary IAM credentials over a static password for applications:
aws redshift-serverless get-credentials \
--workgroup-name analytics-wg \
--db-name analytics \
--duration-seconds 3600Summary
Redshift Serverless is provisioned Redshift with the cluster removed and per-second billing added. Create a namespace for data and a workgroup for compute, set base capacity from measured queue times rather than intuition, and — before anything else — create a usage limit so one runaway query cannot scale to max capacity for a week.
Everything you know about Redshift table design still applies. Sort keys enable block pruning, distribution keys avoid network redistribution, and the SYS_* views tell you which queries are costing you money.
