Promote a Data Cloud data kit from dev to production with Lifecycle Center
A tested, step-by-step process: commit the data kit with a script, promote it through the pipeline, then deploy its components in each org.
Tested end to end in October 2026 on Winter '27 (264.7.5), through a Development → Staging → Production pipeline.
Lifecycle Center is the new name for DevOps Center; the API names are unchanged.
Use ← → to move between slides. Click a screenshot to enlarge it.
Runbook, scripts and sample metadata: github.com/congmingwudi/data360-lifecycle-center-devops
Two things to know before you start
1. DX Inspector can't commit a data kit
What happens: the data kit's components stage, then the commit fails at the last step with Commit failed during processing. Nothing reaches the work item's branch.
What works: commit-datakit-to-work-item.sh makes that
commit for you. Lifecycle Center inspects it and the work item is reviewed and promoted as usual.
2. Promoting installs the data kit; it doesn't deploy it
What happens: the promotion is a Metadata API deploy. It brings the data kit's definition, templates and publishing sequence. No data stream, DLO, mapping or insight is created.
What works: after each install, run
deploy-datakit-components.sh in that org. It runs the data kit's Deploy action, which
creates them.
Everything else in Lifecycle Center (work items, review, Promote Selected, Promote Stage) works for data kits unchanged.
How the pieces fit
flowchart LR DEV["Development sandbox
afd360-dcdev1
build the data kit"] C["commit-datakit-to-
work-item.sh"] subgraph GH["GitHub: branches = stages"] WI["WI-00000N"] ST["dcstaging1"] M["main"] end LC["Hub org
Lifecycle Center"] STG["Staging sandbox afd360-dcstaging1
6 then: deploy-datakit-components.sh"] PRD["Production afd360-prod
9 then: deploy-datakit-components.sh"] DEV -->|"1 manifest + retrieve"| C C -->|"2 commit + push"| WI WI -.->|"3 inspects"| LC WI ==>|"4 Promote Selected"| ST ST -->|"5 install"| STG ST ==>|"7 Promote Stage"| M M -->|"8 install"| PRD classDef dev fill:#E2EDF5,stroke:#2F6F9F,stroke-width:2px,color:#1D2A30 classDef stg fill:#F6ECDC,stroke:#B7791F,stroke-width:2px,color:#1D2A30 classDef prd fill:#E0F0E8,stroke:#2E7D5B,stroke-width:2px,color:#1D2A30 classDef tool fill:#FFFFFF,stroke:#1D2A30,stroke-width:1.5px,color:#1D2A30 classDef hub fill:#E9EDEA,stroke:#5B6770,stroke-width:2px,color:#1D2A30 class DEV dev class STG,ST stg class PRD,M prd class C,WI tool class LC hub click C href "https://github.com/congmingwudi/data360-lifecycle-center-devops/blob/main/scripts/commit-datakit-to-work-item.sh" "Open the script on GitHub" _blank click STG href "https://github.com/congmingwudi/data360-lifecycle-center-devops/blob/main/scripts/deploy-datakit-components.sh" "Open deploy-datakit-components.sh on GitHub" _blank click PRD href "https://github.com/congmingwudi/data360-lifecycle-center-devops/blob/main/scripts/deploy-datakit-components.sh" "Open deploy-datakit-components.sh on GitHub" _blank
Numbers follow the order you do things in. Thick arrows are merges Lifecycle Center makes when you promote; each install is followed by a component deploy in that org. The Hub is the production org: it holds Lifecycle Center and is also the Production stage.
Branches are the pipeline's stages
gitGraph LR: commit id: "pipeline created" branch dcstaging1 checkout dcstaging1 branch WI-000008 checkout WI-000008 commit id: "data kit (script)" checkout dcstaging1 merge WI-000008 id: "Promote Selected" checkout main merge dcstaging1 id: "Promote Stage" checkout dcstaging1 branch WI-000009 checkout WI-000009 commit id: "updated data kit (3 files)" checkout dcstaging1 merge WI-000009 id: "Promote Selected " checkout main merge dcstaging1 id: "Promote Stage "
- The last stage owns
main: Production's branch ismain, the top line. Staging gets its own branch below it,dcstaging1, cut frommain. - Each work item gets a branch
WI-00000Ncut fromdcstaging1when it goes In Progress. The script commits there.
- Promote Selected merges a work item into Staging. Promote Stage merges all of Staging into
main. - Lifecycle Center does the merges. Don't merge the pull requests on GitHub yourself.
What the promotion carries, and what you do in each org
| Installed by the promotion (data kit metadata) | Done in each target after the install |
|---|---|
| Data stream template and bundle | Deploy the data kit's components with deploy-datakit-components.sh. This creates the stream, DLO and mappings. |
| DLO schema, DMO and its field maps | Activate the file connection, only if it's inactive. It was inactive in Staging and already active in Production. |
| Connection information (DevOps data kits carry it) | First time only: Update File on the stream (upload the CSV). This activates the stream. |
| Calculated insights added to the data kit | Run each new insight once (create-calculated-insight.sh --run-only, or Run Now). |
| Publishing sequence (deploy order) | Make sure the data kit's data space exists. Building in default means it always does. |
Never carried: the uploaded file, ingested records, and one system field map
(cdp_sys_SourceVersion__c, so 10 of 11 fields map). All rows still reach the DMO.
One-time setup
Prerequisites, the pipeline, and building the data kit in the development sandbox.
Prerequisites
- Data Cloud provisioned in production and in each sandbox (sandbox Data Cloud needs it in production first).
- A data space with the same name in every org. Build in
defaultand there's nothing to create. - Lifecycle Center permission sets on the Hub (Admin, User, Deployment Manager).
- A GitHub repo connected to the pipeline, and push access to it.
- On your workstation: the sf CLI logged in to every org, plus
jq,gitand bash, and this project'sscripts/.
Hub: assign the permission sets and verify./scripts/setup-devops-center-hub.sh --target-org afd360-prod
Read-only: Data Cloud provisioned, data space ACTIVE./scripts/verify-datacloud-prereqs.sh -o afd360-dcdev1 -o afd360-dcstaging1 -o afd360-prod
| Alias in this deck | Role |
|---|---|
afd360-prod | Hub (Lifecycle Center) and Production stage |
afd360-dcstaging1 | Staging sandbox |
afd360-dcdev1 | Development sandbox |
Substitute your own org aliases.
The pipeline: Development → Staging → Production
- One project, one pipeline, three stages.
- Development (the sandbox you build in) has no branch of its own.
- Staging deploys to the staging sandbox from
dcstaging1. - Production deploys to the production org from
main. - Activate the pipeline before you create work items. Otherwise moving one to In Progress fails with An unexpected error occurred.
Build the data in the dev sandbox
- A File Upload data stream from a CSV, which creates its DLO.
- Map the DLO to a DMO.
- Build it in the
defaultdata space, which exists in every org.
The example: 50 branch offices, CSV stream → branch_offices_seedcsv__dll → custom DMO Branch_Office__dlm.
Package it as a DevOps data kit
- Data kit type: DevOps, never Standard. Only a DevOps data kit can be built in a sandbox, moves by Metadata API, and carries its connector information. You can't switch types later.
- Add the data stream bundle. Its DLO, DMO and mappings come with it.
- Check the Publishing Sequence: connection, then bundle.
A "You haven't defined the publishing sequence" banner may stay after you save it. It's cosmetic: the order still travels and is used in every target.
Each change: dev to Staging
Work item, scripted commit, review, promotion, then the post-install steps in Staging.
1Create a work item and mark it In Progress
- In the Hub: Lifecycle Center → Work Items → New. Pick the project, and the dev sandbox as the Development Environment.
- Click Mark as In Progress. Lifecycle Center creates the branch
WI-00000Nfromdcstaging1. - Note the work item's name; the script needs it.
WI-000009.Don't commit the data kit with DX Inspector
DX Inspector stages every component, then the commit fails at finalize. Retrying doesn't help. It fails the same way for any data kit, large or small.
- Committing the data kit's components yourself (next step) is the only step that changes.
- Ordinary metadata (flows, permission sets, fields) still commits through DX Inspector. Put it in a separate work item.
- No failed DX Inspector attempt is needed before running the script.
2Commit the data kit with the script
sequenceDiagram autonumber actor You participant S as commit-datakit-to-work-item.sh link S: Open on GitHub @ https://github.com/congmingwudi/data360-lifecycle-center-devops/blob/main/scripts/commit-datakit-to-work-item.sh participant Dev as Dev sandbox participant Stg as Staging (check-only) participant Git as GitHub WI-00000N participant LC as Lifecycle Center (Hub) You->>S: data kit, work item, orgs S->>LC: Work item In Progress? Branch exists? S->>Dev: GET the data kit's manifest S->>Dev: Retrieve exactly those members Note over S: Drop the KQ_* key qualifier field
Raise sourceApiVersion to 64.0 S->>Stg: Check-only deploy (nothing changes) S->>Git: Commit as you, push fast-forward only Git-->>LC: Lifecycle Center inspects the push LC-->>S: Work item's Change List S-->>You: OK, the Change List matches the commit
2Commit the data kit: commands and result
Dry run: builds and validates the commit, pushes nothing./scripts/commit-datakit-to-work-item.sh \ -k branch_offices_datakit -s afd360-dcdev1 \ --hub afd360-prod -w WI-000009 \ --validate-org afd360-dcstaging1 --dry-run
Real run: commit, push, wait for the inspection./scripts/commit-datakit-to-work-item.sh \ -k branch_offices_datakit -s afd360-dcdev1 \ --hub afd360-prod -w WI-000009 \ --validate-org afd360-dcstaging1
- The data kit's own manifest decides what's committed, so nothing is missed and nothing extra goes in.
- It works in a separate git worktree under
tmp-cli-validation/. Your checkout isn't touched. - Check the data kit's contents in Data Cloud Setup first: the manifest only lists what's in the data kit.
3Create the review, then mark it Ready to Promote
- In DX Inspector in the dev sandbox, on the work item, click Create Review. Lifecycle
Center opens a pull request from
WI-00000Ntodcstaging1. - The work item moves to In Review. Review the pull request as your process requires.
- Click Mark as Ready to Promote. Don't merge the pull request on GitHub.
4Promote Selected to Staging
- Open the pipeline. The work item is under Approved Work Items.
- Select it and click Promote Selected.
- Options: Only changes missing from Staging, No tests (a data kit has no Apex).
- Lifecycle Center merges into
dcstaging1and deploys to the staging sandbox.
This only installs the data kit. Continue with the post-install steps.
After each install: what happens in the target org
sequenceDiagram autonumber participant LC as Lifecycle Center participant T as Target org (Staging, then Production) actor You LC->>T: Metadata API deploy: data kit definition, templates, publishing sequence Note over T: Installed. No stream, DLO, mapping or insight yet You->>T: Activate the UploadedFiles connection (only if inactive) You->>T: deploy-datakit-components.sh (Connect API: deploy the data kit) T-->>You: Job Complete: stream (inactive), DLO, mappings, insights You->>T: Update File: upload the CSV, Deploy (first time only) Note over T: Stream active, rows flow to the DMO You->>T: Run each new calculated insight once
Scripts: deploy-datakit-components.sh,
create-calculated-insight.sh.
5Activate the file connection, if it's inactive
Data Cloud Setup → Other Connectors → UploadedFiles (File Upload). An inactive
connection makes the component deploy fail.
- Staging: it was inactive; activate it.
- Production: it was already active, and the data kit left it alone. Skip the step.
Check the connection's statesf data query -o afd360-dcstaging1 --query "SELECT DeveloperName, ConnectionState FROM MktDataConnection WHERE DeveloperName = 'UploadedFiles'"
6Deploy the data kit's components
Dry run: what's installed, what's deployed, what's pending./scripts/deploy-datakit-components.sh -k branch_offices_datakit -o afd360-dcstaging1 --dry-run
Deploy, wait for the job, verify./scripts/deploy-datakit-components.sh -k branch_offices_datakit -o afd360-dcstaging1
- It runs the data kit's Deploy action through the Connect API, then polls the job.
- It compares the publishing sequence with the data kit's deployment log, and deploys only if something was never deployed.
- It checks every item and ends with one
RESULTline.
Never run two deploys of one data kit at once. Both try to create the DLO, and one fails with already exists.
==> Publishing sequence vs. what's deployed in afd360-dcstaging1:
1. DataConnection: UploadedFiles — connection (redeploys harmlessly)
2. DataStreamBundle: branch_offices_bundle — NOT DEPLOYED
==> Deploying branch_offices_datakit components in afd360-dcstaging1 ...
job status: InProgress
job status: Complete
==> Deployment log for job 08P…:
21:51:05 (kit) Successful
21:51:07 UploadedFiles Successful
21:51:41 branch_offices_seedcsv Successful
==> Streams in afd360-dcstaging1: 1 of 1
branch_offices_seedcsv: INACTIVE
==> RESULT: SUCCESS. Job 08P… deployed every item.
The stream arrives inactive. That's expected; the next step activates it.
7Upload the file to activate the stream
- Data Streams → the stream → Update File.
- Keep Full Refresh, upload the CSV, wait for File Upload Complete, click Deploy.
- The stream turns Active and runs once. Rows reach the DLO and the DMO.
Needed once per org, because a data kit carries the stream's configuration but never the uploaded file. Later component deploys keep the stream and its data.
Staging to Production
Promote the whole stage, then repeat the post-install steps in production.
8Promote Stage to Production
- In the Staging column, click Promote Stage. It promotes every work item in Staging together, by merging
dcstaging1intomain. - Keep the defaults: Promote Now, Only changes missing from Production, No tests.
"Can't validate promotion… source control is checking the pull request" is temporary. GitHub is still checking the merge. Wait a minute and click Promote Stage again.
9Repeat the post-install steps in production
Connection already active? (it was, in our production org)sf data query -o afd360-prod --query "SELECT DeveloperName, ConnectionState FROM MktDataConnection WHERE DeveloperName = 'UploadedFiles'"
Deploy the components./scripts/deploy-datakit-components.sh -k branch_offices_datakit -o afd360-prod --dry-run ./scripts/deploy-datakit-components.sh -k branch_offices_datakit -o afd360-prod
- Then Update File on the stream, the first time (the row menu in the Data Streams list works too).
- Result: the stream is Active, its last run succeeded, and the DLO and DMO each hold all 50 rows.
Updating a data kit
Tested by adding a calculated insight to the deployed data kit and promoting it to both targets.
Add the component in dev, then commit the change
- Create the calculated insight in the dev sandbox (the UI, or the example script below).
- Data Kits → the data kit → add the insight. Check the Publishing Sequence lists it after the bundle.
- New work item, In Progress, then the same commit script.
Example insight, dev sandbox only./scripts/create-calculated-insight.sh -o afd360-dcdev1 --run
The commit carried only the change: 3 files (the insight template, its data kit membership, and the data kit definition's new deploy order). The same data kit is updated; no new data kit is needed.
In each target: promote, deploy the components, run the insight
The promotion again only installs. The dry run shows the new insight as NOT DEPLOYED. The component deploy replays the whole sequence:
| Log row | Result |
|---|---|
| (kit) | Successful |
UploadedFiles | Successful (redeployed) |
branch_offices_seedcsv | Successful (redeployed in place, data kept) |
| Branch Footprint by Region | Successful (new) |
Run the insight the data kit deployed (target orgs: never create one)./scripts/create-calculated-insight.sh -o afd360-prod --run-only
No re-upload needed: the stream stayed Active with its 50 rows. A new insight arrives active but never run, so run it once.
Command cheat sheet
Once
./scripts/setup-devops-center-hub.sh --target-org afd360-prod ./scripts/verify-datacloud-prereqs.sh -o afd360-dcdev1 -o afd360-dcstaging1 -o afd360-prod
Each change: commit (after Mark as In Progress)
./scripts/commit-datakit-to-work-item.sh -k branch_offices_datakit -s afd360-dcdev1 \ --hub afd360-prod -w WI-00000N --validate-org afd360-dcstaging1 --dry-run ./scripts/commit-datakit-to-work-item.sh -k branch_offices_datakit -s afd360-dcdev1 \ --hub afd360-prod -w WI-00000N --validate-org afd360-dcstaging1
Then in the UI: Create Review, Ready to Promote, Promote Selected.
After Promote Selected: Staging
./scripts/deploy-datakit-components.sh -k branch_offices_datakit -o afd360-dcstaging1 --dry-run ./scripts/deploy-datakit-components.sh -k branch_offices_datakit -o afd360-dcstaging1 ./scripts/create-calculated-insight.sh -o afd360-dcstaging1 --run-only
After Promote Stage: Production
./scripts/deploy-datakit-components.sh -k branch_offices_datakit -o afd360-prod --dry-run ./scripts/deploy-datakit-components.sh -k branch_offices_datakit -o afd360-prod ./scripts/create-calculated-insight.sh -o afd360-prod --run-only
Before the first deploy in an org: activate the connection if it's inactive. After it: Update File on the stream.
Gotchas we hit, and what to do
- DX Inspector commit fails for any data kit. Use the commit script; keep other metadata in a separate work item.
- Key qualifier field (
KQ_*) can't be deployed. The commit script removes it; the component deploy recreates it. - API 62.0 fails on the data kit definition (
useDeterministicNaming). The script raisessourceApiVersionto 64.0. - The component deploy API exists from v64.0, takes an empty
{}body for a DevOps data kit, and endsComplete. The script handles all three. - Two deploys at once fail with a data lake object … already exists. Wait for one to finish.
- "Can't validate promotion" on Promote Stage is temporary. Retry after a minute.
- Don't create a calculated insight by hand in a target. It isn't owned by the data kit, and no data kit can update it later. Use
--run-only. - The data space must exist in the target under the same name.
defaultalways does. - The stream arrives inactive and needs Update File once. The uploaded file never travels.
- One system field map (
cdp_sys_SourceVersion__c) doesn't cross. Map it by hand if you need it. - The publishing-sequence banner can stay after you save. It's cosmetic.
Salesforce sources
- Use CLI to Deploy Changes from Sandbox to Data 360 (developer guide): the manifest path and the
KQ_*files. - Use the Connect REST API to Deploy Data 360 Data Kits (developer guide): the component deploy.
- Data 360 Connect API: Calculated Insights (API reference).
- Data 360: Data Kit Considerations & Common Issues (Knowledge article 003960830).
- Deploy Data 360 Changes Across Orgs and Considerations for Data Kits in Data 360 (Salesforce Help).
- Data 360 Sandboxes (Trailhead module): DevOps data kits, deployment tools, pre- and post-deploy checks.
- Build and Share Data 360 Functionality (Salesforce Help).
The runbook, scripts and sample kit metadata are in github.com/congmingwudi/data360-lifecycle-center-devops. The scripts are bash and run against any orgs you're logged in to with the sf CLI. Each takes the org alias, is safe to re-run, and checks its own result. Run the dry run first.