Snap CD vs Argo CD / Flux
Argo CD and Flux are the standard GitOps controllers for Kubernetes. If your team already uses one of them to deploy applications, it's natural to wonder whether the same tool can handle your Terraform infrastructure too.
The short answer: they solve different problems. Argo CD and Flux are designed to sync Kubernetes manifests into a cluster. Snap CD is designed to orchestrate Terraform and OpenTofu deployments across independent states. Understanding where each tool fits — and where they complement each other — saves you from forcing a square peg into a round hole.
Different problem domains
Argo CD and Flux watch a Git repository for Kubernetes manifests (YAML, Helm charts, Kustomize overlays) and continuously reconcile the cluster to match. The workflow is elegant: commit a change, the controller picks it up, the cluster converges.
Snap CD watches Git repositories for Terraform/OpenTofu modules and orchestrates plan → approve → apply workflows across multiple independent states. It manages the dependency graph between those states so that when one module's outputs change, downstream modules automatically re-plan.
The two tools operate at different layers of the stack:
| Layer | Tool | What it manages |
|---|---|---|
| Application deployments | Argo CD / Flux | Kubernetes manifests, Helm releases, Kustomize overlays |
| Infrastructure | Snap CD | Terraform/OpenTofu modules — VPCs, databases, clusters, DNS, IAM |
Trying to use one tool for both layers means working against its design rather than with it.
The state model difference
This is the fundamental reason Kubernetes GitOps controllers don't translate well to Terraform.
Argo CD / Flux: the cluster is the state. You declare what you want (a Deployment, a Service, a ConfigMap), and the controller applies it. Kubernetes handles the reconciliation loop — it knows what exists, what's desired, and how to converge. There's no external state file. If the controller restarts, it reads desired state from Git and actual state from the cluster API, and carries on.
Terraform: state is explicit and external. Terraform maintains a state file that maps your configuration to real-world resources. Every plan reads this state, every apply updates it. The state file must be stored somewhere (S3, Azure Blob, GCS, Terraform Cloud), locked during operations to prevent concurrent writes, and passed consistently between runs.
This difference has several consequences:
- No idempotent apply. In Kubernetes, applying the same manifest twice is harmless — the API server recognises nothing changed. In Terraform, running
applywithout the correct state can cause duplicate resources, orphaned infrastructure, or destructive changes. - Locking matters. Two Argo CD sync operations on the same app won't corrupt state. Two concurrent
terraform applyoperations on the same state can. - Plan review. Kubernetes manifests are declarative and relatively predictable. Terraform plans can include resource replacements, data source lookups, and provider-specific side effects that need human review before applying.
A GitOps controller that treats Terraform like "just another set of files to sync" misses all of this.
Dependency management
Argo CD has sync waves and resource hooks for ordering resources within a single application. You can say "create the namespace before the deployment" or "run a Job after the main resources sync." Flux has similar ordering via dependencies between Kustomizations.
These mechanisms work within a single reconciliation scope — one app or one Kustomization. They don't address cross-state dependencies between independent Terraform root modules.
Snap CD manages dependencies between completely independent Terraform states. A networking Module's VPC ID flows automatically into a compute Module's input. When networking's outputs change, compute re-plans without manual intervention:
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"
}
This is the core problem Snap CD solves: making multiple Terraform states behave as a connected system rather than isolated silos.
Execution model
Argo CD runs as a set of controllers inside your Kubernetes cluster. It needs access to the cluster API and to the Git repositories it watches. Everything happens in-cluster.
Flux is similar — it runs as a set of controllers in the cluster, reconciling resources via the Kubernetes API.
Snap CD separates orchestration from execution. The Snap CD Server handles configuration, scheduling, and the dependency graph. Runners — lightweight agents that execute the actual terraform plan and apply commands — can run anywhere: in a Kubernetes pod, on a VM, in a container on a developer's machine. Each Runner connects to the Server via an authenticated WebSocket.
This separation matters for Terraform because:
- Credential isolation. A Runner deploying to production Azure only needs production Azure credentials. A Runner deploying to a test AWS account only needs test AWS credentials. There's no single execution context that accumulates every credential.
- Network access. Terraform often needs to reach cloud provider APIs that aren't accessible from inside a Kubernetes cluster. Runners can be placed wherever network access is available.
- No Kubernetes dependency. You don't need a Kubernetes cluster to use Snap CD. Runners are standalone binaries.
Running Terraform from Argo CD
Some teams have tried to bridge the gap by running Terraform from within the Argo CD ecosystem. The common approaches:
Custom Argo CD applications with config management plugins
You write a plugin that runs terraform plan and apply when Argo CD detects changes. The problems:
- No state management. Argo CD doesn't know about Terraform state files. You need to handle state storage, locking, and passing externally.
- No plan review. Argo CD's sync model is "apply immediately" (or with manual sync, "apply on click"). There's no built-in concept of reviewing a plan diff, requiring approvals, and then applying.
- No cross-state dependencies. Each Argo CD application is independent. There's no mechanism to wire outputs from one Terraform state as inputs to another.
- Error handling. A failed
terraform applyleaves Argo CD in a degraded state. Argo CD expects to be able to retry syncs, but retrying a partially-applied Terraform change isn't always safe.
Terraform operator (Kubernetes CRDs)
Projects like the HashiCorp Terraform Cloud Operator or community Terraform operators let you define Terraform workspaces as Kubernetes custom resources. Argo CD then manages these CRDs like any other Kubernetes resource.
This is cleaner than plugins, but adds complexity:
- You're managing Terraform through two layers of abstraction: Git → Argo CD → CRD → Operator → Terraform Cloud/Enterprise → Terraform.
- Debugging a failed apply means looking at the CRD status, the operator logs, and the Terraform run logs — three different places.
- You're coupling your infrastructure management to a Kubernetes cluster. If the cluster goes down, you can't deploy infrastructure.
- Cross-state dependencies still require external wiring.
These approaches can work for simple cases, but they're fighting the tool rather than using one designed for the job.
When to use both
The strongest pattern is using both tools at their respective layers:
Snap CD Argo CD / Flux
────── ──────────────
VPC, subnets, NAT gateways Application Deployments
EKS/AKS/GKE cluster Helm releases
RDS databases ConfigMaps, Secrets
IAM roles, policies Ingress rules
DNS zones, records CronJobs, Jobs
Storage accounts Service meshes
Snap CD deploys the infrastructure — including the Kubernetes cluster itself. Once the cluster exists, Argo CD or Flux takes over for application-level resources.
The handoff is clean:
- Snap CD provisions the cluster, writes the
cluster_endpointand credentials as outputs. - Argo CD / Flux is bootstrapped onto the cluster (potentially by Snap CD as part of the cluster module).
- Application teams use Argo CD / Flux for their deployments. Infrastructure teams use Snap CD for infrastructure changes.
There's no overlap and no conflict. Each tool does what it was designed for.
A practical example
A typical setup might look like this:
Snap CD modules:
networking/ → VPC, subnets (outputs: vpc_id, subnet_ids)
cluster/ → EKS cluster (inputs: vpc_id, subnet_ids; outputs: cluster_endpoint)
database/ → RDS instance (inputs: subnet_ids; outputs: connection_string)
argocd-bootstrap/ → Installs Argo CD onto the cluster
Argo CD applications:
frontend/ → Deployment, Service, Ingress
backend/ → Deployment, Service, ConfigMap (with database connection string)
monitoring/ → Prometheus, Grafana Helm releases
When the networking module changes a subnet, Snap CD cascades the change to the cluster and database modules. Argo CD doesn't need to know — it continues managing application deployments on whatever cluster exists.
When a developer pushes a new container image tag, Argo CD picks it up and syncs the deployment. Snap CD doesn't need to know — it continues managing the infrastructure underneath.
Summary
| Dimension | Argo CD / Flux | Snap CD |
|---|---|---|
| Primary target | Kubernetes manifests | Terraform/OpenTofu modules |
| State model | Cluster is the state | Explicit state files |
| Reconciliation | Continuous sync | Plan → approve → apply |
| Dependencies | Sync waves within an app | Cross-state output wiring |
| Execution | In-cluster controllers | Distributed Runners (anywhere) |
| Credential model | Cluster RBAC + secrets | Per-Runner credential isolation |
| Plan review | N/A (declarative sync) | Built-in approval gates |
The tools aren't competitors — they're complements. Use Argo CD or Flux for what they're built for (Kubernetes app deployments), and Snap CD for what it's built for (Terraform infrastructure orchestration). Trying to make one tool do both means neither works well.
See also
- Modular Deployments — how Snap CD's Module system wires cross-state dependencies
- Self-Hosted Terraform Runners with Credential Isolation — how Runners provide per-environment credential isolation
- Event-driven Continuous Deployment — how Snap CD cascades changes automatically
- Non-invasive Orchestration — Snap CD runs standard Terraform commands, no wrappers