+ Shared CI workflow
+ authored & owned by platform
+
+ build
+ lint
+ test
+ typecheck
+
+
+
+
+
+
+
+
+
deploy A
+
deploy B
+
deploy C
+
deploy D
+
deploy E
+
+
+ deploy per repo
+
+
+
+
diff --git a/src/content/changelog/artifacts/2026-08-04-build-and-deploy-on-push.mdx b/src/content/changelog/artifacts/2026-08-04-build-and-deploy-on-push.mdx
new file mode 100644
index 00000000000..c566a8925e2
--- /dev/null
+++ b/src/content/changelog/artifacts/2026-08-04-build-and-deploy-on-push.mdx
@@ -0,0 +1,75 @@
+---
+title: Build and deploy Artifacts repos on every push
+description: Trigger a Workflow on push to run CI checks and deploy your Artifacts repo to a Worker or User Worker.
+products:
+ - artifacts
+ - workflows
+date: 2026-08-04
+publish_future_dated_entry: true
+---
+
+import { TypeScriptExample, WranglerConfig } from "~/components";
+
+You can now run your CI/CD pipeline on your [Artifacts](/artifacts/) repo by defining a CI [Workflow](/workflows/) with the [CI SDK](https://github.com/cloudflare/ci), automatically triggered on Artifacts push events.
+
+This allows you to:
+
+- Automatically build and deploy application code stored in Artifacts.
+- Run linting, type checking, tests, and other checks on every push.
+- Reuse dependencies when the lockfile (i.e. `pnpm-lock.yaml`) has not changed.
+- Stop deployment when a check or build fails.
+- Restrict API token access to the deployment step.
+- Deploy the output to a [Worker](/workers/) or a [Workers for Platforms](/cloudflare-for-platforms/workers-for-platforms/) User Worker.
+
+Define your CI steps with `@cloudflare/ci`. Each `ci.runner()` spins up an isolated sandbox, and the `cache` option reuses installed dependencies across each sandboxed step in your CI job.
+
+Point `cache.inputs` at your lockfile (i.e. `pnpm-lock.yaml`, `bun.lock`), and the install step only runs again when that lockfile changes:
+
+
+
+```ts
+const deps = await ci.runner({
+ name: "install",
+ command: "bun install --frozen-lockfile",
+ cache: { inputs: ["package.json", "bun.lock"] },
+});
+
+await Promise.all([
+ deps.runner({ name: "lint", command: "bun run lint" }),
+ deps.runner({ name: "test", command: "bun run test" }),
+ deps.runner({ name: "typecheck", command: "bun run typecheck" }),
+ deps.runner({ name: "build", command: "bun run build" }),
+]);
+
+await deps.runner({ name: "deploy", command: "bun wrangler deploy" });
+```
+
+
+
+To start the Workflow automatically after each push, add a `cf.artifacts.repo.pushed` trigger to your Wrangler configuration:
+
+
+
+```jsonc
+{
+ "triggers": {
+ "events": [
+ {
+ "type": "cf.artifacts.repo.pushed",
+ "filter": {
+ "namespace": "CI",
+ "repoName": "my-repo",
+ },
+ "target": {
+ "scriptName": "my-ci-worker",
+ "workflowName": "ci-workflow",
+ },
+ },
+ ],
+ },
+}
+```
+
+
+
+To learn more, refer to [Build and deploy Artifacts repos](/artifacts/guides/build-and-deploy-on-push/).
diff --git a/src/content/docs/artifacts/guides/build-and-deploy-on-push.mdx b/src/content/docs/artifacts/guides/build-and-deploy-on-push.mdx
new file mode 100644
index 00000000000..abbfceee6a7
--- /dev/null
+++ b/src/content/docs/artifacts/guides/build-and-deploy-on-push.mdx
@@ -0,0 +1,250 @@
+---
+title: Build and deploy Artifacts repos
+description: Build projects stored in Artifacts repos and deploy them as Workers or Workers for Platforms User Workers.
+pcx_content_type: how-to
+sidebar:
+ order: 5
+products:
+ - artifacts
+---
+
+import {
+ DashButton,
+ PackageManagers,
+ Tabs,
+ TabItem,
+ TypeScriptExample,
+ WranglerConfig,
+} from "~/components";
+import ArtifactsCIPipelineDiagram from "~/components/ArtifactsCIPipelineDiagram.astro";
+import ArtifactsCIWorkflowDiagram from "~/components/ArtifactsCIWorkflowDiagram.astro";
+import ArtifactsPlatformSharedCIDiagram from "~/components/ArtifactsPlatformSharedCIDiagram.astro";
+
+Artifacts events can build and deploy projects stored in Artifacts repos. When a user or agent pushes a commit, the event triggers a [Workflow instance](/workflows/build/trigger-workflows/).
+
+Within the Workflow, you define a continuous integration (CI) pipeline with the `@cloudflare/ci` SDK to cache dependencies, run checks, and build the project. The final step in your CI pipeline can deploy the output to a [Worker](/workers/) or a [Workers for Platforms](/cloudflare-for-platforms/workers-for-platforms/) User Worker.
+
+This is useful when you need to:
+
+- Automatically build and deploy application code stored in Artifacts.
+- Run linting, type checking, tests, and other checks on every push.
+- Reuse dependencies when the lockfile (i.e. `pnpm-lock.yaml`) has not changed.
+- Stop deployment when a check or build fails.
+- Restrict API token access to the deployment step.
+- Deploy the output to a [Worker](/workers/) or a [Workers for Platforms](/cloudflare-for-platforms/workers-for-platforms/) User Worker.
+
+## How it works
+
+
+
+1. **Push repo changes** — A `git push` to the Artifacts repo emits an `artifacts.repo.pushed` event that identifies the pushed repo, branch, and commit.
+2. **Run the CI Workflow** — The event starts a Workflow that checks out the commit, installs and caches dependencies, and runs CI steps — build, lint, typecheck, and format — in parallel. A failed step stops the Workflow before deployment.
+3. **Deploy the Worker** — The Workflow deploys the built Worker either directly to your account or as a User Worker, if using Workers for Platforms
+
+### Run the CI Workflow
+
+Use the `@cloudflare/ci` SDK to define the CI steps. The SDK provides two tools to help you build the pipeline:
+
+- **Runners** — each `runner()` call spins up an isolated sandbox and executes a shell command. You use the same commands you already run locally or in another CI system. Each runner captures its own logs, status, and output files.
+- **Cache** — the `cache` option on a runner caches installed dependencies so that later runs do not reinstall them. Pass the files that determine the dependencies, such as `pnpm-lock.yaml`, to `cache.inputs`. When those files have not changed, the SDK restores the cached result instead of running the command again.
+
+
+
+A cached runner takes a [snapshot](/sandbox/api/backups/) of its sandbox, which later runners reuse. Multiple runners can branch from the same cached result — for example, lint, type-check, and test runners can all reuse one cached install.
+
+
+
+A failed runner retries according to its [step configuration](/workflows/build/sleeping-and-retrying/#retry-steps), where you can define the number of retry attempts, backoff schedule, and timeouts. Runners that depend on a previous step do not start until its retry succeeds. If the configured retry limit is reached, the Workflow terminates in an `Errored` state.
+
+Here is an example of how to set up your Workflow to use runners and cache to install dependencies, run checks, build the project, and deploy the Worker:
+
+
+
+```ts
+import { CIWorkflow } from "./src/pipeline";
+import type {
+ CiContext,
+ CiParams,
+ CiRunnerResult,
+ CloudflareArtifacts,
+} from "./src/pipeline";
+import type { WorkflowEvent, WorkflowStep } from "cloudflare:workers";
+
+export class CI extends CIWorkflow {
+ protected async pipeline(
+ _event: WorkflowEvent>,
+ _step: WorkflowStep,
+ ci: CiContext,
+ ): Promise {
+ // Install once, then run independent checks from the shared snapshot.
+ const deps: CiRunnerResult = await ci.runner({
+ name: "install",
+ command: "bun install --frozen-lockfile",
+ cache: { inputs: ["package.json", "bun.lock"] },
+ });
+
+ await Promise.all([
+ deps.runner({ name: "lint", command: "bun run lint" }),
+ deps.runner({ name: "test", command: "bun run test" }),
+ deps.runner({ name: "typecheck", command: "bun run typecheck" }),
+ deps.runner({ name: "build", command: "bun run build" }),
+ ]);
+
+ await deps.runner({
+ name: "deploy",
+ command: "bun wrangler deploy",
+ cloudflareCredentials: {
+ accountId: this.env.CLOUDFLARE_DEPLOY_ACCOUNT_ID,
+ },
+ });
+ }
+}
+```
+
+
+
+:::note
+The credentials on the `deploy()` step are only required when deploying the Worker to an account other than the account where the CI Workflow is running.
+:::
+
+## Start a build when code changes
+
+When a user or agent pushes a commit to an Artifacts repo, Artifacts emits an event that identifies the repo, branch, and commit that changed. You will use this event to trigger a Workflow instance which runs a CI pipeline by automatically checking out the commit and cloning the repo before installing dependencies, running checks, building the project, and/or deploying the Worker, according to your code.
+
+This guide defines those CI steps in a Workflow class named `CIWorkflow`. To start this Workflow automatically after each push, add an `cf.artifacts.repo.pushed` trigger to your Wrangler configuration. You should also include:
+
+- R2 binding: the bucket where your the snapshot of your cached dependencies will be stored
+- Container (and Durable Object) binding: create a binding to your container to access sandboxes during each `runner()` step
+- Workflows binding
+- Artifacts binding
+- Observability (optional): inspect your CI jobs as a Workflow instance with [Workers observability](/workers/observability/)
+
+
+
+```jsonc
+{
+ "$schema": "node_modules/wrangler/config-schema.json",
+ "name": "",
+ "main": "src/index.ts",
+ "compatibility_date": "2026-06-16",
+ "compatibility_flags": ["nodejs_compat"],
+ "artifacts": [
+ {
+ "binding": "ARTIFACTS",
+ "namespace": ""
+ }
+ ],
+ "containers": [
+ {
+ "class_name": "CiSandbox",
+ "image": "./Dockerfile",
+ "max_instances": 10,
+ "instance_type": "standard-4"
+ }
+ ],
+ "durable_objects": {
+ "bindings": [
+ {
+ "name": "SANDBOX",
+ "class_name": "CiSandbox"
+ }
+ ]
+ },
+ "workflows": [
+ {
+ "name": "",
+ "binding": "CI_WORKFLOW",
+ "class_name": "CI"
+ }
+ ],
+ "exports": {
+ "CiSandbox": {
+ "type": "durable-object",
+ "storage": "sqlite"
+ }
+ },
+ "r2_buckets": [
+ {
+ "binding": "BACKUP_BUCKET",
+ "bucket_name": ""
+ }
+ ],
+ "triggers": {
+ "events": [
+ {
+ "type": "cf.artifacts.repo.pushed",
+ // filter is optional. If you don't set repoName we will run the same workflow for every push on any repo in your Artifacts namespace
+ "filter": {
+ "namespace": "CI",
+ "repoName": "my-repo"
+ },
+ "target": {
+ "scriptName": "",
+ "workflowName": ""
+ }
+ }
+ ]
+ },
+ "observability": {
+ "enabled": true,
+ "logs": {
+ "enabled": true
+ }
+ }
+}
+```
+
+
+
+:::note
+If you are running CI for a User Worker, include `"dispatch_namespace": ""` in your trigger target.
+:::
+
+### View build status
+
+The `[observability]` setting in your Wrangler configuration records the status and logs for each pipeline run. To identify which stage failed, inspect the instance in the Workflows dashboard:
+
+
+
+Each runner displays its own input, output, and status, so you can identify the command that failed. When a runner fails, the Workflow records its output and does not start stages that need its files.
+
+## Deploy the application
+
+To deploy a Worker, pass `wrangler deploy` to your final `runner()` step, i.e. `workspace.runner({ name: "deploy", command: "wrangler deploy" })`.
+
+To deploy a User Worker, pass `wrangler deploy --dispatch_namespace ` to your final `runner()` step.
+
+## Run one workflow for every repo in a namespace
+
+The `filter` in your trigger is optional. When you set `repoName`, only pushes to that specific repo start the Workflow. When you omit `repoName`, Cloudflare runs the same Workflow for every push to any repo in your Artifacts namespace.
+
+This is useful for platforms that author and own a single CI Workflow and want to apply it uniformly across every customer repo in a namespace. Instead of maintaining a separate trigger per repo, one shared Workflow builds, checks, and deploys each repo on push.
+
+
+
+To run the same Workflow for every repo in a namespace, drop `repoName` from the trigger `filter` and keep only the `namespace`:
+
+
+
+```jsonc
+{
+ "triggers": {
+ "events": [
+ {
+ "type": "cf.artifacts.repo.pushed",
+ "filter": {
+ "namespace": "CI"
+ },
+ "target": {
+ "scriptName": "my-ci-worker",
+ "workflowName": "ci-workflow"
+ }
+ }
+ ]
+ }
+}
+```
+
+
+
+Each push still starts its own Workflow instance for the repo, branch, and commit that changed, so you can deploy a separate Worker per repo from the same shared Workflow definition.