# Cross-Environment Data Migration

This guide explains how to move workflow apps, chatflow apps, and their custom tool dependencies from one Dify environment to another.

The recommended best-practice path is:

1. Run the export wizard in the source environment.
2. Import the generated migration package in the target environment.

Use scripted export only when you need repeatable automation.

## What Gets Migrated

The migration package can contain:

- Workflow apps and advanced-chat apps.
- Custom API tool providers.
- Workflow tool providers.
- MCP tool providers when secrets are explicitly included.
- Dependency metadata for MCP tools, built-in tools, and plugin tools that must already exist or be configured in the target environment.

Only single source workspace exports are supported. The source and target workspace names do not need to match.

## Recommended Flow: Wizard Export + Import

Run the wizard from the source environment:

```bash
cd api
source .venv/bin/activate
uv run flask app-migration-wizard
```

The wizard asks you to select the source workspace, choose workflow/chatflow apps, discover referenced tools, set import behavior, and write the final migration package JSON.

Then copy the generated JSON package to the target environment and import it:

```bash
cd api
source .venv/bin/activate
uv run flask import-app-migration \
  --input migration-data-20260528-120000.json \
  --target-tenant "production Workspace"
```

This wizard + import combination is the recommended migration workflow because the wizard shows the available apps and tools, discovers app dependencies, summarizes the package before writing it, and uses the same export service as scripted export.

## Wizard Command

```bash
cd api
source .venv/bin/activate
uv run flask app-migration-wizard
```

The wizard has no CLI options. Its interactive inputs are:

- `Source tenant`: the source workspace to export from. Select one tenant by number.
- `App selection`: apps to export. Supported app types are `workflow` and `advanced-chat`. Enter `all`, one number, or comma-separated numbers.
- `Automatically export tools referenced by selected apps?`: recommended `yes`. This scans selected app graphs and automatically includes referenced custom API tools, workflow tools, and MCP tool references.
- `Export additional tools manually?`: optional. Use this when you need to migrate tools that are not referenced by the selected apps.
- `Include secrets in output JSON?`: default `no`. When `no`, workflow/app DSL secrets are omitted or masked, API tool credentials are omitted, and MCP providers are exported as dependency metadata only. When `yes`, treat the output JSON as sensitive.
- `Create or reuse app API tokens during import?`: default `no`. When `yes`, import creates an app API token for imported apps that have none, or reuses an existing token.
- `Import ID strategy`: `preserve-id` or `generate-new-id`. Default is `preserve-id`.
- `Import conflict strategy`: `fail`, `skip`, or `update`. Wizard default is `update`.
- `Output path`: output migration package path. The default is `migration-data-YYYYMMDD-HHMMSS.json`.
- `Overwrite existing output`: shown only if the selected output file already exists.
- `Write migration package?`: final confirmation after the wizard prints the summary.

## Import Command

```bash
cd api
source .venv/bin/activate
uv run flask import-app-migration --input migration-package.json --target-tenant "target Workspace"
```

Options:

- `--input`: required. Path to the migration package JSON generated by the wizard or scripted export.
- `--target-tenant`: optional only when package metadata already contains a target tenant. Target workspace name or workspace UUID. This overrides package metadata and is recommended for reusable packages.
- `--operator-email`: optional. Email of the target-tenant account used as the import operator. If omitted, import uses the earliest owner account in the target tenant.
- `--id-strategy`: optional override for the package import option. Allowed values:
  - `preserve-id`: keep source app/tool IDs when supported. This is the recommended default for preserving workflow references across environments.
  - `generate-new-id`: let the target environment generate new IDs and rewrite references through the migration ID mapping.
- `--conflict-strategy`: optional override for the package import option. Allowed values:
  - `fail`: stop at the first existing target resource conflict. Previously committed resources are not rolled back.
  - `skip`: keep the existing target resource and skip importing that resource.
  - `update`: update the existing target resource in place.
- `--create-app-api-token-on-import`: optional override. Create or reuse app API tokens during import.
- `--no-create-app-api-token-on-import`: optional override. Do not create app API tokens during import.

Import prints a report that includes the resolved target tenant, operator, created/updated/skipped resources, unresolved dependencies, app API token counts, and ID mappings used to rewrite references.

## Optional Flow: Scripted Export

Scripted export is supported for automation. The recommended scripted path is:

1. Generate an export config template.
2. Edit the template.
3. Run scripted export.
4. Import the generated package.

Generate the template:

```bash
cd api
source .venv/bin/activate
uv run flask app-migration-template --output export-config.json
```

Edit `export-config.json`, then run:

```bash
cd api
source .venv/bin/activate
uv run flask export-app-migration \
  --input export-config.json \
  --output migration-package.json
```

Import it:

```bash
cd api
source .venv/bin/activate
uv run flask import-app-migration \
  --input migration-package.json \
  --target-tenant "production Workspace"
```

### Template Command

```bash
cd api
source .venv/bin/activate
uv run flask app-migration-template [--output export-config.json] [--overwrite]
```

Options:

- `--output`: optional. Path to write the scripted export config JSON template. If omitted, the template is printed to stdout.
- `--overwrite`: optional. Allows replacing an existing output file. Without this option, the command fails if `--output` already exists.

### Scripted Export Command

```bash
cd api
source .venv/bin/activate
uv run flask export-app-migration --input export-config.json --output migration-package.json
```

Options:

- `--input`: required. Path to the export config JSON.
- `--output`: required. Path to write the migration package JSON.
- `--overwrite`: optional. Allows replacing an existing output package. Without this option, the command fails if `--output` already exists.

### Export Config Fields

The generated template uses this shape:

```json
{
  "source_tenant": {
    "mode": "single",
    "id": "",
    "name": "admin's Workspace"
  },
  "apps": {
    "modes": ["workflow", "advanced-chat"],
    "ids": [],
    "all": true
  },
  "include_referenced_tools": true,
  "additional_tools": {
    "api_tools": [],
    "workflow_tools": [],
    "mcp_tools": []
  },
  "include_secrets": false,
  "import_options": {
    "create_app_api_token_on_import": false,
    "id_strategy": "preserve-id",
    "conflict_strategy": "fail"
  }
}
```

Fields:

- `source_tenant.mode`: must be `single`.
- `source_tenant.id`: optional source workspace UUID. Use this when workspace names are duplicated or when you want strict source selection.
- `source_tenant.name`: required source workspace name. When `source_tenant.id` is set, the ID and name must match.
- `apps.modes`: optional list of app modes for validation. Supported values are `workflow` and `advanced-chat`.
- `apps.ids`: app IDs to export when `apps.all` is `false`.
- `apps.all`: when `true`, export all supported apps in the selected source workspace. When `false`, export only `apps.ids`.
- `include_referenced_tools`: recommended `true`. Automatically discovers tools referenced by selected workflow/chatflow apps.
- `additional_tools.api_tools`: optional custom API tool provider names to export in addition to referenced tools.
- `additional_tools.workflow_tools`: optional workflow tool provider IDs to export in addition to referenced tools.
- `additional_tools.mcp_tools`: optional MCP provider IDs or server identifiers to export in addition to referenced tools.
- `include_secrets`: default `false`. When `false`, credentials are omitted and MCP providers are recorded as dependency metadata. When `true`, custom API credentials, workflow DSL secrets, and full MCP connection data can be written to the package.
- `import_options.create_app_api_token_on_import`: default `false`. Package-level default for import token creation.
- `import_options.id_strategy`: package-level default ID strategy. Allowed values are `preserve-id` and `generate-new-id`.
- `import_options.conflict_strategy`: package-level default conflict strategy. Allowed values are `fail`, `skip`, and `update`.

## Referenced Tools

Use automatic referenced-tool discovery whenever possible.

When enabled, Dify scans selected workflow/chatflow graphs and agent tool configs, then de-duplicates discovered custom API tools, workflow tools, and MCP tool references before export. This reduces the chance of importing an app whose workflow references missing providers.

Built-in and plugin tools are not serialized into the package. The migration report records them as dependency metadata; make sure the target environment has the required built-in or plugin tools installed and configured.

MCP providers are also dependency-only unless `include_secrets` is enabled. If MCP secrets are not exported, configure the corresponding MCP provider manually in the target workspace before running migrated workflows.

## Secret Handling

The safe default is `include_secrets: false`.

Only enable secret export when you have a controlled transfer path for the JSON package. With secrets enabled, the package may include API tool credentials, workflow/app DSL secret values, MCP server URLs, MCP headers, authentication data, and cached MCP tool lists.

## FAQ

### What happens when `include_secrets` is `false`?

This is the recommended default, but it means the migration package is intentionally incomplete for sensitive runtime configuration.

Important behavior:

- MCP tools are not exported as full tool providers. They are recorded as dependency metadata only.
- Custom API tool credentials are not exported. The provider schema can be exported, but credentials must be configured again in the target workspace.
- Workflow/app DSL secrets are omitted or masked. Any app variables, credentials, or secret-backed settings must be reviewed in the target workspace after import.
- Built-in and plugin tools are never serialized as custom migration data. They are recorded as dependencies only.

How to handle it:

- Before importing, install or enable the required built-in/plugin tools in the target environment.
- For MCP tools, manually create or configure the corresponding MCP provider in the target workspace before running migrated workflows.
- For custom API tools, open the imported provider in the target workspace and re-enter credentials.
- After import, review each migrated workflow/chatflow before production use. Pay special attention to tool nodes, agent tool configs, environment variables, app variables, and credential-backed settings.
- Use the import report. Items marked `dependency-only`, `skipped`, or `unresolved` usually need manual follow-up.

If you need the package to carry full MCP provider configuration or credentials, rerun export with `include_secrets` set to `true` and transfer the generated JSON as sensitive data.

### What should I consider when `include_secrets` is `true`?

The package can include sensitive runtime configuration such as API tool credentials, workflow/app DSL secret values, MCP server URLs, headers, authentication data, and MCP tool lists.

How to handle it:

- Store and transfer the JSON package as a secret.
- Delete the package after import if your security policy requires it.
- Prefer using this only for controlled one-time migrations where manual target-side credential setup is harder than securing the package.

### Should I use `preserve-id` or `generate-new-id`?

The ID strategy controls whether imported resources keep source IDs or receive new IDs in the target environment. It applies to workflow apps and custom tool resources where the target service supports explicit IDs.

Use `preserve-id` by default. It is recommended for cross-environment app migration because imported apps and tools try to keep their source IDs, which makes workflow references easier to preserve.

Use `preserve-id` when:

- You are migrating between environments that are meant to mirror each other, such as staging to production.
- You want workflow tool references and provider references to remain as stable as possible.
- The target environment does not already contain unrelated resources with the same IDs.

Watch out for:

- If the target already has a resource with the same ID, `conflict_strategy=fail` stops the import, `skip` keeps the target resource, and `update` updates it in place.
- If the same ID was reused for a different resource in the target environment, review carefully before using `update`.

Use `generate-new-id` when the target environment should keep its own local IDs.

Use `generate-new-id` when:

- The target environment already has resources that may conflict with source IDs.
- You are importing a package as a copy rather than trying to mirror environments.
- You want to avoid preserving source database IDs in the target environment.

Watch out for:

- Import records source-to-target ID mappings. Always review the import report's ID mappings.
- Workflow DSL provider references are rewritten using the generated ID mapping where possible.
- Dependencies that are metadata-only, such as MCP providers exported with `include_secrets=false`, still require manual target-side configuration.
- Built-in/plugin tools are not remapped as migrated custom resources; the target environment must provide them separately.
