Ivan Jairus

DevSecOps

Platform engineer who turns release days into non-events — CI/CD pipelines, security gates and release automation for banking-grade systems.

40+ repos · 5 artifact types · 3 environments · 1 gateway

Stack I work in daily GitLab CIJenkinsJiraGroovyBashDockerOpenShiftLinuxServers / VMsSonarQubeVaultTrivyFirebaseTestFlightPythonGit
Ivan Jairus — DevSecOps

Release pipeline stages: commit (git push), build (maven, gradle, npm), scan (trivy, sonarqube), approval gate (five-layer chain), deploy to SIT, promote to UAT, release to PROD with a release ledger.

scan · gate — fail closed

.gitlab-ci.ymlthe whole file, in every service repo
# this is all a service repo carries
include:
  - project: 'core/pipeline-gateway'
    file: '/gateway.yml'
sample in the real format — project name sanitized, no internal links

Pipelines other people are happy to inherit.

DevSecOps, Jakarta. I design and maintain the release machinery behind banking-grade systems — and I document the trade-offs, not just the wins.

artifacts I ship: jar · image · apk/aab · ipa · web bundle — one standard for all five

Things I built and still maintain

Sanitized descriptions — no hostnames, no tenant names, no internals. The engineering is real; the labels are not.

ChatOps Release Orchestrator

Webhook-driven orchestrator that turns issue-board movements into action: branch creation, merge requests, approval validation, deploy triggers and scan scheduling, with a state machine persisted in the issue itself.

  • 32 pipeline jobs across 18 orchestrators
  • ~330 unit tests covering routing, idempotency and merge races
  • Anti-loop watermarks added after a real infinite-webhook incident
Python 3GitLab GraphQLWebhooksVaultpytest
See the interactive walkthrough →

Pipeline-as-a-Product Platform

A single repository holding every CI/CD definition: a gateway include that routes 40+ service repos to domain pipelines, shared stage templates per artifact type, and a codified rulebook the whole organization deploys by.

  • 40+ repositories onboarded via a four-line include
  • 52-rule codified standard, versioned with the pipelines
  • Vulnerability scan + SBOM on every container image
GitLab CIJenkins GroovyBashTrivyCycloneDX
See the gateway diagram →

Release Control Plane

Self-built release management platform: deployment history, build and deploy triggering, scan results, environment promotion, secrets and audit in one UI — fed live by pipeline callbacks over server-sent events.

  • Composite quality gate: deploy + code quality + vulnerabilities
  • Role-based access: admin / devsecops / developer
  • Live updates over SSE; API-key auth for machine callers
Node.jsExpressSSERBAC
Try the interactive replica →
Case study I — release orchestration

I turned a bare GitLab board into a release system

A GitLab issue board has almost none of what a regulated release needs: no approval chain, no phase enforcement, no persistent state, no deploy trigger. Instead of buying a tool, I made the board itself drive the pipeline. Engineers move a card; branches, merge requests, scans, approvals, deploys and audit comments happen by themselves — and every result is written back into the ticket.

before: approvals lived in chat, release state lived in one person's head, deploys were triggered from a terminal

gitlab out of the box

To do

Doing

Done

no approval chain · no phase state · no trigger — just columns

1 card moved → 6 automated actions
the board I built

To do

#471Intake

In Dev

branch auto-made

In Review

MR + scans

Merged

✓✓✓✓✓5-layer approval

Deploy

pipeline + audit

phase badge · approval chips · bot comments · deploy trigger — the board runs the release

live walkthrough 1 / 8

To do1

In Dev1

#468
fix: timeout on statement export

In Review0

Merged1

#465
chore: bump base image

Deploy0

#471Intake
feat: nightly ledger reconciliation
Sprint 24 severity:2 priority:high
state<!-- PIPELINE_STATE -->
◧
#471 · feat: nightly ledger reconciliation
activity — everything below is written by the automation

D1A five-layer approval chain, enforced by machine

team→ business→ product→ architecture→ engineering
  • Every phase demands a specific approval field. Skip one and the phase is reverted automatically with an explanatory comment.
  • Phase jumps are rejected: the sequential flow allows no leap forward, and one role may hold only one approval state at a time — a contradictory second value clears both.
  • Fail-closed by default: a missing milestone or an unconfirmed merge stops the job with exit 1 instead of quietly continuing with a guess.

D2Designed around a real incident

A retry command with substring matching let the bot trigger itself: hundreds of runaway comments across multiple tickets in one night. The fix is layered — four guards — and 25 regression tests keep it from recurring.

L1bot accounts excluded at the CI rule level
L2bot username guard inside the orchestrator
L3anchored regex — a comment must start with the command
L4bot replies are written so they can never re-trigger
deploy watermark 60s scan cooldown 900s merge dedupe 120s cycle idempotency

chatops // /retry · /sonarqube · /diff <domain> · /sync · /follow #iid · /summarypic · /notif — the board doubles as a command line; every command is anchored, gated by ticket status and bot-proof.

Case study II — release control plane

The dashboard that replaced five tools and a spreadsheet

A release management platform I built and maintain: version drift between environments, build and deploy triggering, live console streaming, quality gates, secret changes, mobile test recordings and role-based access — on one page, updated in realtime. Below is an interactive replica with sanitized dummy data. Click anything.

before: five tools and one spreadsheet to answer "what runs in UAT right now?"

release-control-plane / interactive replica — dummy data live
Dashboard --:--:--

The replica UI is shown in English, exactly like the real product. Names, versions, hashes and paths below are sanitized dummy data — the behaviour, the layout and the numbers describing the platform itself are real.

12views in a single-page app
~110route handlers across 7 API modules
14realtime event types pushed over SSE
3runtime dependencies on the server
~9.9klines written and maintained by me

Diff-driven releases

The core question is "what differs between SIT and UAT?" — the release plan is generated only from services whose versions actually diverged, so nothing rides along by accident.

Deploy without leaving the page

Pick services or paste a commit SHA; the platform resolves which services that diff touches, triggers the build, then streams queue, stages and console output back live.

Release notes write themselves

Tag descriptions are composed from grouped tickets — features, bugfixes, hotfixes — plus config and secret changes, then applied to every repository in the group at once.

Accountable by design

Three roles, per-project assignment, revocable API keys for pipelines, an audit log for every destructive action, and a platform-update button that redeploys the dashboard itself.

One gateway, forty repositories

The idea I keep returning to: developers should never own pipeline plumbing. A four-line include routes every repository to its domain pipeline, and the standard evolves in one place.

before: every repo kept its own copy of the pipeline — fixing the standard meant editing 40 files

40+ service repos domain pipelines environments service repo service repo service repo +35 more gateway.yml route by project id mobile banking dashboards onboarding SIT UAT PROD

Routing is data, not duplication

Project IDs map to domain pipelines in one config. Onboarding a new team means changing one include, not forking a pipeline.

Two engines, one contract

GitLab CI and legacy Jenkins coexist behind the same stage contract and callback format, so migration is incremental instead of a big bang.

Promotion is an event

SIT → UAT → PROD moves are tag-and-merge operations with an append-only release ledger as the source of truth for what runs where.

The lifecycle of one change

click any node — the detail appears below the rail

→ → → →
What the standard enforces six rules a service cannot opt out of — the same ones the lifecycle above applies
  1. A branch is created from the milestone release branch and named after the ticket, so a wrong name cannot be produced.
  2. The merge request opens against the correct target for that release line — nobody picks a branch.
  3. Deploy, quality gate and zero critical findings must agree, or the release is blocked rather than queued.
  4. The merge stays human: the bot prepares, records and waits; a person clicks merge.
  5. Promotion between environments is tag-and-merge — same pipeline, same artefact, only the target changes.
  6. A fix cascades forward through merge requests between release branches, so it never misses an older line.
email reports report emails my pipelines generate — sample data, sanitized
IJ
devops-platform <noreply@…>
[board] Branch Auto Sync Summary — Thu 02:14 WIB
BRANCH AUTO SYNC REPORTDEVOPS PLATFORM
2026-10-01 · 02:14 WIBdomain: core12 branch pairs · 9 MR
6merged
3no changes
1conflict
1approval failed
1skipped
servicebranch pairstatusmr
ledger-serviceR1.0 → R1.1✓ MERGEDview MR
mobile-bffR1.1 → R1.2⚠ CONFLICT — REBASE NEEDEDview MR
scheduler-jobR1.0 → R1.1— NO CHANGES
auth-gatewayR1.1 → R1.2✕ APPROVAL FAILEDview MR
note // one design system, three report types — the run always reports itself

// dummy data in the report format my pipelines generate — names, domains and links are sanitized. The amber disclaimer is deliberate: a scanner that overclaims is worse than no scanner

then — jira + jenkins
  • dozens of shared-library steps wiring Jira webhooks to GitLab automation
  • Jira webhook → gateway job parses the changelog and dispatches sibling Jenkins jobs
  • those jobs create branches, open MRs, approve, merge and deploy across SIT / UAT / PROD
  • secrets from Vault, email reports before any dashboard existed
migrated the standard, not just the tools
now — gitlab board + gitlab ci
  • the issue board is the trigger — no second tracker, no gateway fan-out
  • one webhook → workflow.rules routes to 18 orchestrators, one module per job
  • state lives inside the ticket; watermarks in notes — no database to drift
  • the same gates survived the move: approval chain, scans, fail-closed
what "release ready" means on my platform
✓Deployment SUCCESS
✓Quality gate PASSED
✓No CRITICAL findings
composite gate
···

// security is a gate, not a report — SAST on the merge path, SBOM per image, secrets fail closed

// the stage orchestrator was designed on paper before a line of code — 40 repos share one contract, so a new team onboards with a four-line include

// every helper ships with permanent debug logs — when production misbehaves, I read, I do not guess

// I hate copy-paste versioning more than I hate writing code: sync groups retag a whole release train consistently, next version suggested

Running it in production

The pipeline is only half of it. These are the settings that decide whether a service keeps running after it ships — and my part in them is not choosing the numbers. It is making sure the number that was agreed is the number that runs, in every repo, without drift.

Capacity is decided upstream. I make it stick.

decision
Requests, limits and the autoscaler range default from the shared stage contract. A service that genuinely needs different numbers carries that override in its own manifest, with the reason written beside it.
alternative
Every team copies a deployment manifest and edits the numbers by hand.
why
The capacity target is the system analyst's decision, not mine. My job is that the agreed target reaches production unchanged, that 40+ repositories start from the same default, and that an exception reads as an exception instead of a silent fork.
cost
A wrong default is amplified into every repo that includes it, and an override can outlive the reason it was written for — so the contract is versioned, and an override has to be justified in the merge request that adds it rather than in a later incident review.

One rollout rule, and no two deploys at once.

decision
The update strategy is set in the contract rather than per team, so every repo rolls the same way — and the deploy job refuses a second run while one is still going.
alternative
Each service picks its own strategy, including stop-then-start, usually at the moment someone is in a hurry.
why
A release that interrupts traffic is an incident with a ticket number attached, and two releases racing for the same route turn a rollback into a guess. The board already knows which services moved; the platform should not be the thing that surprises anyone.
cost
One rule for everything means a service that genuinely needs different behaviour asks for an override — the same exception path as capacity, reviewed where it is added. And refusing to queue means someone waits for a free slot, on purpose.

A backup is only real if it expires.

decision
Nothing destructive runs without a dated backup taken first, and nothing is kept forever — anything past the retention date is removed on a routine, not when someone notices the disk.
alternative
Back up once, keep everything, delete by hand when the volume complains.
why
"We have backups" without an expiry rule becomes an outage months later when the storage fills up — and a folder nobody is allowed to clean eventually stops being inspected at all.
cost
A cleanup rule that is wrong deletes the evidence you would have wanted, so the date lives in the filename and removals are logged. The window itself is the bank's to publish, not mine; the rule and the routine are what I am claiming.

What I would do differently

Resource values started out copied from whatever service shipped last, because nothing measured them and nobody was asked for a number. If I set this up again, the contract would carry a measured baseline per artifact type, so the decision starts from an observation instead of an estimate — and the list of overrides stays short enough to read in one sitting.

Decisions and what they cost

Each decision, the alternative I rejected, and what it cost.

One gateway, not per-repo templates

decision
A single gateway.yml include routes every repo to its domain pipeline.
alternative
Copy-paste a template into each repository.
why
The standard evolves in one place; onboarding stays a four-line include.
cost
The gateway is critical path — one bad merge affects everyone. Paid for by versioning it with the same pipeline that protects releases.

Gates fail closed

decision
A missing secret or unconfirmed merge stops the job with exit 1.
alternative
Warn and continue — keep builds green.
why
A silently-falling-back secret is a compliance incident with a head start.
cost
Builds fail when config is missing — the team cursed it for a week, then config drift stopped shipping. Availability traded for correctness, deliberately — and plan B is honest: a manual run from the GitLab UI, same pipeline, same gates.

Two CI engines, no big-bang migration

decision
GitLab CI and legacy Jenkins kept running side by side.
alternative
Force every pipeline onto one engine now.
why
29 pipelines still deliver; migration is only safe behind one stage contract.
cost
Dual maintenance plus the contract abstraction itself — invisible to teams, which was the point.

What I deliberately did NOT build

decision
No state database for the board; no deploy queue; no auto-merge on a human's behalf.
alternative
Postgres for state, a queue for busy deploys, a bot that clicks merge.
why
State lives inside the ticket; a second deploy while one runs is refused, not queued; the bot reverts the card and waits for a person.
cost
Fewer moving parts, fewer ways to be wrong — and occasionally waiting for a human, on purpose.

Built the control plane, did not buy one

decision
A release board, an append-only ledger and generated reports, written by me on top of Jira and GitLab.
alternative
ArgoCD for deploys, GitLab Environments for promotion, Backstage as the read layer.
why
The source of truth here is the issue board, not a git repository: a release is the set of services that diverged between environments, and each stage is approved by a different group. Argo reconciles one application from one repo, and Environments assume one approver per environment. On-prem OpenShift, no cloud tenancy to attach either to.
cost
I own a UI that Argo ships for free, every feature costs me, and the bus factor is smaller than a mainstream tool's. If this moved to a public cloud with git-driven deploys, I would retire it and run the standard — it exists because it fits more shops than a bespoke one does.

What I was actually defending

The tool list is what I used — this table is what I was thinking about.

asset threat the gate that closes it
release integritymerge without approval · phase skip5-layer chain + machine revert with an explanatory comment
productionmanual "quick fix" deploydeploy only fires from board state; watermark + running-pipeline refusal
secretsplaintext in logs or repoKV store, JWT-scoped pipeline auth, fail-closed, values masked in audit
artifactsvulnerable or untraceable imagetrivy + SBOM on the merge path; quality gate before promote
release record"what runs where" disputesappend-only ledger + pipeline callbacks written back to the ticket

Toolbox, honestly rated

No percentages — they cannot be falsified. Each skill carries a usage tier and a pointer to the project that proves it. Hover or tap a reference to see where the evidence lives.

production dailyin my hands every week, in production
production regularregular production use, not daily
project exposureused in a specific project, not owned
filter

CI/CD & Automation

GitLab CIproduction daily

gateway include routes 40+ repos; the 52-rule standard is versioned with the pipelines.proj-02

Bashproduction daily

SSH deploy and rollback tooling for JAR and image paths.proj-02

Jenkins / Groovyproduction regular

29 legacy Groovy pipelines kept alive behind one stage contract during the dual-engine migration.proj-02

Pipeline designproduction daily

stage contract per artifact type; onboarding a team is one include, not a fork.proj-02

Languages

Pythonproduction daily

18 ChatOps orchestrators and ~330 unit tests, stdlib-only by constraint.proj-01

JavaScript / Nodeproduction regular

the release control plane: Express, SSE event bus, RBAC — three dependencies total.proj-03

Groovyproduction regular

shared pipeline logic and security-scan jobs as declarative Groovy.proj-02

SQL / Oracleproject exposure

AWR tuning tasks shipped from the same orchestrator repo that drives the board.proj-01

Security

SAST / Semgrepproduction daily

centralized per-domain SAST in the merge path, results commented back on the MR.proj-02

Container scanning / Trivyproduction regular

vuln+secret+config scanning on every image, HIGH/CRITICAL summarized in the build.proj-02

Secrets / Vaultproduction daily

KV v2 with JWT-scoped pipeline auth; missing secrets abort the job, fail-closed.proj-02

SBOM / CycloneDXproduction regular

CycloneDX SBOM archived alongside every container build.proj-02

Infrastructure & Platforms

Docker / Registryproduction daily

image build and push on every backend deploy, incremental tags.proj-02

OpenShift / Kubernetesproduction regular

rolling deploys via rendered deployment manifests across three namespaces.proj-02

Linux / SSHproduction daily

service lifecycle over SSH with dated backups and 5×10s verify retries.proj-02

Gitproduction daily

append-only release ledger; promotion is tag-and-merge, reviewable in a diff.proj-02

Multi-artifact deliveryproduction daily

five artifact types — jar, image, apk/aab, ipa and web bundle — each with its own stage contract behind one gateway.proj-02

Firebase / TestFlightproduction daily

where every mobile build lands: apk/aab and ipa produced by the pipeline are distributed through them for SIT and UAT testing.proj-02

On-prem VM provisioningproduction regular

built the platform's own machines from an empty image — Jenkins node, GitLab runner, app server, web server: install, configure, keep updated.proj-02

Practices

Logging & debuggabilityproduction daily

permanent debug logs in every helper; deploy callbacks stream into the dashboard over SSE.proj-03

Incident-driven designproduction daily

anti-loop watermarks and the scan-fails=fails rule both exist because of real incidents.proj-01

Templating & rendered manifestsproduction regular

per-artifact stage templates and rendered OpenShift manifests from one service map.proj-02

Automated testingproduction regular

~330 tests covering routing, idempotency and merge races — written after incidents, not before.proj-01

One direction, deepened

2024 — Present

DevSecOps

State-owned banking group, Indonesia
  • Own CI/CD for a wholesale banking platform: one pipeline standard shared by 40+ repositories across two CI engines.
  • Built the release control plane and the ChatOps orchestrator that drives branch, merge, deploy and scan workflows from issue boards.
  • Codified a 52-rule pipeline standard and the security gates (SAST, container scan, SBOM, quality gate) enforced on every merge.
  • Designed the platform architecture and delivery workflows end-to-end: gateway routing, stage contracts, environment promotion and the release ledger.
  • Mentored and onboarded new engineers, and transferred ownership of the four projects I held — with architecture docs and runbooks.
  • Built and maintained the delivery layer that came before GitLab-native automation: dozens of Jenkins shared-library steps wiring Jira webhooks to GitLab across SIT, UAT and PROD.
2022 — 2024

Backend Developer

State-owned banking group, Indonesia
  • Built backend services in Java Spring Boot and Python, with the design patterns and framework knowledge that keep microservice APIs robust.
  • Ran deployments on OpenShift and OpenFaaS with a continuous-deployment approach, cutting downtime and shortening the path from code to running service.
  • Pushed code quality through testing and SonarQube on the merge path, leaving cleaner codebases and less technical debt behind.

education S1 Informatics — Universitas Tarumanagara

◇

I design the architecture and the workflow — the diagram is drawn before the YAML is written.

◆

I onboard and mentor engineers onto pipelines they now run without me.

▣

Four projects I owned have been handed over — documented well enough to survive the handover.

Let's make releases boring.

Open to DevSecOps, platform engineering and developer-experience roles — on-premise or cloud, banking-grade or startup-speed.