Developer Tools8 min read

How to Integrate Pulumi with Your Headless CMS

Connect Pulumi to publish events so content changes can trigger infrastructure updates, preview environments, redirects, and deployment config without manual tickets.

Published April 30, 2026
01 β€” Overview

What is Pulumi?

Pulumi is an infrastructure-as-code platform that lets teams define cloud resources with TypeScript, Python, Go, C#, Java, or YAML. Platform, DevOps, and product engineering teams use it to provision AWS, Azure, Google Cloud, Kubernetes, Cloudflare, and other services from versioned code. Pulumi sits in the same category as Terraform and cloud-native CDKs, with Pulumi Cloud adding state, policy, stack history, deployments, and access controls.


02 β€” The case for integration

Why integrate Pulumi with a headless CMS?

Pulumi usually runs from code, pull requests, CI jobs, or Pulumi Cloud Deployments. That works well for infrastructure owned by engineers, but it gets awkward when content operations need to influence infrastructure. Think about a docs team adding a new product version that needs a CDN route, a marketing team publishing a regional microsite that needs a domain mapping, or a developer relations team launching a sandbox that needs a short-lived preview stack.,Connecting Pulumi to a headless CMS category tool gives you a controlled path from editorial change to infrastructure change. With Sanity, content is structured as typed JSON in the Content Lake, so a Pulumi program can read fields like `domains`, `redirects`, `regions`, `docsVersion`, and `stackName` directly. GROQ selects only the fields the infrastructure job needs, and webhooks fire when a document is published, updated, or deleted.,The alternative is usually a chain of tickets, copied JSON files, Slack approvals, and manual `pulumi up` commands. That can be fine for one site. It breaks down when you have 12 locales, 4 deployment environments, and release windows where content and infrastructure must move together. The trade-off is that you need guardrails. Don’t let arbitrary editors define raw infrastructure code. Model safe fields, validate them, and map them to Pulumi config in a small, reviewed integration layer.


03 β€” Architecture

Architecture overview

A typical setup starts with a schema in Sanity Studio for deployment-related content, such as site environments, docs versions, redirect rules, domains, or feature flags. When a document is published, a Sanity webhook sends a POST request to a Node.js webhook listener, or a Sanity Function runs server-side on the mutation. The handler reads the document ID from the event, fetches the full document from the Content Lake with GROQ, validates the fields, and passes the result to Pulumi. For long-running infrastructure updates, the webhook listener usually calls Pulumi Automation API from a service that has the Pulumi CLI, cloud credentials, and `PULUMI_ACCESS_TOKEN`. If your stack already uses Pulumi Cloud Deployment Settings, a Sanity Function can instead call the Pulumi Deployments REST endpoint, `POST /api/stacks/{org}/{project}/{stack}/deployments`, to start an update without running the Pulumi CLI itself. Pulumi then updates the target stack, such as Cloudflare routes, AWS CloudFront behaviors, Kubernetes config maps, or preview environments. End users see the result when the frontend requests the same structured content from Sanity and the infrastructure change has finished, for example a new docs version route returning the right page instead of a 404.


04 β€” Use cases

Common use cases

🌐

Content-driven domain and route updates

Publish a market or docs-version document in Sanity, then use Pulumi to update Cloudflare routes, CDN behaviors, or load balancer rules.

πŸ§ͺ

Preview stacks for launches

Create short-lived Pulumi stacks when a release or campaign document enters review, so editors can test real infrastructure before publish.

πŸ”

Redirects as reviewed content

Let editors maintain safe redirect fields in Sanity while Pulumi applies them to edge infrastructure as code.

πŸ—ΊοΈ

Regional rollout config

Use Sanity fields for locales, regions, and rollout dates, then let Pulumi update deployment targets for each approved market.


05 β€” Implementation

Step-by-step integration

  1. 1

    Set up Pulumi

    Create a Pulumi account and organization, install the Pulumi CLI, run `pulumi login`, and create a project and stack such as `docs-infra/dev`. For a TypeScript webhook service, install `@pulumi/pulumi`, `@pulumi/pulumi/automation`, and the provider packages you use, such as `@pulumi/cloudflare` or `@pulumi/aws`. Store `PULUMI_ACCESS_TOKEN` and cloud provider credentials in your deployment environment, not in Sanity.

  2. 2

    Model deployment content in Sanity Studio

    Create a schema for the fields Pulumi is allowed to read, for example `stackName`, `domains`, `regions`, `redirects`, `docsVersion`, and `enabled`. Keep secrets out of the schema. If editors need to choose from secret-backed options, model an enum like `integrationProfile`, then resolve it to secrets in your webhook service.

  3. 3

    Create a focused GROQ query

    Write a GROQ query that fetches only the fields Pulumi needs. For example, query one site environment by document ID and project redirects into `{from, to, permanent}` objects. This keeps the sync payload small and avoids passing unrelated editorial fields into infrastructure code.

  4. 4

    Create the sync mechanism

    Use a Sanity webhook for publish, update, and delete events, with a projection that includes the document ID and type. Point it at a Node.js route, small API service, or queue worker. If the job only needs to trigger Pulumi Cloud Deployments, a Sanity Function can call the Pulumi REST API directly. If it needs Pulumi Automation API, run it in a service where the Pulumi CLI and cloud credentials are available.

  5. 5

    Connect to Pulumi

    Map validated Sanity fields to Pulumi stack config, then run `stack.up()` with Pulumi Automation API or start a Pulumi Cloud Deployment with the REST API. Keep the Pulumi program itself in Git, and treat Sanity content as input data, not executable infrastructure code.

  6. 6

    Test the end-to-end path

    Start with a dev stack. Publish a test document in Sanity Studio, inspect the webhook payload, confirm the GROQ result, check the Pulumi Cloud update log, and verify the frontend route, redirect, or preview environment. Add failure handling before production, including retry rules, signature verification, and a rollback plan for bad config.


06 β€” Code

Code example

typescript
import { createClient } from "@sanity/client";
import { LocalWorkspace } from "@pulumi/pulumi/automation";
import type { NextRequest } from "next/server";

const sanity = createClient({
  projectId: process.env.SANITY_PROJECT_ID!,
  dataset: process.env.SANITY_DATASET!,
  apiVersion: "2025-02-19",
  token: process.env.SANITY_READ_TOKEN!,
  useCdn: false,
});

export async function POST(req: NextRequest) {
  const { _id } = await req.json();

  const site = await sanity.fetch(
    `*[_id == $id][0]{
      stackName,
      domains,
      "redirects": redirects[]{from, to, permanent}
    }`,
    { id: _id }
  );

  if (!site?.stackName) {
    return Response.json({ skipped: true }, { status: 202 });
  }

  const stack = await LocalWorkspace.selectStack({
    stackName: site.stackName,
    workDir: process.env.PULUMI_WORK_DIR!,
  });

  await stack.setConfig("site:domains", {
    value: JSON.stringify(site.domains ?? []),
  });

  await stack.setConfig("site:redirects", {
    value: JSON.stringify(site.redirects ?? []),
  });

  const result = await stack.up({ onOutput: console.log });

  return Response.json({
    stack: site.stackName,
    changes: result.summary.resourceChanges,
  });
}

07 β€” Why Sanity

How Sanity + Pulumi works

Build your Pulumi integration on Sanity

Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect publish events with Pulumi infrastructure updates.

Start building free β†’

08 β€” Comparison

CMS approaches to Pulumi

CapabilityTraditional CMSSanity
Infrastructure-safe content modelingUses schema-as-code in Sanity Studio, so fields that feed Pulumi can be reviewed, versioned, and tested like application code.
Triggering Pulumi on publishWebhooks can fire on specific mutations, and Functions can run server-side logic when content changes.
Field-level query controlGROQ can select only the fields Pulumi needs and join referenced documents in one query.
Editor control with developer guardrailsDevelopers define the editorial interface, validation rules, and allowed fields, while editors work in Sanity Studio.
Long-running infrastructure updatesWebhooks can call a dedicated worker for Pulumi Automation API, while Functions can trigger Pulumi Cloud Deployments for lighter jobs.
Multi-channel reuseThe same structured content can feed Pulumi, websites, mobile apps, and AI agents through APIs like GROQ and Agent Context.

09 β€” Next steps

Keep building

Explore related integrations to complete your content stack.

Ready to try Sanity?

See how Sanity's Content Operating System powers integrations with Pulumi and 200+ other tools.