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.
What Terragrunt is
Terragrunt is an open-source CLI tool by Gruntwork that wraps the terraform binary. It adds:
- DRY configuration — shared backend config, provider blocks, and common variables via
includeandgenerateblocks interragrunt.hclfiles. - Dependency orchestration —
dependencyblocks that read outputs from other modules' state files, plusrun-allto execute an entire dependency graph in one command. - Scaffold and catalog — templates for creating new modules from a standard layout.
It's a thin wrapper: your Terraform code stays standard, and Terragrunt generates the glue (backend blocks, .auto.tfvars files) before calling terraform underneath.
Architecture
This is the fundamental difference.
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 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, and cross-stack dependencies remain an open problem. - 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 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 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's run-all executes the dependency graph in a single process. It parallelises independent modules (configurable with --terragrunt-parallelism) but everything runs on one machine. If the process is interrupted, you restart from the beginning — there's no checkpointing.
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.
For a graph of 5 modules, this distinction barely matters. For 50 modules across multiple cloud providers, it changes how you think about execution.
The run-all problem
Terragrunt's 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)
These aren't edge cases — they're inherent to a stateless CLI that discovers the dependency graph at invocation time and has no persistent record of deployment status. Snap CD's Server tracks which Modules have been deployed, which have pending plans, and which are waiting on dependencies, so none of these scenarios arise.
Change detection
Terragrunt has no built-in change 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 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.
Approval gates
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 has built-in approval thresholds. You can require N approvals before a plan is applied, scoped per Module or per Stack. Approvals are tracked in the Server with a full audit trail.
Access control
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 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.
Learning curve
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 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 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 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 |
| DRY config | Strong (include, generate) |
Not addressed (use standard Terraform) |
| Dependencies | dependency blocks, local state reads |
snapcd_module_input_from_output, server-side |
| Cascading | Manual (run-all) |
Automatic on output change |
| Change detection | None (manual/CI) | Continuous source monitoring |
| Execution | Single process, one machine | Distributed across runners |
| Approval gates | None (build in CI) | Built-in with configurable thresholds |
| RBAC | None | Hierarchical, scoped to stack/namespace/module |
| 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.
Using both? It's possible but usually unnecessary. Some teams use Terragrunt for DRY configuration within their Terraform modules and Snap CD for orchestration across Modules. But since Snap CD already handles input/output wiring, the overlap in dependency management creates confusion. Terragrunt also doesn't support injecting secrets without writing them to disk — Snap CD injects secrets as inputs and environment variables at runtime, so sensitive values never touch the filesystem. In most cases, pick one approach for dependency orchestration and stick with it.
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