Environments and migrating data
Run development, staging and production as separate projects, copy your data model between them with the CLI, and move records and settings safely.
Last updated
Anythink has no built-in "environment" switch. An environment is a project. You keep one project for development, one for staging and one for production, and you copy work from one to the next. Each project has its own database, API, files, users, keys, secrets and billing, so a mistake in development can't touch production.
This page covers setting up the three projects, copying your data model with anythink migrate, moving records, what to set up by hand in each project, and a checklist for going live. If you haven't used the CLI yet, read Install and use the Anythink CLI first.
Environments on Anythink#
Create one project per environment. They can sit on different plans: a small plan for development and staging, and the plan your traffic needs for production. A plan is chosen per project, so each project is billed and limited on its own. See Account, billing and projects.
What stays separate in each project:
| Separate in every project | Consequence |
|---|---|
| Data and files | Records you create in development don't appear in production. |
| Users and roles | A user signs up per project. Sign-ins and tokens from one project don't work in another. |
| API keys | Create a key per project. A development key can't read production. |
| Secrets and connections | Re-enter each secret and reconnect each integration per project. |
| Settings and billing | Theme, sign-up settings, allowed URLs, plan and invoices belong to the project. |
Name your projects and profiles#
Use one pattern for every app, so the project name tells you the environment at a glance:
| Environment | Project name | CLI profile |
|---|---|---|
| Development | acme-dev |
acme-dev |
| Staging | acme-staging |
acme-staging |
| Production | acme-prod |
acme-prod |
projects use saves a profile named after the project in lower case with hyphens, so the profile names above follow from the project names.
In the Anythink dashboard
- Open My Anythink and go to your billing account.
- Select Create Project, choose a plan and name it, for example
acme-staging. - Repeat for each environment. Open a project from the portal to work in its Anythink dashboard. Check the project name shown in the header before you change anything.
With the CLI
anythink projects create "acme-dev" --region lon1
anythink projects create "acme-staging" --region lon1
anythink projects create "acme-prod" --region lon1
Without --plan the CLI shows the plans and asks you to choose. Provisioning takes a moment, so check anythink projects list for an active status. Then save a profile for each:
anythink projects use acme-dev
anythink projects use acme-staging
anythink projects use acme-prod
anythink config show
config show lists every profile and marks the active one.
With an AI assistant (MCP)
Create three projects called acme-dev, acme-staging and acme-prod in the lon1 region, then list my projects.
With the local anythink-mcp server the assistant runs projects_create and projects_list. See Connect Claude to your backend with MCP.
Name the profile on every command#
The active profile is shared by every terminal and tool on your machine, so a command can land on the wrong project if you switched in another window. For anything that changes data, name the profile instead of relying on the active one:
anythink --profile acme-staging entities list
anythink --profile acme-prod data list orders --limit 5
--profile applies to that command only and never changes the active profile. For scripts and CI, create an API key in each project and sign in with anythink login --org-id <id> --api-key <key>, then use --profile throughout. See API keys.
Copy the data model with migrate#
anythink migrate copies a project's data model from one profile to another. It reads the source, compares it with the target by name, and creates whatever the target is missing.
anythink migrate --from acme-dev --to acme-staging --dry-run
anythink migrate --from acme-dev --to acme-staging
Start with --dry-run. It reads both projects, prints a summary of what would be created and changes nothing. Check the --to profile before you run the real thing: migrate writes to the target.
What it copies#
| Part | What is copied |
|---|---|
| Entities and fields | Every non-system entity and its custom fields |
| Entity options | Row level security, public access, locked new records and junction setting |
| Field options | Type, label, default value, required, unique, name field, searchable, publicly searchable and indexed |
| Relationships | Remapped to the target's entities, so a link to customers still points at customers |
System entities and the fields the platform manages are skipped. Relationship fields are created in two passes, so a one-to-many field can refer to an entity created later in the same run.
Add flags to include more:
| Flag | What it copies |
|---|---|
--include-roles |
Roles and their permissions, matched by role name. The Admin User role is skipped. |
--include-workflows |
Workflows with their steps and the links between them. Each is created disabled. |
--include-menus |
Dashboard menus and their items. Menu items keep their links, rewritten for the target project. |
--include-files |
Uploaded files. Each is downloaded from the source and uploaded to the target. |
--include-data |
Records. See Move records below. |
--include-settings |
The project's theme, sign-up settings, allowed application URLs, payment return URLs and Maps key. The target keeps its own name, description and logo files. |
Include menus together with roles: a menu belongs to a role, and the CLI finds the target's role by name. A menu whose role doesn't exist on the target is skipped with an error.
--include-settings replaces the target's settings with the source's. Only use it on a project whose settings you are happy to overwrite, and always run it with --dry-run first.
What it never overwrites#
migrate only adds. It matches entities, fields, roles, workflows and menu items by name, and skips any that already exist on the target.
- A field you changed in development isn't updated in staging. If you need to change a field that already exists on the target, change it there directly.
- A role that exists on the target gains the source role's permissions. It never loses any.
- A workflow with the same name is left alone, so changes to an existing workflow don't carry over.
--force-menusis the exception: it deletes each target menu that has the same name as a source menu, then recreates it. Use it only when you want the target's version of that menu gone.
What it does not copy#
| Not copied | What to do |
|---|---|
| Users and their roles | Users sign up or are invited in each project. |
| Secrets | Create them per project. See Set up the rest per project. |
| API keys | Create a key in each project. |
| Integration connections | Reconnect each provider in the target. |
| Email templates and sending domains | Recreate them in the target. |
| Plan, billing and the project name | Set in My Anythink. |
Read the result#
The summary shows + for what was created and skipped for what already existed. If any entity, field, file or record fails, the command lists each error and exits with code 2, and a clean run exits with 0, so a script can stop on a failure. A second run with the same flags reports Nothing new when the target is up to date.
In the Anythink dashboard
The Anythink dashboard has no migrate screen. To copy a data model by hand, open Entities in the target project and create the same entities and fields. Use the CLI for anything beyond a few entities, because it keeps the options and relationships identical.
With the CLI
anythink migrate --from acme-dev --to acme-staging --include-roles --include-menus --dry-run
anythink migrate --from acme-dev --to acme-staging --include-roles --include-menus
With no --include-* flag, migrate asks which parts to copy, so run it in a terminal for an entities-only copy. In a script, name the parts you want with flags.
With an AI assistant (MCP)
Preview a migration from acme-dev to acme-staging that includes roles and menus. Show me the summary and don't change anything.
The local anythink-mcp server runs migrate through its cli tool, which has no terminal for the prompt, so ask for the parts you want by name. Review the dry-run summary yourself before you ask for the real run. The hosted MCP connector doesn't run migrate.
Move records#
Records are separate from the data model. Move them only when you need them: reference data such as categories or plans, a seed set for staging, or the first load of production.
You have three routes, from simplest to most flexible.
Option 1: copy them with migrate#
anythink migrate --from acme-dev --to acme-staging --include-data --dry-run
anythink migrate --from acme-dev --to acme-staging --include-data
--include-data creates every record of every non-system entity in the target. It also:
- gives each record a new id in the target, and rewrites many-to-one and one-to-one links to the new ids. A link whose target record wasn't copied is left empty;
- copies non-junction entities first, then junction entities, so relationships resolve;
- skips an entity that already has records in the target.
--force-datacopies into those entities too, and--force-data-entities orders,profilesforces only the entities you name. Forcing adds new copies. It doesn't merge with or update what's there; - remaps file fields to the target's files when you also pass
--include-files. Without--include-files, a file field is left empty.
Copying every entity includes any test data. If you only want a few entities, use Option 2 or 3.
Option 2: export and import a CSV file#
The dashboard's Import button loads a CSV file into an entity and runs in the background. Import creates records and never updates existing ones, so importing the same file twice creates each row twice. See Records, views and CSV import for the steps, the column mapping and what a failed row does.
The dashboard has no export button. Read the records through the CLI and convert them to CSV with the tool you prefer:
anythink --profile acme-dev data list categories --json --all > categories.json
Each page is a separate JSON object with an items array.
Option 3: script it with data list and data create#
For one entity, a short script gives you full control. This copies categories from development into staging:
anythink --profile acme-dev data list categories --json --all \
| jq -c '.items[] | del(.id, .created_at, .updated_at, .locked, .__name)' \
| while read -r row; do
anythink --profile acme-staging data create categories --data "$row"
done
data list --json --all prints every page. jq removes the fields the platform sets itself, so the target assigns a new id, timestamps and lock state. Each line then becomes a data create call.
A safe pattern for any copy:
- Count first. Run
anythink --profile acme-dev data list categories --json --all | jq -s '.[0].total_count'so you know what to expect. - Try one row. Pipe through
head -n 1and check the new record in the target. - Copy, then count again on the target and compare.
- Never run it twice against the same target. The script has no duplicate check, so a second run creates every record again. If you must re-run, filter the source with
--filteror delete the first copy. - Mind relationships. A many-to-one field holds the source's record id, which is wrong in the target. Copy the parent entity first, then look up each parent's new id, or use Option 1, which remaps ids for you.
- Don't copy users this way. People should sign up or be invited in each project.
With an AI assistant (MCP)
Copy every record from categories in acme-dev to acme-staging. Count them first, create one to check it, then do the rest and tell me the counts on both sides.
The assistant runs data list and data create through the cli tool. The local MCP server uses one profile per session, so give it each project in turn or run the script yourself for a large entity.
Set up the rest per project#
migrate covers the data model, roles, menus and workflows. Everything below is set up in each project.
| What | How to move it |
|---|---|
| Workflows (single) | Export with anythink --profile acme-dev workflows export <id> -o order-confirmed.json, then load it with anythink --profile acme-staging workflows seed order-confirmed.json. Add --var KEY=VALUE to fill {{KEY}} placeholders and --enabled to switch it on. See How workflows work. |
| Secrets | Create each one in the target with anythink --profile acme-staging secrets create PARTNER_API_KEY. Values never leave a project, so use a different value per environment where you can. See Store API keys and tokens as secrets. |
| Email templates | List them with anythink --profile acme-dev fetch /email-templates, then create each in the target with fetch /email-templates --method POST and the template's type, subject and content. Verify a sending domain in each project. See Send email from a workflow. |
| Integration connections | Connect each provider again in the target, for example anythink --profile acme-staging integrations oauth connect google-calendar. Credentials belong to the project. See How integrations work. |
| API keys | Create a key per project with only the permissions its caller needs. See API keys. |
| Roles and menus | Use --include-roles and --include-menus, or set them up in the dashboard. See Roles and permissions and Menu configuration. |
| Users | Invite or sign up users in each project. |
A workflow that uses a secret or connection fails in the target until you create that secret or connection there, so set those up before you enable the workflow.
Promote to production checklist#
Work through this list, in order, each time you promote.
- Check the profile. Run
anythink config show, and pass--profile acme-prodon every command. - Preview. Run
migrate --from acme-staging --to acme-prod --dry-runwith the flags you plan to use. Read the counts. - Migrate the data model. Run it for real, adding
--include-rolesand--include-menusif they changed. Fix any errors it lists. The command exits with 2 if any step failed. - Load reference data that production needs on day one, such as categories or plans, using Option 1, 2 or 3 above.
- Create secrets. Add every secret your workflows use, with production values.
- Reconnect integrations and verify the email sending domain.
- Copy workflows, then check each one's trigger and steps, and enable it only when its secrets and connections are in place.
- Set project settings. Check sign-up settings, allowed application URLs and payment return URLs point at your production app. Use
--include-settingsonly if you want to overwrite them from the source. - Create production API keys with least privilege, and point your app at the production project's API address and key.
- Check permissions. Confirm each role has the permissions its users need on every entity. A missing permission is the most common cause of a
403after a promotion. - Test as a real user. Sign in to the production app, create a record, and run one workflow end to end.
- Confirm the plan and billing on the production project in My Anythink.