Migrate from Tinybird Classic

Tinybird Forward introduces a new way of working with your data projects, with changes to APIs and CLI that may be incompatible with Tinybird Classic. If you're starting a new project, see the Get started guide.

Migrating doesn't move or copy your data. Your tables and their contents stay exactly where they are. Tinybird only restructures how your project is defined on the backend so it can use Forward's features.

Why migrate to Forward

Forward is a significant evolution of the Tinybird developer experience. Migrating unlocks the following benefits:

  • Deployments: Project iteration is easier. You ship a new version of the project, and Tinybird handles all the backfills, tables exchanges... automatically. No more tb push datasource_v2 and copy data from datasource_v1.
  • Git-native deployments and CI/CD: Every change ships through your repository. Use the built-in CI/CD workflows for automated validation, preview deployments, and safe rollouts to production.
  • Local development: Build and iterate against Tinybird Local with full parity to Tinybird Cloud. Test changes on your machine before they ever reach a shared environment. And easier to verify in your CI with the local image.
  • Faster, simpler testing: Fixture tests make it easier to define realistic scenarios and catch regressions before deploy, replacing the heavier regression and data quality test workflows from Classic.
  • Cleaner project structure: First-class connection files, secrets, and generic pipes replace ad-hoc include files and per-environment glue, so projects stay portable and reviewable in code.
  • Modern, consistent CLI: A redesigned tb command provides a unified workflow for local development, cloud deployments, and testing, with clearer commands and better feedback.
  • AI friendly: Use the agent skills npx skills add tinybirdco/tinybird-agent-skills to teach Tinybird best practices to your coding agent.
  • Active development: New features land in Forward first. Classic continues to receive critical fixes, but the roadmap (new connectors, deployment improvements, agent integrations) is Forward-only.

Considerations before migrating

Before migrating your workspace from Tinybird Classic, understand these key differences in Forward:

  • Development is files based. You edit the .pipe, .datasource... in a Cloud Branch or locally using Tinybird Local. Check Development Workflow for more details.
  • The following features have limitations or require changes:
FeatureStatusSolution/Alternative
BI ConnectorNot supportedUse the ClickHouse HTTP Interface instead. Most BI tools support ClickHouse HTTP connections.
Include filesNot supportedUse tb secret for connector credentials and generic pipes to reuse query logic. See Replace include files for migration steps.
CI/CD workflowsDifferent commandsForward uses different CLI commands. See CI/CD for details.
Testing strategyDifferent approachRegression tests and data quality tests are not supported in Forward. Fixture tests have been enhanced for easier test creation and management. See Test your project for details.
Resource-scoped tokens with :sql_filterNot supportedRemove all tokens using the :sql_filter suffix (e.g., DATASOURCES:READ:datasource_name:sql_filter) before migrating. Use JWTs instead.

If these changes work for your use case, continue reading to learn how to migrate.

Migration is permanent and cannot be reversed. After deploying with Forward, you cannot switch your workspace back to Classic.

Prepare to migrate

Both migration paths start from the same setup: install the Forward CLI, authenticate, and pull your project.

1

Install the Tinybird Forward CLI

Run the following command to install the Tinybird Forward CLI and the Tinybird Local container:

curl https://tinybird.co | sh

See Tinybird Local for more information.

The migration requires Forward CLI version 4.6.4 or later. Check your version with tb --version and run tb update if it's lower.

Managing CLI Versions: Having both Tinybird Classic and Forward CLIs installed can cause version conflicts since both use the tb command. To avoid these conflicts, consider:

  1. Using the uv Python package manager to keep both CLIs completely isolated (recommended):
# For Classic CLI
uvx --from tinybird-cli@latest tb

# For Forward CLI
uvx --from tinybird@latest tb
  1. Creating aliases in your shell configuration:
# Add to .bashrc or .zshrc
alias tb-classic="path/to/classic/tb"
alias tb-forward="path/to/forward/tb"
  1. Using separate virtual environments for each CLI version.

This ensures you use the correct CLI version for each operation during migration.

The following steps use the uv Python package.

2

Authenticate to your workspace

Authenticate to your workspace using the Classic CLI:

uvx --from tinybird-cli@latest tb auth --interactive

Follow the prompts to complete authentication.

3

Pull your project

Pull your datafiles from Tinybird Cloud with the Forward CLI:

tb --cloud pull

For connector-backed projects, this writes a .connection file per connector under connections/ and the data sources under datasources/, and for S3, GCS, Kafka, and DynamoDB it stores the credentials as secrets referenced with tb_secret(...). If a secret of the same name already exists, the pull reuses it and prints a warning so you can confirm the value matches before deploying.

Migrate to Forward

tb migrate-to-forward completes the migration end to end. It works for projects with and without connectors (S3, GCS, Kafka, DynamoDB). If it reports changes it can't reconcile, contact Tinybird support.

1

Audit the pulled connector files

If your project doesn't use connectors, skip to the next step.

Open each pulled .connection file and confirm it has the connector type, credentials reference, and region. The exact fields depend on the connector:

An access-key connection references the secret key with tb_secret(...) and keeps the access key ID and region inline:

connections/s3_events.connection
TYPE s3
S3_ACCESS_KEY AKIAEXAMPLE
S3_SECRET {{ tb_secret("s3_secret_s3_events") }}
S3_REGION eu-west-1

An IAM-role connection references the role ARN with tb_secret(...):

connections/s3_events.connection
TYPE s3
S3_ARN {{ tb_secret("s3_role_arn_s3_events") }}
S3_REGION eu-west-3

Then open each connector-backed .datasource file and confirm it references the connection by name and keeps the import settings. For DynamoDB, confirm it keeps the ReplacingMergeTree settings that updates and deletes rely on:

datasources/orders.datasource
ENGINE "ReplacingMergeTree"
ENGINE_SORTING_KEY "<partition_key>, <sort_key>"
ENGINE_VER "_timestamp"
ENGINE_IS_DELETED "_is_deleted"

IMPORT_CONNECTION_NAME 'ddb_orders'
IMPORT_TABLE_ARN 'arn:aws:dynamodb:us-east-1:123456789012:table/orders'
IMPORT_EXPORT_BUCKET 'my-dynamodb-export-bucket'

For DynamoDB, use only the S3 bucket name in IMPORT_EXPORT_BUCKET, not an s3:// URI. Treat IMPORT_TABLE_ARN and IMPORT_EXPORT_BUCKET as immutable: the first Forward deployment should preserve the existing DynamoDB binding, not change it.

2

Run the migration

Run the migration command:

tb migrate-to-forward

This command deletes branches and releases, runs a deploy check, generates a tinybird.config.json file, switches the workspace to Forward, and deploys it. After it finishes, query a few endpoints and confirm ingestion continues.

If you have CI/CD configured, push the changes to your repository to complete the migration. See CI/CD for details on the CI workflow, CD workflow, and creating preview deployments.

The migration is complete! Your project will continue working as expected; you do not need to change your tokens, endpoint URLs, or anything else.

Common migration errors

Common errors and changes include (but are not limited to):

Replace include files

Include files are not supported in Forward. The fix depends on your use of include files:

  • If you use include files to store secrets, use tb secret to set secrets in your local and cloud environments.
  • If you use include files to reuse query logic, you can create generic pipes and reference them in your endpoint pipes. For example:
reusable_filters.pipe
NODE apply_params
SQL >
    %
    SELECT * FROM my_datasource
    WHERE
        tenant_id = {{ String(tenant) }}
        AND date BETWEEN {{ Date(start_date) }} AND {{ Date(end_date) }}
my_endpoint.pipe
NODE endpoint
SQL >
    %
    SELECT * FROM reusable_filters

TYPE endpoint

Next steps

Updated