From OpenTofu to Argo CD: Building a Fully Automated AWS Control Plane with GitLab
Provisioning cloud environments by hand is one of the most reliable ways to end up with inconsistent servers, undocumented changes, and outages nobody can reproduce. Networking, subnets, clusters, certificates — each piece has its own console, its own failure modes, and its own way of drifting out of alignment. A recent walkthrough published on the GitLab blog, based on a live demo presented at AWS Summit Madrid 2025 by the Clober DevSecOps team (SATEC group), shows a different path: GitLab acting as the central operations brain, OpenTofu defining every AWS resource as code, and Argo CD keeping running applications in sync with what lives in Git. For sysadmins, hosting operators, and anyone managing workloads on AWS or similar clouds, the architecture is worth dissecting — including the parts that deserve caution.
Why Infrastructure as Code and GitOps Belong in Your Hosting Stack
The tutorial opens with the case for infrastructure as code (IaC), and it’s a familiar one to anyone who has rebuilt a broken server at 2 a.m.: environments should be reproducible, meaning the same configuration can be deployed anywhere; versioned, so every change is tracked in Git with full history; and automated, requiring no human interaction once defined.
GitOps extends those principles into the application lifecycle itself. Rather than manually applying changes to a Kubernetes cluster, the desired state of applications lives in a repository, and a GitOps operator — here, Argo CD — continuously ensures the cluster reflects what’s in Git. The claimed benefits map directly to operational realities hosting buyers care about:
- Consistency between environments, reducing “works in staging, breaks in production” surprises.
- Faster recovery, because rolling back is as simple as reverting a commit — effectively a restore path built into your workflow.
- Better collaboration, with changes reviewed and approved through merge requests.
- Improved security, since deployments happen without developers needing direct access to the cluster.
What makes this walkthrough notable is consolidation. GitLab supplies source code management, CI/CD pipelines, managed Terraform/OpenTofu state storage, and a built-in Container Registry — so provisioning, orchestration, and delivery all run through one platform rather than a patchwork of tools stitched together by scripts.
For a more detailed walkthrough of this part of the topic, read KVM VPS vs Container VPS: Docker, CI/CD, AI Agents, and Self-Hosting Compared.
Provisioning AWS Networking and EKS with OpenTofu Through Pipelines
The first stage defines the entire AWS foundation declaratively: the VPC, subnets, gateways, routing tables, load balancers, and an Amazon EKS control plane with worker node groups distributed across multiple availability zones. OpenTofu — the open-source fork of Terraform — executes these definitions inside GitLab CI/CD pipelines.
Configuration is deliberately kept out of the code. Environment variables such as TF_VAR_region (the example uses eu-west-1), TF_VAR_instance_type (t3.medium), TF_VAR_instance_count, and TF_VAR_cluster_name are declared under Settings > CI/CD > Variables in the parent GitLab group, inherited by all child projects alongside AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. All variables are marked protected, and sensitive ones like the AWS secret key are also masked, limiting exposure to secure pipeline contexts. Separating code from configuration keeps the same definition reusable across environments — a small discipline that pays off enormously during migrations or disaster recovery.
Related ServerSpan guide: VPS Hosting for DevOps Pipelines: Setting Up GitLab CI/CD on Your Own Server.
State handling deserves particular attention. The pipeline uses GitLab’s integrated HTTP backend for Terraform/OpenTofu state instead of a local file. According to GitLab’s documentation, this managed approach provides automatic encryption at rest, built-in versioning showing who changed what and when, access control through the existing GitLab permission model, and conflict-free collaboration. Role requirements are explicit: locking, unlocking, and writing state via tofu apply requires Maintainer or Owner, while reading state with tofu plan -lock=false is available from Developer upward. Support for GitLab-managed OpenTofu states arrived in GitLab 18.3, requiring the glab CLI 1.66 or later.
When the provisioning job completes successfully, a trigger job fires the next pipeline automatically — no human handoff between stages.
Bootstrapping Cluster Tooling Automatically: Argo CD, CertManager, and the Ingress Question
Once the EKS cluster exists, a secondary pipeline hosted in a separate repository installs the operational tooling. It uses OpenTofu’s Helm provider to manage Helm releases declaratively, downloading the kubeconfig from the newly created cluster and deploying three components:
- Argo CD, for GitOps-based application management.
- CertManager, for automated TLS certificate issuance and renewal.
- Ingress NGINX Controller, for ingress traffic routing.
Each Helm release lives in the OpenTofu configuration, so upgrading or rolling back cluster tooling means editing infrastructure code and letting the pipeline do the work — consistent with everything else in the stack.
There’s an important caveat buried in the walkthrough, and it’s the kind of detail that separates current guidance from stale copy-paste tutorials: the authors flag that Ingress NGINX Controller is being deprecated in the Kubernetes ecosystem, and recommend considering alternatives such as Traefik or an API-gateway-based approach for new deployments. If you’re standing up a cluster today, treat that ingress choice as an open architectural decision rather than a default.
A third pipeline then fires, applying the Argo CD application manifests that describe the sample web application — creating the aws-summit-app namespace, applying repository.yaml, application.yaml, and cluster-issuer.yaml, updating kubeconfig and credentials dynamically from CI/CD variables, and generating a Container Registry secret so pods can pull images securely using an access token. Once applied, the Argo CD controller detects the manifests and synchronizes actual cluster state with the repository.
Closing the Loop: Builds, Registry, and GitOps-Driven Delivery
The final piece is the application build-and-delivery cycle, structured around a clean GitOps separation: an Apps/ section holds source code repositories, while a Deployments/ section holds the Kubernetes manifests (deployment.yaml, ingress.yaml, service.yaml) for each application. Code and configuration stay independent but synchronized through automation.
The pipeline in the application repository compiles the source, builds a Docker image from the provided Dockerfile, tags it, and pushes it to the GitLab Container Registry sitting alongside the code. It then commits the new image tag — or opens a merge request — into the corresponding deployment repository. That commit is the entire deployment signal: Argo CD detects the change and synchronizes updated manifests into the cluster with no manual intervention.
The result is an environment where infrastructure comes from OpenTofu, cluster tools come from declarative Helm releases, and applications arrive purely through Git activity. Every change is reviewable, attributable, and reversible. The full source code for the demo is available publicly in the SATEC Public Demo Group on GitLab for teams wanting to reproduce it.
Key Takeaways Checklist
- Store all secrets as protected, masked CI/CD variables in a parent group — never hardcode credentials in
.tffiles or manifests. - Use a managed remote state backend (GitLab’s HTTP backend qualifies) with encryption, versioning, and role-based access instead of local state files.
- Treat Ingress NGINX as a legacy choice: evaluate Traefik or API-gateway approaches for new clusters.
- Separate app repositories from deployment manifest repositories so image updates and infrastructure changes stay independently reviewable.
- Design rollbacks as commit reverts, and verify they work before you need them under pressure.
- Chain pipelines with trigger jobs so provisioning, tooling bootstrap, and app deployment run as one auditable workflow.
Conclusion
This walkthrough isn’t introducing exotic technology — OpenTofu, Helm, Argo CD, and CI/CD pipelines are all established tools. What it demonstrates convincingly is integration: one platform holding code, state, registries, and pipelines, with three chained workflows turning a blank AWS account into a fully operational, Git-synced Kubernetes environment. For hosting teams and sysadmins, the appeal is operational risk reduction — fewer manual steps, cleaner recovery paths, and complete change traceability.
Be clear-eyed about the tradeoffs, though. You’re concentrating significant dependency on a single platform, your team needs genuine fluency in IaC and Kubernetes, and the demo doesn’t address cost visibility or multi-cloud portability — those questions remain open. Still, as a reference architecture for automating AWS infrastructure from commit to running application, it sets a high bar worth measuring your own stack against.