> ## Documentation Index
> Fetch the complete documentation index at: https://docs.goldsky.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from Mirror to Turbo

> Step-by-step guide for moving an existing Mirror pipeline to Turbo with minimal downtime.

[Turbo](/turbo-pipelines/introduction) is the recommended successor to [Mirror](/mirror/introduction). For a complete feature-by-feature comparison, see [Mirror vs. Turbo pipelines](/mirror-vs-turbo). This page walks you through the practical steps to migrate an existing Mirror pipeline.

<Note>
  In most cases (around 95%) a Mirror pipeline can be mapped 1:1 onto a Turbo pipeline. The remaining cases involve features that Turbo does not currently support — see [When you can't migrate yet](#when-you-cant-migrate-yet) before you start.
</Note>

## Before you start

* Install the latest CLI. The Turbo commands live under `goldsky turbo` — see the [Turbo CLI reference](/turbo-pipelines/cli-reference) and the [installation guide](/turbo-pipelines/cli).
* Read [Mirror vs. Turbo pipelines](/mirror-vs-turbo) so you know which sources, transforms, and sinks change shape.
* Export your existing Mirror pipeline definition with `goldsky pipeline get <name>` so you have the YAML to translate.

### When you can't migrate yet

Turbo doesn't (yet) replace every Mirror feature. Stay on Mirror if your pipeline depends on:

* **[Subgraph entity sources](/mirror/sources/subgraphs)** — Turbo only supports [dataset sources](/turbo-pipelines/sources/overview).
* **Elasticsearch, Timescale, DynamoDB, or Snowflake sinks** — see [Mirror vs. Turbo § Sinks](/mirror-vs-turbo#sinks) for the supported sink list on each product.
* **Mirror-only handler options** like `payload_columns` — see [Mirror vs. Turbo § Transforms](/mirror-vs-turbo#transforms).

If none of those apply, you're good to migrate.

## Step 1: Translate the YAML

Turbo's YAML is similar to Mirror's but drops a few fields and renames others.

### Top-level changes

| Mirror                                    | Turbo                                   |
| ----------------------------------------- | --------------------------------------- |
| `apiVersion: 3` (required)                | Remove — Turbo doesn't use `apiVersion` |
| `resource_size: s \| m \| l \| xl \| xxl` | Same — top-level `resource_size`        |
| `name`, `description`                     | Same                                    |

See the full Turbo schema in [Pipeline configuration](/turbo-pipelines/pipeline-config).

### Sources

Both products use `type: dataset`, but Turbo adds `start_block` / `end_block` for block ranges and uses `start_at: latest | earliest` for the starting position. Mirror's source-level Fast Scan `filter:` carries over to Turbo with the same SQL `WHERE`-clause syntax — see [Fast scan in Turbo EVM sources](/turbo-pipelines/sources/evm#fast-scan). See also:

* [Turbo EVM sources](/turbo-pipelines/sources/evm)
* [Turbo Solana sources](/turbo-pipelines/sources/solana)
* [Sources overview](/turbo-pipelines/sources/overview)

### Transforms

* **SQL transforms** — Turbo uses [Apache DataFusion](/turbo-pipelines/transforms/sql) instead of Flink. Most Mirror SQL works as-is; we've backported the common Flink functions as UDFs. A handful of less-used functions need rewrites — check [Turbo SQL functions reference](/turbo-pipelines/reference/sql-functions) for the supported list.
* **External HTTP handlers** — see [HTTP handler transforms](/turbo-pipelines/transforms/http-handler). `secret_name`, `one_row_per_request`, `payload_version`, `schema_override`, `batch_size`, and `batch_flush_interval` are all supported in Turbo. Mirror's `payload_columns` is the one option that doesn't carry over.
* **New transforms you can adopt** — [TypeScript/WASM transforms](/turbo-pipelines/transforms/typescript) (note the [sandbox constraints](/turbo-pipelines/transforms/typescript#not-available) — no Node APIs, no `fetch`, no `async`/`await`), [dynamic tables](/turbo-pipelines/transforms/dynamic-tables), and [throttle](/turbo-pipelines/transforms/throttle).

### Sinks

Most sink types have direct equivalents. Watch out for these specific renames and reshapes:

* **PostgreSQL secrets** — Mirror uses a JSON-object secret (`type`, `host`, `port`, ...). Turbo uses a Postgres connection string (`postgres://user:pass@host:port/db`). Recreate the secret with `goldsky secret create`. See [Turbo Postgres sink](/turbo-pipelines/sinks/postgres) and [Mirror secrets](/mirror/manage-secrets).
* **SQS** — Mirror uses `type: sqs`; Turbo uses `type: sqs_sink` and lets you pass `access_key_id` / `secret_access_key` inline. See [Turbo SQS sink](/turbo-pipelines/sinks/sqs).
* **S3** — In Mirror this goes through the [object storage sink](/mirror/sinks/object-storage); Turbo has a native [`s3_sink`](/turbo-pipelines/sinks/s3).
* **Kafka** — Turbo's [Kafka sink](/turbo-pipelines/sinks/kafka) adds `data_format` (required), `topic_partitions`, and `message_max_bytes`. `schema_registry_url` lives in the [Kafka secret](/turbo-pipelines/pipeline-config#secrets), not the sink config.
* **Webhooks** — Turbo's [webhook sink](/turbo-pipelines/sinks/webhook) supports `secret_name` (with `httpauth` secrets), `one_row_per_request`, `payload_version`, and `primary_key`.
* **Google Cloud Pub/Sub** — Turbo has a native [Pub/Sub sink](/turbo-pipelines/sinks/pubsub); Mirror does not.

### `U256` / `I256` values

EVM datasets contain `uint256` / `int256` values (e.g. `value`, `balance`, `amount`). The Postgres sink writes these as `NUMERIC(78, 0)` — see [256-bit integers in the Postgres sink](/turbo-pipelines/sinks/postgres#256-bit-integers-u256--i256). If you have existing Postgres tables created by a Mirror pipeline with a different column type, you'll need to migrate the column or drop and let Turbo recreate the table.

For the full matrix and per-sink tuning options see [Mirror vs. Turbo § Sinks](/mirror-vs-turbo#sinks) and [§ Sink tuning parameters](/mirror-vs-turbo#sink-tuning-parameters).

## Step 2: Validate the Turbo definition

Once you have a translated YAML file:

```bash theme={null}
goldsky turbo validate path/to/pipeline.yaml
```

Then use [live inspect](/turbo-pipelines/live-inspect) to see the data flowing through each step before you point it at production sinks:

```bash theme={null}
goldsky turbo inspect path/to/pipeline.yaml
```

A quick sanity check is to swap the real sink for the [blackhole sink](/turbo-pipelines/sinks/blackhole) during inspection.

## Step 3: Choose a cutover strategy

Mirror's processed-data state does **not** carry over to Turbo, so plan for one of the strategies below depending on how much double-write or downtime you can tolerate. Turbo itself is [at-least-once with checkpointing](/turbo-pipelines/sinks/overview), so make sure your sink is set up to deduplicate (typically via `primary_key`) before you cut over.

### Option A — Run both in parallel (recommended)

Best when your sink can tolerate duplicate writes (e.g. Postgres with primary keys, ClickHouse with `ReplacingMergeTree`, or any idempotent webhook).

1. Deploy the Turbo pipeline with `start_at: latest` in every dataset source so it begins at the chain tip.
2. Leave the Mirror pipeline running.
3. Once the Turbo pipeline is healthy and you see writes landing in your sink, pause the Mirror pipeline with `goldsky pipeline pause <name>`.
4. After a buffer period to confirm Turbo is stable, delete the Mirror pipeline.

You may see a small window of duplicate inserts during the overlap; upserts or unique constraints will absorb them.

### Option B — Block-number cutover

Best when your sink cannot dedupe and you want a clean handoff.

1. Pick a future block X for the cutover.
2. Edit the Mirror pipeline so it stops at block X (add a block-number filter in your SQL transform — Mirror sources don't support `end_block`).
3. Deploy the Turbo pipeline configured to start at block X+1 (use `start_block` on the source — see [EVM sources](/turbo-pipelines/sources/evm)).
4. Once Mirror reaches block X and Turbo picks up at X+1, pause and remove the Mirror pipeline.

### Option C — Cold cutover (downtime accepted)

If a short gap in your sink is fine, pause Mirror, deploy Turbo with `start_at: latest`, and accept the brief gap. Simplest, but you lose the blocks in between.

## Step 4: Decommission Mirror

After the Turbo pipeline has been running cleanly for your chosen verification window:

```bash theme={null}
goldsky pipeline delete <mirror-pipeline-name>
```

Mirror secrets can stay in place if they're shared, or be removed via `goldsky secret delete`.

## Common gotchas

* **Solana, Bitcoin, Stellar, and NEAR sources** were removed from Mirror — they're Turbo-only now. See [Solana](/turbo-pipelines/sources/solana), [Bitcoin](/turbo-pipelines/sources/bitcoin), [Stellar](/turbo-pipelines/sources/stellar), and [NEAR](/turbo-pipelines/sources/near).
* **CLI namespace** — every command moves from `goldsky pipeline ...` to `goldsky turbo ...`. Update scripts and CI accordingly.
* **Job mode** — if your migration is actually a one-time backfill, consider [Turbo job mode](/turbo-pipelines/job-mode) instead of a streaming pipeline.

## Get help

If you hit anything that doesn't translate cleanly, reach out to support or your Goldsky contact — we're actively closing the remaining gaps and happy to help with the rewrite. For background on what differs between the two products, see [Mirror vs. Turbo pipelines](/mirror-vs-turbo) and the [Turbo FAQ](/turbo-pipelines/faq).
