Snap CD vs Terragrunt
Terragrunt is one of the most popular tools in the Terraform ecosystem. It solves real problems — repetitive backend configuration, duplicated provider blocks, and the lack of a built-in way to orchestrate multiple Terraform root modules. If you've used Terragrunt, you've probably appreciated how much boilerplate it eliminates.
Snap CD solves a different (overlapping) set of problems: persistent orchestration, cross-state dependency management, approval workflows, and granular access control. This guide compares the two so you can decide which fits your situation — or whether you might use elements of both.
Architecture
This is the fundamental difference.
Terragrunt
Terragrunt is a local CLI tool. It runs on the machine where you invoke it — your laptop, a CI runner, a bastion host. There's no server, no persistent state tracking, no web UI, no API. When the process exits, the orchestration is done.
Snap CD
Snap CD is a Server + Runner architecture. The Server (snapcd.io or self-hosted) handles configuration, dependency tracking, source monitoring, and logging. Runners are lightweight agents deployed in your infrastructure that execute the actual Terraform commands. The Server persists the full history of plans, applies, and approvals.
What this means in practice: Terragrunt orchestrates one run at a time — if you want continuous deployment, you build that in CI. Snap CD continuously monitors for changes and orchestrates deployments across your entire infrastructure graph, with a persistent audit trail.
Dependency management
Both tools solve the problem of wiring outputs from one Terraform root module into another. The approaches are fundamentally different.
Terragrunt
Terragrunt's dependency blocks read outputs directly from another module's state file:
# compute/terragrunt.hcl
dependency "networking" {
config_path = "../networking"
}
inputs = {
vpc_id = dependency.networking.outputs.vpc_id
private_subnet_ids = dependency.networking.outputs.private_subnet_ids
}
When you run terragrunt run-all apply, Terragrunt builds the dependency graph, applies modules in the correct order, and passes outputs between them.
This works well, but has constraints:
- State access: the machine running Terragrunt needs read access to every module's state backend. For
run-allacross 20 modules spanning AWS, Azure, and GCP, that's a lot of credentials in one place. - No change detection: Terragrunt doesn't know when outputs change. You run
run-alland it re-plans everything, or you manually decide which modules to touch. - No cascading: if networking's outputs change, Terragrunt doesn't automatically trigger a re-plan of compute. You re-run
run-alland hope the dependency ordering handles it. - Limited dependency modelling: Terragrunt's
dependencyblocks are static references — there's no way to fan out dependencies withfor_eachorcount. Cross-stack dependency support was added in v1.1.0, but the wiring is still manual — there's no automatic cascading when upstream outputs change. - Local execution: the entire graph executes in one process on one machine. For large graphs, this can be slow — you're waiting for module A to finish before module B starts (unless they're independent).
Snap CD
Snap CD tracks dependencies server-side via snapcd_module_input_from_output resources:
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 after an apply, the Server automatically queues compute for re-planning. Each Runner only needs access to its own Module's state backend — not every Module's.
DRY configuration
This is Terragrunt's strongest feature, and Snap CD doesn't try to compete here.
Terragrunt
Terragrunt eliminates repetitive Terraform boilerplate:
# root terragrunt.hcl
remote_state {
backend = "s3"
config = {
bucket = "my-terraform-state"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
key = "${path_relative_to_include()}/terraform.tfstate"
}
}
generate "provider" {
path = "provider.tf"
if_exists = "overwrite"
contents = <<EOF
provider "aws" {
region = "us-east-1"
}
EOF
}
Every child module inherits this configuration via include. No more copying backend blocks into 30 modules.
Snap CD
Snap CD takes a different approach: your Terraform code is standard Terraform with its own backend configuration. If you want to reduce boilerplate in the Terraform code itself, you use Terraform's native mechanisms (shared modules, .tf files). Snap CD manages what's around the Terraform code — when to run it, what inputs to provide, who can approve it — not the code's internal structure.
If your primary pain point is Terraform boilerplate and you're a small team that doesn't need persistent orchestration, Terragrunt may be the better fit.
Execution model
Terragrunt
Terragrunt's run-all executes the dependency graph in a single process. It parallelises independent modules (configurable with --parallelism) but everything runs on one machine. If the process is interrupted, you restart from the beginning — there's no checkpointing.
run-all has a deeper limitation: it has no internal knowledge of which modules in the selection have already been deployed and which haven't.
First-time deployments fail. When you run terragrunt run-all apply on a fresh stack, Terragrunt tries to resolve all dependency block outputs before any applies happen. If module B depends on module A's outputs and neither has been deployed, the dependency resolution fails — module A has no state yet, so there are no outputs to read. You work around this with mock_outputs, but those mocks are a maintenance burden and mask real errors. (#2714, #1246)
Plans fail on fresh stacks for the same reason. terragrunt run-all plan resolves dependency outputs upfront. On a stack where not every module has been deployed, plans for downstream modules fail because upstream outputs don't exist yet. (#2907)
Partial failures leave you in an unknown state. If run-all apply succeeds for modules A and C but fails on module B, Terragrunt has no record of what succeeded. Re-running run-all apply re-applies everything, including the modules that already succeeded. For idempotent applies this is usually fine, but it wastes time and can trigger unexpected changes if upstream state has shifted.
Partial teardowns are fragile. run-all destroy has the inverse problem: if some modules are destroyed and others fail, re-running doesn't reliably skip the already-destroyed ones. (#3183)
Snap CD
Snap CD distributes execution across Runners. The Server queues work, Runners pick up jobs, and results are tracked persistently. If a Runner goes down mid-apply, the Server knows where it stopped. Independent Modules execute on different Runners in parallel without sharing a machine.
The Server tracks which Modules have been deployed, which have pending plans, and which are waiting on dependencies. First-time deployments, partial failures, and teardown ordering are all handled — these aren't edge cases when the orchestrator has persistent state.
For a graph of 5 modules, the difference barely matters. For 50 modules across multiple cloud providers, it changes how you think about execution.
Change detection and drift
Terragrunt
Terragrunt has no built-in change detection or drift detection. You run terragrunt run-all plan when you want to see what changed. In CI, this typically means running on every commit or on a schedule.
Snap CD
Snap CD monitors your Git sources continuously. When a new commit lands on the branch a Module watches, a plan is triggered automatically. When an upstream Module's outputs change, dependent Modules are re-planned. You don't need to set up CI triggers or cron jobs — the Server handles it.
Drift detection runs on a configurable schedule per Module. When drift is detected, a plan is created and routed through the normal approval workflow.
Approval gates
Terragrunt
Terragrunt has no approval mechanism. If you want approval before apply, you build it in CI — typically a manual approval step in your pipeline, or a PR-based workflow where merging triggers the apply.
Snap CD
Snap CD has built-in approval thresholds. You can require N approvals before a plan is applied, scoped per Module or per Stack. Anyone with the Approver role at the Module's scope (or above) can approve. Approvals are tracked in the Server with a full audit trail.
Access control
Terragrunt
Terragrunt is a CLI tool with no concept of users, roles, or permissions. Access control is handled externally — whoever can run terragrunt apply on the machine has full access to whatever that machine's credentials allow.
Snap CD
Snap CD has hierarchical RBAC: roles scoped to organisations, Stacks, Namespaces, Modules, and Runners. A user can be Contributor on test but Reader on prod. A service principal can have Approver on staging but nothing on production. These permissions apply uniformly across the web UI, API, and Terraform provider.
Secrets
Terragrunt
Terragrunt doesn't support injecting secrets without writing them to disk. Sensitive values passed through inputs end up in .auto.tfvars files on the filesystem.
Snap CD
Snap CD stores secrets encrypted and injects them as inputs and environment variables at runtime. Sensitive values never touch the filesystem in plaintext. Secrets are scoped to the Stack, Namespace, or Module level and bound via snapcd_module_input_from_secret.
Learning curve
Terragrunt
Terragrunt has its own configuration language — terragrunt.hcl — with include, dependency, generate, inputs, locals, and other blocks. It's HCL-based so it's familiar to Terraform users, but it's a distinct dialect with its own semantics. Teams need to learn how Terragrunt's inheritance model works, how dependency mock outputs behave, and how run-all traverses the graph.
Snap CD
Snap CD configuration is done either through the web UI or via the Terraform provider (standard Terraform resources). If you already know Terraform, you know how to configure Snap CD — there's no separate language to learn.
Lock-in
Both tools score well here.
Terragrunt
Terragrunt wraps your Terraform code but doesn't modify it. Your .tf files are standard Terraform. If you remove Terragrunt, you need to add backend blocks and provider configurations back into each module, but the infrastructure code itself is unchanged.
Snap CD
Snap CD is similarly non-invasive. Your 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 need to change — you just need another way to orchestrate it.
Comparison table
| Dimension | Terragrunt | Snap CD |
|---|---|---|
| Architecture | Local CLI wrapper | Server + distributed Runners |
| Dependencies | dependency blocks, local state reads |
snapcd_module_input_from_output, server-side |
| DRY config | Strong (include, generate) |
Not addressed (use standard Terraform) |
| Execution | Single process, one machine | Distributed across Runners |
| Cascading | Manual (run-all) |
Automatic on output change |
| Change detection | None (manual/CI) | Continuous source monitoring |
| Drift detection | None | Scheduled per Module |
| Approval gates | None (build in CI) | Built-in with configurable thresholds |
| RBAC | None | Hierarchical, scoped to Stack/Namespace/Module |
| Secrets | Written to disk as .auto.tfvars |
Encrypted, injected at runtime |
| Audit trail | None (check CI logs) | Persistent server-side history |
| Web UI | None | Dashboard with plan review |
| State access | Runner needs access to all states | Each Runner accesses only its own Module's state |
| Lock-in | Low — standard Terraform code | Low — standard Terraform code |
| Open source | Yes (MIT) | Runner is source-available; Server is SaaS or self-hosted |
| Learning curve | terragrunt.hcl dialect |
Terraform provider or web UI |
When to choose which
Terragrunt is a good fit when:
- You're a solo operator or small team that primarily needs DRY configuration.
- Your dependency graph is small enough to run locally or in a single CI job.
- You don't need persistent orchestration — running
terragrunt run-all applyon demand or from CI is sufficient. - You want a free, open-source tool with no server infrastructure to manage.
Snap CD is a good fit when:
- You need continuous, event-driven deployment — not just on-demand runs.
- Your dependency graph spans multiple cloud providers or teams, and you need distributed execution.
- You want approval gates, RBAC, and an audit trail without building them yourself.
- You're scaling beyond what a single CI job can orchestrate.
- You want automatic cascading: when one module's outputs change, dependents re-plan without manual intervention.
See also
- Modular Deployments — deep dive into Snap CD's Module, input, and dependency system
- Self-Hosted Terraform Runners with Credential Isolation — how Runners scope credentials per environment
- Splitting a Terraform Monolith — approaches to breaking a monolith into smaller states
- Non-invasive Orchestration — how Snap CD runs standard Terraform commands without wrappers
- Event-driven Continuous Deployment — automatic cascading vs manual
run-all
Intelligent GitOps for Infrastructure as Code. Automate, orchestrate, and scale your infrastructure deployments with confidence.
© 2026 Snap CD. All rights reserved.