Standardizing application delivery with GitLab CI
Shared pipeline templates that gave every service the same build, scan and deploy path, so delivery stopped depending on who wrote the pipeline.
Problem
Each repository carried its own hand-written pipeline. Builds, image tags, scanning and deployments behaved differently per project, and fixing one pipeline fixed only that one.
Constraints
- Teams had to keep shipping while pipelines were migrated; no freeze window.
- Existing projects used different languages, base images and branching habits.
- Security scanning had to be added without making pipelines slow enough that people bypassed them.
Decisions
- Versioned, includable pipeline templates instead of copy-paste examples
- A fix or new security stage lands in every project by bumping one version, and teams can pin while they test.
- Build once, promote the same image through environments
- What was tested is exactly what runs in production; environments differ by configuration, not by rebuild.
- Scanning stages that report first and block later
- Teams saw findings before enforcement, which kept trust in the pipeline while the backlog was cleared.
Architecture
Source
Merge request opens; protected branches and required approvals gate main.
- GitLab
Build
Shared template builds a container image with a reproducible tag derived from the commit.
- GitLab CI
- Docker
Verify
Unit tests, linting, dependency and image scanning run from the same template stages.
- Trivy
- SAST
Publish
Image pushed once to the registry; later stages reference it by digest.
- Container registry
Deploy
Environment-specific configuration applied; production requires a manual, audited approval.
- GitLab environments
- Kubernetes
Build notes
Implementation
The template library started with the stages every service already needed: build, test, publish. Each stage was a separate, includable file with documented inputs, so a project could adopt the build stage without committing to the full flow on day one.
Image tags were derived from the commit SHA and promoted by digest. That removed a whole category of “it worked in staging” problems where two environments silently ran different builds.
Security stages were introduced in report-only mode first. Findings appeared in merge requests, teams triaged them, and enforcement was switched on per stage once the noise was understood.
Challenges
The hardest part was not technical. Some projects had pipelines with years of special cases. Rather than forcing them into the template, the template exposed a small number of extension points and the remaining special cases were documented as known exceptions with an owner.
Outcome
Pipelines became predictable and reviewable in one place. New services start from a working pipeline, and security checks run everywhere by default rather than by request.
What I learned
Pipeline templates are a product. Versioning, changelogs and a migration path mattered as much as the YAML.