Case Study

Procal

From ClickOps to GitOps: Terraform, Atlantis, and Jenkins for a 4-service cost-estimate and approval platform.

Procal is an internal tool for building, submitting, and approving project cost estimates. Three roles (admin, PM, and approver) move an estimate through a Draft → Submitted → Approved / Rejected state machine, with every transition recorded in an immutable audit log. It was built and is operated by a small cohort team as part of the DevOps Institute program. The application itself is four services, but the real focus of the project was the infrastructure underneath it: moving from ClickOps and manual SSH deploys to a full Terraform + Atlantis GitOps workflow.

Application architecture

diegoSwiftUI, TestFlight
mapsNext.js, public BFF
doraFastAPI, internal API
RDS Postgres
SQS
bootsGo worker
Email (SMTP)

diego (the iOS approver app) never calls dora directly; it goes through maps' API proxy route, same as the web client.

Infrastructure: layered Terraform stacks

Atlantis + state bucketfoundation, local state
BootstrapVPC / NAT / subnets, imported and not re-applied
SharedALB / ACM / Route53 / VPC endpoints
Per-env commonsRDS / SQS / SES
Per-env servicesdora / maps / boots

One shared VPC across all environments, S3-backed Terraform state, applied through Atlantis GitOps. Direct local terraform apply is reserved for the foundation layer only.

Tech stack

Application

FastAPI
Next.js
Go
SwiftUI

Infrastructure

Terraform
Atlantis
AWS VPC
ALB
RDS
SQS
SES

CI/CD

Jenkins
Packer

Config-as-Code

Ansible
Molecule

Security & Lint

Checkov
tflint

Observability

Grafana

GitOps & deployment flow

  1. 1

    Open a PR

    A change to any Terraform stack is proposed via pull request.

  2. 2

    Atlantis auto-plans

    Atlantis detects the affected project(s) and runs a plan automatically; no manual trigger needed.

  3. 3

    tflint + Checkov gate the plan

    Both scanners run on every plan. Skipped checks (e.g. RDS Multi-AZ on throwaway learner environments) are documented inline with a reason, not silently ignored.

  4. 4

    Team reviews the plan output

    The plan diff is posted straight to the PR for review before anything touches real infrastructure.

  5. 5

    `atlantis apply` on approval

    Applying is a deliberate action: someone comments `atlantis apply -p <project>` once the plan looks right.

  6. 6

    Jenkins handles the rest

    Ansible-lint runs on every push; app deploys go out via Ansible (dev) or a bake-AMI → SSM → Terraform ASG refresh path (staging/prod); rollback redeploys a prior AMI.

Highlights

  • Terraform stacks are strictly dependency-layered (foundation → bootstrap → shared → per-env commons → per-env services), with apply/destroy order documented so nobody has to guess what depends on what.
  • Every plan runs through two independent static-analysis tools before a human even looks at it.
  • Infra changes are reviewed as code, in a PR, before they're applied, a deliberate move away from ad-hoc console changes and manual SSH deploys.
  • Staging and prod use an AMI-bake + Auto Scaling Group instance-refresh deploy model instead of direct SSH, so a rollback is just redeploying a known-good AMI.
  • Each backend service ships with its own Grafana dashboard (infra, application, and log panels), not a single generic one.

More Case Studies

aform

An Airtable Forms clone built around a queue-decoupled write path (RabbitMQ + worker), with a hardened deployment flow: systemd, Nginx, UFW, S3-backed backups, and a real k6 load test.

GoRabbitMQAWSPrometheusNginx
DevOps Institute
View case study →

Procal Ops Bot

A Claude-powered Telegram bot for incident triage, PR review, and infra ops, wired into Grafana, AWS, Bitbucket, SonarQube, and Jenkins via MCP.

Claude APIMCPPythonTelegramFastAPI
DevOps Institute
View case study →