What a 2-week CI/CD and IaC rebuild delivers

A two-week CI/CD and IaC rebuild should leave a push gate, encrypted state, plan brakes and deploys by digest.

5 min read

A two-week calendar of ten day cells ending in a completed check, with a five-step delivery pipeline drawn beneath

In two weeks, a CI/CD and infrastructure-as-code rebuild should leave you with four things that work end to end. A gate that runs before every push. Infrastructure described in code, with encrypted state. A plan step that stops destructive changes. And releases cut by tag and deployed by image digest. Our CI/CD and IaC package does this for USD 4,000 in two weeks.

“We’ll set up a pipeline” tells you nothing, so here are the actual commands. It’s the setup we run on the platforms we build, an AI marketing platform’s among them. For a portfolio-analytics SaaS platform, the promotion pipeline we built checks the same thing at the end: that what runs is what was released.

How do you know your CI/CD is working?

Use DORA’s metrics, which split delivery into throughput (change lead time, deployment frequency, failed deployment recovery time) and instability (change fail rate, deployment rework rate). DORA’s guide says speed and stability “are not tradeoffs”, so a rebuild that makes you deploy faster and break more hasn’t worked. If you want this built for you, the CI/CD and IaC package is our fixed-scope version of it.

Whether you measure these today is a fair question to ask yourself before you start. Plenty of teams think they do and don’t, and improving a number nobody trusts is wasted effort. How to measure DORA metrics goes through it from raw pipeline data.

What does the push gate do?

The cheapest place to catch a broken build is the developer’s machine, before anything leaves it. Ours runs four checks in parallel before every push: secrets scanning, typecheck, lint and build. If one fails, the push doesn’t happen.

A minimal version for a Node project:

#!/usr/bin/env bash
# scripts/push-gate.sh, run as: scripts/push-gate.sh && git push
set -euo pipefail
gitleaks git --redact --log-opts="origin/main..HEAD" &
pnpm typecheck & pnpm lint & pnpm build &
for job in $(jobs -p); do wait "$job"; done

wait on each job propagates its exit code, so one red check fails the whole gate. In some repos we add architecture boundary checks and a dead-code budget to the same script. It runs in seconds, locally, and the same rules apply to everyone. A rule that only some people’s machines enforce isn’t a rule.

How do you keep infrastructure-as-code state safe?

The state file maps your code to real resources, and it often holds sensitive values. Losing it hurts. Leaking it is worse. We use OpenTofu with encrypted state, and the documented setup is a few lines:

terraform {
  encryption {
    key_provider "pbkdf2" "main" {
      passphrase = var.state_passphrase
    }
    method "aes_gcm" "main" {
      keys = key_provider.pbkdf2.main
    }
    state {
      method   = method.aes_gcm.main
      enforced = true
    }
    plan {
      method   = method.aes_gcm.main
      enforced = true
    }
  }
}

Two warnings from the OpenTofu docs are worth repeating. With encryption on, state and plan files “become unrecoverable without the appropriate encryption key”. And on an existing project you need a temporary unencrypted fallback method to migrate. In production I’d use a cloud key management service rather than a passphrase, and the key never goes in the repository.

What is a destructive-change brake?

A plan that deletes a database looks exactly like a plan that adds a tag, unless something reads it. Ours does. OpenTofu’s JSON plan lists every resource change with its actions, and the docs suggest scanning that list for "delete", which catches deletes and replacements alike:

tofu plan -out=plan.bin
deletes=$(tofu show -json plan.bin \
  | jq '[.resource_changes[] | select(.change.actions | index("delete"))] | length')
if [ "$deletes" -gt 0 ]; then
  echo "Plan deletes or replaces $deletes resource(s); needs explicit approval." >&2
  exit 1
fi

Resources that hold data also get prevent_destroy in their lifecycle. Removing one then takes a deliberate commit.

What does “release by tag, deploy by digest” mean?

A release is an annotated SemVer tag on the main branch: git tag -a v1.4.0 -m "..." && git push origin v1.4.0. The tag starts the pipeline, which builds the image, runs migrations if the repo has them, and deploys.

The deploy goes by the image’s content hash, registry/app@sha256:…. A mutable tag like latest can point at different bytes tomorrow. A digest names exactly one set of bytes, so you can prove that what runs in production is what you built and tested:

docker buildx build --push -t "$IMAGE:$TAG" --metadata-file meta.json .
DIGEST=$(jq -r '."containerimage.digest"' meta.json)
gcloud run deploy app --image="$IMAGE@$DIGEST" --region="$REGION"

Afterwards the pipeline reads back the digest the platform is really running and compares it with the one it built. If they differ, the release isn’t done. That’s how every production deploy of an AI marketing platform we built was verified across its 89 releases in 10 days. It’s also how the first release of a portfolio-analytics SaaS platform through its new pipeline was checked against its 12 running images.

What happens in the two weeks?

The exact plan is agreed on day one, but the order is usually the same.

Week 1 is reading and gating. We inventory what exists: repositories, pipelines, cloud accounts, manual steps, secrets. We stand up the local push gate. Then we bring existing infrastructure under code, importing what already runs instead of recreating it, with encrypted remote state.

Week 2 is the release path. We add the plan brake and prevent_destroy on anything that holds data, then build the tag-driven pipeline: build, migrate, deploy by digest, read back. Then we cut a real release through it and roll one back.

Week one inventories and gates, week two builds the release path. The exit is a real change reaching productionWeek one inventories and gates, week two builds the release path. The exit is a real change reaching production
The exact plan is agreed on day one, but the order is usually the same.

The exit criterion is a real change going from commit to production through the new path while you watch. A pipeline that has never shipped anything isn’t delivered.

Do I need to change my CI tool?

Usually not. The age of the CI server matters little, Jenkins included. Ask whether the pipeline definitions live in the repository, whether a run is reproducible, and whether the deploy step is tied to an immutable artifact. If all three are yes, switching tools is optional. If any is no, a new tool won’t fix it.

The tools are rarely the hard part. The hard part is the decisions nobody wants to make: who can approve a destructive plan, what blocks a release, how a rollback works, and who has actually tested it. Two weeks is enough when the scope is one product and its environments. With many products on different stacks, start with a Discovery, or treat the first product as the template for the rest. If you’re also weighing the tool underneath, OpenTofu vs Terraform covers the license question.

Describe your current release in one sentence. If it contains the words “someone runs”, this package is for you. Start the CI/CD & IaC package: USD 4,000, 2 weeks, or check every other package on the pricing page.

Sources, accessed 2026-10-06: DORA, software delivery performance metrics, OpenTofu docs, State and Plan Encryption.

  • devops
  • ci/cd
  • infrastructure as code
  • opentofu

Read this post in SpanishRead this post in Portuguese

New posts by email

Notes on rates, platform engineering, DevOps, SRE, QA and AI engineering, with the prices and figures from our own work.

DX Clouditive LLC (7901 4th St N, Ste 300, St Petersburg, FL 33702, USA, info@dxclouditive.com) will email you new posts and a weekly digest once you confirm. You can withdraw consent anytime with the unsubscribe link in any email. Read our privacy policy. Unsubscribe anytime from any email.

Keep reading

All posts

Tell us your case.

We reply to every request within 1 business day. We sign an NDA before the call if you ask.