Snap CD vs Pulumi Deployments
Pulumi and Terraform represent two different philosophies of infrastructure as code. Pulumi lets you define infrastructure in general-purpose languages — TypeScript, Python, Go, C# — with the full power of loops, conditionals, and type systems. Terraform uses HCL, a purpose-built declarative language. Both work. The question becomes: once your infrastructure is defined, how do you orchestrate deployments across multiple stacks at scale?
Pulumi Deployments is Pulumi's answer — a managed orchestration layer built into Pulumi Cloud. Snap CD is a deployment orchestrator for Terraform and OpenTofu (with Pulumi support in preview). This guide compares the two approaches to help you decide which fits your team.
The language question
This is the most visible difference, but it's worth addressing upfront because it shapes everything else.
Pulumi programs are real code. You can unit test them, share logic via packages, and use your language's ecosystem. A database module in TypeScript can share interfaces with the application that uses it. This is genuinely powerful for teams with strong software engineering practices.
Terraform uses HCL — a declarative language that's simpler but more constrained. You can't write a for loop that conditionally creates resources based on an API call. But the constraint is also the point: a Terraform plan is a complete, reviewable description of what will change.
Snap CD is agnostic. It orchestrates whatever IaC tool your runners execute. Today that's Terraform and OpenTofu (with Pulumi in preview). Your choice of language doesn't change how Snap CD manages dependencies, approvals, or execution.
Architecture
Pulumi Deployments
Pulumi Deployments runs your Pulumi programs on Pulumi Cloud's infrastructure. You configure deployment settings per stack, and Pulumi Cloud handles execution, state management, and secrets.
This means:
- Your code runs in Pulumi's environment. Pulumi programs can contain arbitrary code — API calls, file reads, network requests. That code runs on Pulumi Cloud's workers, which need access to your cloud providers and potentially your internal network.
- Secrets are managed by Pulumi Cloud. Stack secrets are encrypted with keys managed by Pulumi Cloud (or your own KMS via the paid tiers). This is convenient but means secrets pass through their infrastructure.
- Self-hosted agents are available for teams that need execution inside their own network, but the control plane remains in Pulumi Cloud.
Snap CD
Snap CD separates orchestration from execution. The Server (snapcd.io or self-hosted) handles configuration, dependency tracking, scheduling, and logging. Runners — lightweight agents deployed in your infrastructure — handle the actual execution.
- Code and credentials stay in your environment. Runners clone your repo and execute
terraform plan/terraform apply(orpulumi preview/pulumi up) locally. Nothing is uploaded to snapcd.io. - The server is available for self-hosting under a source-available license. There's no separate "enterprise" tier.
- Runners connect outbound via authenticated WebSocket. No inbound firewall rules needed.
Cross-stack dependencies
This is where the orchestration models diverge most significantly.
Pulumi: StackReferences
Pulumi's StackReference reads outputs from another stack:
const network = new pulumi.StackReference("org/networking/prod");
const vpcId = network.getOutput("vpcId");
This is similar to Terraform's terraform_remote_state — it reads the other stack's state to extract outputs. The problems are also similar:
- Cyclic dependencies have no resolution. If stack A needs an output from stack B and stack B needs an output from stack A, there's no way to express this. You have to restructure your stacks.
- Missing references crash the preview. If the referenced stack doesn't exist yet (a fresh deployment), the
StackReferencefails. You can't catch this with try/catch — the preview fails before your error handling runs. - Aliases break across stacks. Resource aliases — used for refactoring without destroying and recreating resources — don't work across stack or project boundaries.
- No automatic cascading. When the networking stack's outputs change, nothing automatically re-deploys the compute stack. You trigger it manually, via CI, or by configuring Pulumi Deployments triggers.
Snap CD: declarative dependency wiring
Snap CD makes cross-state dependencies explicit with snapcd_module_input_from_output:
resource "snapcd_module_input_from_output" "vpc_id" {
module_id = snapcd_module.compute.id
input_kind = "Param"
name = "vpc_id"
output_module_id = snapcd_module.networking.id
output_name = "vpc_id"
}
The Server knows the full dependency graph. When networking's outputs change, compute automatically re-plans. There's no state file reading across Modules — Snap CD handles the data flow internally. First-time deployments work because the dependency graph determines execution order; upstream Modules apply before downstream Modules attempt to read their outputs.
Secrets management
Pulumi has built-in secrets management — values marked as secret are encrypted in the state file. This is a genuine advantage over Terraform, which stores secrets in plaintext in state. However, Pulumi's secrets lack project-level scoping, and pluggable secret backends have been requested for years.
Snap CD provides scoped secrets at the Stack, Namespace, and Module level, with support for Azure Key Vault as a secret backend. Sensitive outputs flowing between Modules are encrypted in Snap CD's secret store — they're never written to disk in plaintext.
Deployment triggers
Pulumi Deployments
Pulumi Deployments can trigger deployments from:
- Git push — a commit to a configured branch triggers a deployment.
- Click to Deploy — manual trigger from the Pulumi Cloud console.
- REST API — programmatic triggers via the Pulumi Cloud API.
- Drift detection — scheduled reconciliation runs that detect and optionally correct drift.
Cross-stack triggers require explicit configuration. If stack B should re-deploy when stack A's outputs change, you configure that relationship in Pulumi Cloud's deployment settings.
Snap CD
Snap CD monitors Git sources continuously and triggers plans when new commits land. Cross-Module cascading is automatic — when a Module's outputs change, all dependent Modules re-plan without additional configuration.
Drift detection is built in, with configurable check intervals per Module. Drift-triggered plans always route through the normal approval workflow.
Stack and project organisation
Pulumi's organisational model has been a source of friction. The relationship between stacks, projects, and state has led to confusion — particularly around how stack names, backends, and project namespacing interact. The design of self-managed state backends is tracked as an ongoing epic.
Snap CD uses a three-level hierarchy: Stacks contain Namespaces, which contain Modules. Permissions, secrets, and Runners can be scoped to any level:
resource "snapcd_stack" "prod" {
name = "prod"
}
resource "snapcd_namespace" "platform" {
name = "platform"
stack_id = snapcd_stack.prod.id
}
resource "snapcd_module" "networking" {
name = "networking"
namespace_id = snapcd_namespace.platform.id
source_url = "https://github.com/myorg/infra-networking.git"
runner_id = snapcd_runner.platform.id
}
Approval workflows
Pulumi Deployments has Review Stacks — ephemeral stacks that preview changes before they're applied to the target stack. Combined with Policy Packs (OPA/CrossGuard), this provides a review workflow for infrastructure changes.
Snap CD has built-in approval thresholds per Module. You set the number of required approvals, and anyone with the Approver role at the Module's scope (or above) can approve. Plans are visible in the dashboard for review before approval.
resource "snapcd_module" "production_network" {
name = "networking"
namespace_id = snapcd_namespace.prod.id
source_url = "https://github.com/myorg/infra-networking.git"
runner_id = snapcd_runner.prod.id
apply_approval_threshold = 2
}
Access control
Pulumi Cloud uses team-based access control with stack-level permissions. Teams can have read, write, or admin access to stacks. More granular policies require Policy Packs.
Snap CD has hierarchical RBAC with roles (Owner, Contributor, Reader, Approver) assignable at any level — organisation, Stack, Namespace, Module, or Runner. A user can be Contributor on the test Stack but Reader on prod, without writing policy code.
Lock-in
Pulumi Deployments requires Pulumi Cloud. There is no self-hosted version of Pulumi Deployments — it's a feature of the managed service. Your Pulumi programs themselves are portable (you can run pulumi up from the CLI), but the orchestration layer is tied to the platform.
Snap CD's Terraform code has no Snap CD-specific constructs. Runners execute standard terraform plan and terraform apply. If you leave Snap CD, your Terraform code doesn't change — you just need another way to orchestrate it. The Server itself is available for self-hosting.
Pricing
Pulumi Cloud offers a free tier for individuals. Team and enterprise tiers are priced per user, with Pulumi Deployments billed by deployment minutes. Resource count and stack count factor into higher tiers.
Snap CD prices based on modules managed, users, and module jobs. There's no per-runner cost. The pricing is consumption-based, not seat-based with feature gates.
Comparison table
| Dimension | Pulumi Deployments | Snap CD |
|---|---|---|
| IaC language | TypeScript, Python, Go, C#, Java, YAML | Terraform/OpenTofu (HCL), Pulumi (preview) |
| Architecture | Managed SaaS (Pulumi Cloud) | Orchestration server + self-hosted runners |
| Execution | Pulumi Cloud workers (or self-hosted agents) | Self-hosted runners only |
| Cross-stack dependencies | StackReferences (read other stack's state) | snapcd_module_input_from_output (server-managed) |
| Automatic cascading | Configurable triggers | Built-in on output change |
| Secrets | Built-in encryption (Pulumi Cloud KMS or custom) | Scoped secrets + external store integration |
| Approval workflow | Review Stacks + Policy Packs | Built-in approval thresholds + RBAC |
| Drift detection | Scheduled reconciliation | Scheduled with approval routing |
| Self-hosting | Agents only; control plane is SaaS | Full stack available (source-available license) |
| RBAC | Team-based, stack-level | Hierarchical, scoped to stack/namespace/module/runner |
| Pricing | Per user + deployment minutes | Per module/user/job |
When to choose which
Pulumi Deployments is a good fit when:
- Your team writes infrastructure in TypeScript, Python, Go, or C# and wants to keep that investment.
- You're already using Pulumi Cloud for state management and secrets.
- Review Stacks and Policy Packs meet your approval and policy needs.
- You prefer a fully managed platform and don't need to self-host the control plane.
Snap CD is a good fit when:
- You use Terraform or OpenTofu and want orchestration without changing your IaC tooling.
- Cross-state dependency management with automatic cascading is a primary need.
- You want full control over where execution happens — runners in your environment with your credentials.
- You need hierarchical RBAC scoped below the Stack level (Namespace, Module, Runner).
- Self-hosting the entire stack is a requirement.
Both? If your organisation uses both Terraform and Pulumi (common during migrations or when different teams have different preferences), Snap CD's Pulumi preview support means you can orchestrate both from a single control plane — each Module runs whatever IaC tool its Runner is configured for.
See also
- Modular Deployments — deep dive into Snap CD's Module, input, and dependency system
- Self-Hosted Terraform Runners with Credential Isolation — how Runners keep credentials in your network
- Managing Secrets in Terraform — scoped secrets and encrypted output wiring
- A Permission System Built for Infrastructure — hierarchical RBAC across Stacks, Namespaces, Modules, and Runners