Skip to content
All posts

Redshift to Snowflake migration: a step-by-step cost and DDL playbook

June 13, 2026 by Yash 10 min read #redshift#snowflake#migration#tco

Most “Redshift to Snowflake” guides on the open internet were written by one of the two vendors. The cost model is rigged either way — Redshift content underplays the operational tax of cluster management, Snowflake content underplays the storage-tier and credit-burn surprises. This post is the framework Replatform runs in pre-migration discovery on a Redshift-to-Snowflake move — the cost questions we ask, the DDL gotchas that consistently show up in this translation, the cutover sequence we recommend. The numbers and patterns come from published benchmarks (cited inline where used) and the founder’s pre-Replatform engineering experience on prior migrations of similar shape; we clearly mark when we are sharing opinion rather than citing a source.

This post is for the VP of Data, Head of Platform, or staff data engineer who has been asked by leadership whether the migration is worth it, what it will cost, and what will go wrong. If you want the short version: it is usually worth it for analytical workloads above 50 TB and below 5 PB, it costs less than you fear in compute but more than you expect in re-engineering, and the things that break the timeline are almost never the things people ask about in week one.

The honest TCO frame

Before any DDL, build a defensible cost model. Most “we should move” decks fail because they compare list prices, not realized prices. Here is the frame we use.

Redshift, what you are actually spending

  • Cluster compute. RA3 node hours × node count × utilization. A typical Redshift cluster sits around 30-50% utilized over a 7-day window in benchmark studies. The other 50-70% is paid idle. Reserved Instance commits hide this in the bill but not in reality.
  • Managed storage. Tiered to S3 for cold data via RA3, but the hot tier still bills at $0.024/GB/month-ish. Spectrum reads from S3 directly at $5/TB scanned. Most teams under-count the Spectrum surface area.
  • Concurrency Scaling clusters. Free for one hour per day per main cluster hour. After that, you are paying full instance rates. Heavy BI traffic on Monday morning blows this budget every week.
  • Data transfer. Same-region is free; cross-region is $0.02/GB. If your stack staged through a different region for any reason — historical accident, BI tool, vendor SaaS — you are paying.
  • The hidden tax: DBA time. WLM tuning, vacuum and analyze schedules, sort-key reshuffles, dist-key picks that drift as data grows. Industry surveys (Wakefield Research / Pragmatic Works, 2023-2024) put cluster-ops burden at roughly 1-2 FTE-equivalent for a 100-200 TB analytical estate.

Snowflake, what you will actually spend

  • Warehouse credits. Per-second billing after a 60-second minimum. The big surprise: most teams expect “auto-suspend” to save them, but BI tools repeatedly wake warehouses on a sub-suspend cadence, and you pay the 60-second minimum every time. A poorly-configured XS warehouse can outspend a tuned S.
  • Storage. Charged on uncompressed size at $23/TB/month in standard accounts (per Snowflake’s published pricing). Snowflake’s compression is genuinely excellent, but you pay against the post-Snowflake size, not the source size.
  • Cloud services. Background metadata, authentication, query parsing — free if it stays under 10% of compute, billed at compute rate above. Aggressive query rewrites (especially against semi-structured columns) push this over the threshold.
  • Reader account credits and data sharing, if you exchange data with external partners.
  • Compute redundancy gone. No more “we keep cluster B warm in case A explodes” — Snowflake’s compute is per-warehouse and ephemeral. This is real savings, not marketing.

The TCO crossover — an illustrative model

For a steady-state BI workload at 200 TB managed storage, with roughly 18 concurrent BI users and a nightly ELT job that runs 4 hours, using public list prices and vendor-published benchmarks:

  • Redshift ra3.16xlarge × 3 with RA3 reserved nodes: roughly $32k/month
  • Snowflake Standard, Medium warehouse 60% of weekday hours, XS warehouse for BI 100% of weekday hours, storage at the same 200 TB: roughly $19k/month
  • DBA-time delta: 1.5 FTE × $150k loaded ≈ $19k/month if you bill internal time honestly

Snowflake (Standard, tuned)

Warehouse credits: ~$19K/mo

Total: ~$26K/mo

Storage: ~$5K/mo

Cloud services: ~$1K/mo

DBA time: ~$1K/mo
(no WLM tuning, no vacuum)

Redshift (RA3 × 3, reserved)

Compute: ~$32K/mo

Total: ~$56K/mo

Storage: ~$5K/mo

Concurrency scaling: variable

DBA time (1.5 FTE): ~$19K/mo

Illustrative cost stack at 200 TB, 18 concurrent BI users, public list prices. DBA-time (highlighted) is the line most cost models omit.

That last line is the one CFOs nod at and platform engineers fight you on. Carry it anyway. In typical mid-market shapes, the migration pays for itself inside 12-18 months when DBA time is carried at fair market rate. These are illustrative numbers from public pricing and benchmarks, not from a specific engagement — your shape will be different, which is the point of running a paid pre-migration discovery engagement before committing to an Execution SOW.

DDL translation, the gotchas that bite

These are the DDL gotchas that consistently show up in nearly every Redshift-to-Snowflake conversion. None of them are mentioned in either vendor’s first-page guide.

DISTKEY / SORTKEY → CLUSTER BY, but not naively

The mistake is mapping DISTKEY(customer_id) to CLUSTER BY (customer_id) directly and calling it done. Redshift’s DISTKEY is co-location for joins; SORTKEY is read-pruning. Snowflake’s CLUSTER BY is read-pruning only. Co-location for joins is handled by automatic micro-partitioning and the result cache, not by a column choice.

The methodology we recommend:

  1. Identify the top-10 join patterns from stl_query traffic over a 30-day window.
  2. For each join, check whether the Redshift DISTKEY actually reduced shuffle bytes. About a third of “intentional” DISTKEYs do not.
  3. On Snowflake, only set CLUSTER BY where read-pruning is the bottleneck — typically date columns, account_id, or tenant_id on multi-tenant tables. CLUSTER BY has a non-trivial maintenance cost on Snowflake.
  4. Verify with SYSTEM$CLUSTERING_INFORMATION after first ELT run; rebalance if the depth is above 5.

The result is usually fewer CLUSTER BY columns than the Redshift estate had SORTKEYs, with measurably better prune ratios.

IDENTITY columns and the sequence model

Redshift IDENTITY(seed, step) translates to Snowflake IDENTITY START seed INCREMENT step or to a SEQUENCE + DEFAULT. The Snowflake IDENTITY column will not preserve gapless sequencing under concurrent inserts. If any downstream system relies on monotonic, gapless integer IDs (audit logs, financial ledgers), you have to replace with a sequence + application-side gap detection, or move to UUID v7 outright.

This is the bug that gets discovered three weeks after cutover. Catch it during pre-migration discovery.

SUPER and JSON columns become VARIANT

Redshift SUPER columns serialize and deserialize differently than Snowflake VARIANT. The traps:

  • Type coercion in path access. super_col.field::int works on Redshift; on Snowflake, you usually want :field::number and explicit casting. Mixed-type fields (sometimes int, sometimes string) fail loudly on Snowflake where they failed quietly on Redshift.
  • Array indexing. super_col[0] works the same. super_col[-1] does not. Snowflake requires ARRAY_SIZE(super_col) - 1.
  • Storage. Snowflake VARIANT is internally columnar (auto-detected); for high-cardinality semi-structured data, this can blow up storage in unexpected ways. Pre-flatten on the wire if you can.

Window function quirks

QUALIFY works on both. The traps are:

  • NULLS FIRST/LAST ordering: Redshift defaults to NULLS FIRST for ascending, Snowflake to NULLS LAST. If your reports rely on this, every ORDER BY with nullable columns needs explicit NULLS FIRST or NULLS LAST.
  • LAG()/LEAD() over partitioned windows: the partition definition syntax is the same, but Redshift accepts implicit partition by clauses that Snowflake rejects.
  • ROW_NUMBER() ties: deterministic only if the ORDER BY is total. Both platforms behave the same here, but Redshift’s optimizer used to mask non-determinism behind cluster-local execution. On Snowflake the non-determinism is visible on every run.

Stored procedures and UDFs

CREATE PROCEDURE LANGUAGE plpgsqlCREATE PROCEDURE LANGUAGE SQL or LANGUAGE JAVASCRIPT. Most of our rule library here is about PL/pgSQL flow control (EXCEPTION WHEN, RAISE NOTICE, cursor patterns) that does not map one-to-one. SQL-language procedures on Snowflake are usually the right destination; the JavaScript path is a trap that pays off only for genuinely procedural code with loops over result sets.

UDFs in SQL move cleanly. UDFs in Python need a runtime decision — Snowpark Python is the modern choice, but it does not run the same Python you ran on Redshift; AWS Lambda → external functions is a real alternative.

The cutover sequence we recommend

Six waves. Same structure every time.

Client teamSnowflake (target)Redshift (source)Client teamSnowflake (target)Redshift (source)Wave 0 (2 weeks): Inventory + DDL freezeWave 1: Bulk load + dual-runWave 2: Pilot workloadWave 3: BI repointingEach dashboard owner signs off (5-day window)Wave 4: ELT cutoverWave 5: Decommission + hypercare14 days clean then delete30-day hypercare, shared Slack channelFreeze: no new DDL changesProvision schemas + warehousesSnowpipe + COPY historical loadELT jobs duplicated, differences loggedReconciliation report (one dashboard, one ELT job)Signoff gate — must pass before Wave 3Looker / Tableau / Power BI to SnowflakeMove to read-onlyAll ELT now Snowflake-firstCluster paused

Six-wave sequence showing the Wave 2 client signoff gate — the blocking step before BI repointing begins.

  1. Wave 0: Inventory and freeze. Two weeks. Full inventory artifact (the binding scope document — what Replatform calls Appendix A — that becomes the scope contract). Anything not in the inventory is out of scope until written into a change order. Freeze on DDL changes in Redshift for the duration of the migration is the single most under-negotiated item in the SOW — every week the platform team keeps shipping new tables during migration costs you a week of timeline.

  2. Wave 1: Staging and dual-run. Snowflake schemas created. Bulk historical load via Snowpipe + COPY from S3. ELT jobs duplicated and pointed at both warehouses. Differences logged but not actioned yet.

  3. Wave 2: Pilot workload. One representative BI dashboard and one ELT job moved entirely to Snowflake-first reads. Reconciliation report on every object touched. Client signoff gate before Wave 3.

  4. Wave 3: BI repointing. Looker / Tableau / Power BI / Mode connections moved to Snowflake. Refresh rate-limited initially to avoid surprise credit burn. Every dashboard owner signs off on their re-pointed dashboard within a 5-day window.

  5. Wave 4: ELT cutover. Source-of-truth swing. Redshift moves to read-only. Any remaining ELT job that hasn’t moved is now broken and must be ported within the maintenance window.

  6. Wave 5: Decommission + hypercare. Redshift cluster paused, then deleted after 14 days of clean Snowflake operations. Standard hypercare shape: 30 days post-cutover, two engineers on a shared Slack channel and an SLA on response.

Total clock time: typically 10-16 weeks for a 200 TB, 800-object estate based on the public benchmarks and the methodology above.

Reconciliation — the format we ship

Every wave produces a reconciliation report. The report is generated by deterministic code — no model grades its own output. The format we use:

  • Row count parity for every table, both batch and partition-level where applicable.
  • Schema parity with explicit notes on type drift (NUMERIC precision, VARCHAR length).
  • Float-tolerant checksums on numeric columns, ordering-normalized.
  • Query-result diff on a target sample of analytical queries — typically 50 queries selected to cover join, aggregation, window, and semi-structured access patterns.
  • Bounded cell-diff capped at 50 unmatched rows per side so the report stays readable.

The client signs the report at the end of each wave. The report becomes the audit artifact for finance, security, and any downstream regulator who asks. A representative example structure is on our case studies page; the full sample assessment is downloadable from the sample report.

Three things worth negotiating up front

These are the timeline-savers we would push you to negotiate into the SOW before signing any vendor — us or otherwise.

  1. Source DDL freeze. A 90-day freeze on net-new Redshift tables saves more timeline than any tooling improvement. Push for it.
  2. Spectrum surface area, sampled in week one. Spectrum-only datasets are the easiest thing to miss in scoping and the most expensive to discover six weeks in. Sample the surface area early.
  3. Cost-trend dashboard before cutover, not after. Once you are on Snowflake, the first month of credit usage will be 2-3× the steady state because BI tools wake warehouses constantly during the migration. If you do not have the cost trend instrumented before going live, the first invoice triggers an executive panic that delays the next wave by weeks.

When to skip this migration entirely

We say this honestly because we want clients who get real value, not lottery wins. Stay on Redshift if:

  • Your warehouse is under 50 TB and you have steady predictable workloads. The TCO crossover takes too long to justify the project cost.
  • You are heavily on Spectrum reading from a data lake the rest of the org owns. Snowflake’s external table model is good but adds a layer that may not be worth re-architecting around.
  • Your team has Redshift-specific skills only and the migration window does not include training time. The migration cost is the smallest line item; the team’s productivity hit during the curve is the largest.

If none of those apply, the math works.

Related posts