CI/CD Pipelines: GitHub Actions, GitLab CI, Jenkins
The pipeline starts in 20 minutes, deployment takes 30 seconds — a typical picture for projects where CI/CD setup is done "on the knee." Developers get used to waiting, stop committing frequently, and start accumulating changes in long-lived branches. The cycle closes, Lead Time for Changes grows to two days.
We build pipelines that provide feedback in minutes — not because "it's faster to deploy," but because it directly impacts development culture. In one Python monorepo, the original pipeline took 35 minutes. After optimization (cache, parallelization, matrix tests) — 4 minutes (89% reduction). The team started making small commits, reducing Lead Time from 2 days to 3 hours. End-to-end CI/CD pipeline setup is not about deployment, but about how the team works. This practice is known as continuous integration — merging changes frequently requires fast feedback loops.
Pipeline Architecture: What Matters Most
Most pipelines are written linearly: step by step, one after another. This is easier to read but scales poorly. Good architecture is built on two principles: fail fast and parallelism where there are no dependencies.
The first stage should answer the question "Is this even worth continuing?" — in 1–2 minutes. Lint, static analysis, config syntax checks. If the linter fails, there's no point in building a Docker image for 10 minutes.
The second stage — parallel build and tests. Building a Docker image and running unit tests don't depend on each other — they should run simultaneously.
The third stage — integration tests and security scan. They already require an artifact from the second stage.
The fourth — deployment to staging. The fifth — smoke tests and manual approval (or automatic deployment to production for teams with high Deployment Frequency).
In GitHub Actions this looks like:
jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm run lint build: needs: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v5 with: cache-from: type=registry,ref=${{ env.REGISTRY }}/app:cache cache-to: type=registry,ref=${{ env.REGISTRY }}/app:cache,mode=max push: true tags: ${{ env.REGISTRY }}/app:${{ github.sha }} test: needs: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm test security-scan: needs: build runs-on: ubuntu-latest steps: - uses: aquasecurity/trivy-action@master with: image-ref: ${{ env.REGISTRY }}/app:${{ github.sha }} exit-code: 1 severity: HIGH,CRITICAL ignore-unfixed: true deploy-staging: needs: [test, security-scan] ... build and test run in parallel after lint. deploy-staging waits for both test and security-scan. If any of them fails — deployment doesn't happen.
How Caching Accelerates Pipelines?
Caching is the most ignored optimization area. Three sources of loss:
Docker layer cache
Without cache, each pipeline run downloads the base image and installs dependencies from scratch. Node.js project: npm install on empty cache — 2–3 minutes. With cache — 10 seconds (only check). BuildKit can cache layers in the registry using --cache-from / --cache-to. For GitHub Actions: type=gha uses built-in Actions cache. For GitLab CI: type=registry with registry cache is more reliable because the cache is not tied to a specific runner. Savings on CI minutes after implementing Docker layer cache amounts to $200–400 per month on an average project. The key Dockerfile rule for good caching: first COPY package*.json ./ and RUN npm ci, then COPY . .. This way the dependencies layer is invalidated only when package-lock.json changes, not on any code change.
FROM node:20-alpine AS deps WORKDIR /app COPY package*.json ./ RUN npm ci --only=production FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:20-alpine AS runner WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY --from=builder /app/dist ./dist USER node CMD ["node", "dist/index.js"] Dependency cache in CI
In GitHub Actions — actions/cache with key from lock file hash:
- uses: actions/cache@v4 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node- In GitLab CI — built-in cache: block. Important to set policy: pull for jobs that only read cache, and policy: pull-push only for the job that populates it. Otherwise, each parallel job tries to write the cache and they compete.
Test parallelization
For Go: go test -parallel 8 ./... — built-in support. For pytest: pytest-xdist with -n auto runs tests in parallel on all CPUs. For Ruby/RSpec: parallel_tests. But if the test suite is large (>500 tests), parallelization within a single job is not enough. Matrix is needed: split tests into N groups and run N jobs. In GitHub Actions:
test: strategy: matrix: shard: [1, 2, 3, 4] steps: - run: pytest --splits 4 --group ${{ matrix.shard }} Real result on one project: Python service, 1200 tests, no parallelization — 22 minutes. With pytest-xdist -n 4 in one job — 8 minutes. With a 4×4 matrix (4 shards × 4 workers) — 3 minutes. Parallelism gives 2–3x improvement over purely sequential runs.
Which CI/CD Platform to Choose: GitHub Actions, GitLab CI, or Jenkins?
| Criteria | GitHub Actions | GitLab CI | Jenkins |
|---|---|---|---|
| Syntax | YAML, readable | YAML, powerful | Groovy DSL or declarative |
| Hosted runners | Yes, pay per minute | Yes (GitLab.com) | No, only self-hosted |
| Self-hosted | Supported | Supported | Primary mode |
| Reusability | Composite actions, reusable workflows | Includes, extends, templates | Shared Libraries |
| Secrets | GitHub Secrets + OIDC | CI/CD Variables + Vault | Credentials plugin + Vault |
| Matrix builds | Native | parallel: |
Parallel stages |
| Docker cache | type=gha or registry |
Registry cache | BuildKit + registry |
Jenkins remains relevant for enterprise with on-premise requirements and complex Groovy pipelines. But the entry barrier is higher — you need to maintain Jenkins itself, plugins, and update regularly. Shared Libraries in Jenkins are a powerful mechanism for standardization but require discipline: without code review, libraries become legacy. Migrating from Jenkins to GitHub Actions often reduces CI infrastructure costs by 2–3 times: instead of paying for servers for Jenkins master and agents — only minutes of GitHub-hosted runners, saving $500–1000 per month for a team of 10 developers.
What Security Scan Tools Can Be Integrated into the Pipeline?
You can integrate Trivy (Docker image vulnerability scanning), Snyk (dependency analysis), SonarQube (SAST for code). Trivy runs with a single command and checks the image for critical CVEs. Snyk provides automatic fix in PR. All are easily connected via GitHub Actions or similar in GitLab CI. Security scanning catches 70–80% of vulnerabilities before deployment.
Managing Secrets in the Pipeline
Storing secrets in CI system environment variables is the minimum security level, but not sufficient for production. Problem: rotating a secret requires manually updating the variable in each repository. And there is no audit log — who used the token and when. The correct approach is OIDC authentication. GitHub Actions and GitLab CI support obtaining short-lived tokens for AWS, GCP, Azure without static credentials:
- uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456789:role/github-actions-deploy aws-region: eu-central-1 The pipeline gets a temporary token for the duration of the job. No AWS_ACCESS_KEY_ID in secrets. No rotation. For application secrets in Kubernetes — Vault Agent Injector or External Secrets Operator, not kubectl create secret manually.
Typical Mistakes in Pipeline Setup
Common errors and how to fix them:
| Mistake | Consequence | Fix |
|---|---|---|
| Run on every push to feature branches | Wastes CI minutes, increases queue | workflow_dispatch for drafts; trigger only on PR and main |
| No timeout on jobs | Runner stuck for hours; blocks other builds | Set timeout-minutes: 15 per job |
| Artifacts not cleaned | Terabytes of storage after weeks | Reduce retention to 7 days (retention-days: 7) |
| Hardcoded secrets in YAML | Security leak, rotation pain | Use OIDC or Vault; never store keys in code |
| Sequential stages when parallel possible | Doubles pipeline duration | Always analyze dependency graph; run independent jobs in parallel |
Pipeline runs on every push to any branch, including feature/*. Developer makes 10 commits — 10 runs, 10 × 20 minutes of resources. Solution: workflow_dispatch for draft branches, automatic run only on PR and main. No timeout on jobs. A job hangs on an integration test waiting for an unavailable service — and hangs for 6 hours, occupying a runner. Always set timeout-minutes: 15 (or reasonable value for the specific job). Artifacts are not cleaned. Each run saves artifacts with default retention (90 days in GitHub). 100 runs per day × 500 MB = terabytes per quarter. Set retention-days: 7 for regular artifacts. Hardcoded secrets in YAML — a simple git log exposes tokens. Migrate to OIDC or external secret stores. Sequential stages ignoring parallelism — a common oversight that doubles execution time.
What's Included in Turnkey CI/CD Pipeline Setup
- Audit of current pipeline and infrastructure (1–2 days)
- Architecture design: DAG of dependencies, parallelism, caching
- Implementation of workflow files with detailed comments
- Setup of Docker layer cache and dependency cache
- Integration of security scan (Trivy, Snyk) and SAST tools
- Implementation of OIDC authentication for cloud providers
- Test parallelization (sharding, matrix builds)
- Documentation in repository (pipeline README, runbook)
- Team training (2-hour webinar, recording + cheat sheet)
- Post-release support for 2 weeks
We rely on implementation experience in 30+ projects, certified engineers (AWS Certified DevOps, CKA). We guarantee at least 3x pipeline time reduction — or we'll refine for free.
How to Order CI/CD Pipeline Setup?
Write to email or messengers — within a day we'll prepare a commercial proposal with specific metrics (speedup, resource savings). Get an engineer consultation and estimate without obligations. Order turnkey setup with guaranteed results — you'll feel the difference by the second week. Contact us today to discuss your pipeline challenges — we'll design a solution that fits your stack and team size.







