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-all across 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-all and 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-all and hope the dependency ordering handles it.
  • Limited dependency modelling: Terragrunt's dependency blocks are static references — there's no way to fan out dependencies with for_each or count. 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 apply on 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

Snap CD

Intelligent GitOps for Infrastructure as Code. Automate, orchestrate, and scale your infrastructure deployments with confidence.


© 2026 Snap CD. All rights reserved.

An unhandled error has occurred. Reload 🗙