CI/CD Pipeline Setup: GitHub Actions, GitLab CI, Jenkins

CI/CD pipeline setup turns every code change into a repeatable, tested and traceable release: a developer pushes a commit, the pipeline lints, tests and builds it, and a verified artefact reaches staging or production without anyone copying files over FTP or typing commands into a server at midnight. Continuous integration catches problems while they are still cheap to fix; continuous delivery makes releasing a routine, boring event instead of a risky one.

At CodeWiz we design and build pipelines on GitHub Actions, GitLab CI and Jenkins for Laravel and PHP applications, WordPress and WooCommerce sites, Node.js APIs, frontend apps and Python services. We set up automated tests and quality gates, secure secrets handling, build caching, preview environments and zero-downtime deployments, and we document the whole flow so your team understands what happens after git push.

The service is for teams that deploy manually and are afraid of Friday releases, agencies that need a consistent process across dozens of client projects, and companies whose existing pipeline is slow, flaky or understood by only one person. If a release currently needs a checklist and a lot of luck, an automated pipeline is usually the highest-leverage DevOps investment you can make.

Pipeline: Angled view of HTML source code with syntax highlighting on a dark screen

What a Well-Designed CI/CD Pipeline Looks Like

Whatever the platform, a good pipeline follows the same shape. Fast checks run first, so feedback arrives in minutes; slower and more expensive stages run only when the cheap ones pass; the same artefact moves from staging to production instead of being rebuilt for each environment.

  1. Validate: linting, formatting, type checks and static analysis (ESLint, TypeScript, PHPStan or Larastan, PHP_CodeSniffer, Ruff).
  2. Test: unit and integration tests against real service containers such as PostgreSQL, MySQL and Redis.
  3. Build: compile assets, install production dependencies, build and tag a container image or a release archive.
  4. Scan: dependency audits, secret detection and image scanning before anything is published.
  5. Deploy: automatic deployment to staging, then a protected, often manually approved, promotion to production.
  6. Verify: smoke tests and health checks after release, with an automatic or one-click rollback.

GitHub Actions CI/CD Pipelines

GitHub Actions is the natural choice when your code already lives on GitHub. Workflows are YAML files in .github/workflows, triggered by pushes, pull requests, tags, schedules or manual dispatch, and the marketplace offers ready-made actions for almost every language and cloud provider.

How we build GitHub Actions workflows

  • A pull request workflow that runs checks and tests in parallel jobs, with a matrix for several PHP or Node.js versions when a library has to support them.
  • Dependency caching through the setup actions or actions/cache, and concurrency groups that cancel outdated runs when a new commit lands.
  • Reusable workflows and composite actions, so twenty repositories share one maintained pipeline instead of twenty slightly different copies.
  • Environments with required reviewers and environment-scoped secrets for production deployments.
  • OpenID Connect (OIDC) to AWS, Google Cloud or Azure, so the pipeline receives short-lived credentials instead of storing long-lived cloud keys.
  • Self-hosted runners on your own servers when builds need more CPU, GPU access or access to a private network.
name: ci on: pull_request: push: branches: [main] concurrency: group: ci-${{ github.ref }} cancel-in-progress: true permissions: contents: read jobs: test: runs-on: ubuntu-latest strategy: matrix: php: ["8.3", "8.4"] steps: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: { php-version: "${{ matrix.php }}", coverage: none } - run: composer install --no-progress --prefer-dist - run: vendor/bin/phpstan analyse - run: vendor/bin/pest --parallel

Note the top-level permissions: contents: read: by default we give the workflow token the minimum it needs and grant more only to the job that deploys. GitHub’s own security hardening guide for Actions is worth reading for anyone maintaining workflows.

GitLab CI/CD Configuration

GitLab CI is built into GitLab, whether you use gitlab.com or a self-managed instance. A single .gitlab-ci.yml defines stages and jobs, and GitLab Runners execute them in Docker containers, on virtual machines or in Kubernetes. Teams that self-host GitLab often choose it precisely because code, pipelines, container registry and issue tracking stay on their own infrastructure.

What we set up in GitLab

  • rules that decide exactly when a job runs (merge requests, default branch, tags), instead of the older only/except syntax.
  • needs to turn stages into a dependency graph, so a deploy job starts as soon as its own build finishes.
  • Shared templates through include and extends, kept in one central repository for all projects.
  • Protected and masked CI/CD variables, protected branches and environments with deployment approvals.
  • Review apps that deploy every merge request to its own temporary URL, so clients and testers can click through a change before it is merged.
  • Our own runners on Hetzner or DigitalOcean servers when shared runner minutes become a bottleneck or a cost.
stages: [test, deploy] test: stage: test image: node:22-alpine cache: key: { files: [package-lock.json] } paths: [.npm/] script: - npm ci --cache .npm --prefer-offline - npm run lint - npm test -- --coverage rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH deploy_production: stage: deploy environment: production script: ./scripts/deploy.sh rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: manual

The full keyword reference lives in the official GitLab CI/CD YAML documentation.

Jenkins Setup and Modernization

Jenkins is the veteran of the group: open-source, self-hosted and extensible through a huge plugin ecosystem. It remains a strong choice when you need full control, have to build on-premises behind a firewall, or already run years of working jobs. It also tends to become a fragile “snowflake” server, so most of our Jenkins work is about making it reproducible again.

How we set up Jenkins properly

  • Pipelines as code: declarative Jenkinsfiles in each repository and multibranch pipelines instead of jobs configured by clicking through the UI.
  • Configuration as Code (JCasC): the controller’s settings, credentials references and plugins live in YAML in Git, so the whole server can be rebuilt in minutes.
  • Ephemeral agents: builds run in Docker or Kubernetes agents that are created per build and thrown away afterwards; the controller itself runs no builds.
  • Shared libraries: common steps (build, scan, deploy, notify) are written once in Groovy and reused across all pipelines.
  • Plugin hygiene and security: a minimal, pinned plugin set, regular updates, role-based access and credentials stored in the Jenkins credentials store or an external vault.

If your Jenkins is old and nobody wants to touch it, we can either modernize it this way or migrate the jobs to GitHub Actions or GitLab CI – whichever costs less to run and maintain long term.

GitHub Actions vs GitLab CI vs Jenkins: Which One Fits?

CriteriaGitHub ActionsGitLab CIJenkins
HostingCloud runners, optional self-hosted runnersgitlab.com or self-managed, shared or own runnersAlways self-hosted
ConfigurationYAML workflows per repositoryOne .gitlab-ci.yml plus includesGroovy Jenkinsfile plus JCasC
Setup effortLowLow on gitlab.com, medium self-managedMedium to high
MaintenanceMinimal (managed)Minimal to mediumOngoing: updates, plugins, agents
EcosystemLarge actions marketplaceIntegrated registry, security scanning, environmentsHuge plugin ecosystem of varying quality
Best forCode on GitHub, open-source, small to mid teamsAll-in-one DevOps platform, self-hosting needsOn-premises, complex legacy builds, full control

Our rule of thumb: use the CI system built into your Git host unless there is a concrete reason not to.

Automated Testing in the Pipeline

A pipeline is only as trustworthy as the tests it runs. Automated testing is where we spend the most time, because a green build has to actually mean “safe to release”. We follow the testing pyramid: many fast unit tests, fewer integration tests, and a small, focused set of end-to-end tests for the flows that make money.

Test layers we automate

  • Unit tests: PHPUnit or Pest for PHP and Laravel, Vitest or Jest for JavaScript and TypeScript, pytest for Python.
  • Integration tests: real PostgreSQL, MySQL, Redis or Typesense instances started as service containers, with migrations run from scratch.
  • End-to-end tests: Playwright or Cypress for checkout, login, registration and other critical user journeys, with screenshots and traces stored as artefacts when a test fails.
  • Static analysis: PHPStan or Larastan, ESLint and tsc --noEmit catch whole classes of bugs before a single test runs.
  • Security checks: composer audit, npm audit, gitleaks for committed secrets and Trivy for container images.
  • Performance budgets: Lighthouse CI fails a pull request that makes key pages noticeably slower – the same discipline we describe in our article on achieving 100/100 page vitals.

Keeping tests fast and reliable

We parallelize test suites, cache dependencies and skip heavy jobs on documentation-only changes. Flaky tests are quarantined and fixed rather than retried until they pass, because a pipeline people learn to ignore is worse than none. If a project has no tests, we start with smoke tests for the most valuable paths and grow coverage alongside new features.

Dense lines of colorful JavaScript code filling a monitor screen

Zero-Downtime Deployment Strategies

The “CD” half of CI/CD decides how a release reaches users. The right strategy depends on your infrastructure and how much risk a bad release carries.

StrategyHow it worksGood fit
Atomic symlink releaseNew release is prepared in its own directory, then a symlink switches instantlyPHP, Laravel and WordPress on VPS or dedicated servers
Rolling updateInstances are replaced a few at a time behind a load balancerDocker Swarm, Kubernetes, several app servers
Blue-greenTwo identical environments; traffic switches when the new one is verifiedCritical systems that need instant rollback
CanaryA small share of traffic gets the new version first, then it is ramped upHigh-traffic applications with good monitoring

Database changes deserve special care. We use expand-and-contract migrations – add new columns first, deploy code that uses them, remove old columns in a later release – so the previous version keeps working during the switch and a rollback never meets an incompatible schema. Where we deploy to different providers, the pipeline adapts to the target; we have written more about deployment strategies across DigitalOcean, AWS, Hetzner and bare metal.

CI/CD Security and Secrets Management

A pipeline has write access to production, which makes it an attractive target. We treat it as part of your attack surface:

  • Secrets live in the CI platform’s encrypted store or a vault, never in the repository, and are scoped to the environment that needs them.
  • OIDC federation replaces long-lived cloud access keys wherever the provider supports it.
  • Third-party actions and images are pinned to versions or commit SHAs, and Dependabot or Renovate proposes updates through reviewed pull requests.
  • Pipelines triggered from forks never receive secrets, and production deploys run only from protected branches or tags.
  • Deploy users on servers get only the permissions a deployment needs, with SSH keys dedicated to CI.

Supply-chain attacks through package registries are a real risk in CI too; our guide on protecting your environment from malicious npm packages explains the threat in more detail.

Common CI/CD Mistakes We Fix

  • Rebuilding the application separately for staging and production, so what was tested is not what gets deployed.
  • Slow pipelines where nothing is cached and every job installs dependencies from zero.
  • Production credentials available to every branch and every pull request.
  • Tests that are allowed to fail “temporarily” and stay that way for months.
  • Deploy scripts that run git pull on the production server, leaving it half-updated if anything fails.
  • No rollback plan, so a broken release means a hurried hotfix under pressure.

What Affects the Cost of CI/CD Pipeline Setup

We estimate after reviewing your repositories and current release process. The main factors are:

  • The number of repositories and whether they can share templates or reusable workflows.
  • The current state of tests – writing a test suite from scratch is often bigger than the pipeline itself.
  • Deployment targets: one VPS, several servers, containers, Kubernetes or serverless platforms.
  • Requirements for self-hosted runners, preview environments, approvals and audit trails.
  • Migration from an existing system such as an old Jenkins installation.
Laptop with a terminal window full of command output next to a code editor

How We Work

  1. Review: we map your repositories, branching model, environments, current deploy steps and the places where releases go wrong.
  2. Design: we choose the CI platform, pipeline stages, test strategy, deployment strategy and secrets approach, and agree on scope.
  3. Build: we write the workflows, shared templates and deploy scripts, and add or extend automated tests where they are missing.
  4. Dry run: we run the pipeline against staging, rehearse rollbacks and measure build times until feedback is fast.
  5. Go live: we switch production deploys to the pipeline and watch the first releases closely with your team.
  6. Handover and support: you get documentation and a short walkthrough, and we can keep the pipeline, runners and dependencies maintained.

Why CodeWiz

  • Developers who write the code: we build Laravel, Node.js, WordPress and AI applications ourselves, so we know which tests and checks actually catch real bugs.
  • Tool-agnostic: GitHub Actions, GitLab CI or Jenkins – we recommend what fits your Git host and team, not what we prefer.
  • Open-source first: Playwright, PHPStan, Trivy, gitleaks and self-hosted runners keep licence costs low and avoid lock-in.
  • Fair pricing: premium development without premium prices, from a team in Banja Luka working remotely with clients locally and internationally.
  • The full picture: pipelines, servers, performance and security are all things we do every day – learn more about CodeWiz.

Frequently Asked Questions

How long does it take to set up a CI/CD pipeline?

For a single application that already has tests, a working pipeline with automated deployment to staging and production usually takes a few days. Multiple repositories, preview environments, a Jenkins migration or writing a test suite from scratch extend that to a few weeks.

Which is better: GitHub Actions or GitLab CI?

Both are mature and capable. The deciding factor is usually where your code lives: GitHub Actions for GitHub repositories, GitLab CI for GitLab, especially self-managed. Migrating Git hosting just to change the CI system rarely pays off.

Do we need automated tests before setting up CI/CD?

No, but the pipeline gets far more valuable with them. Without tests we still automate linting, static analysis, builds and deployments, then add smoke tests and tests for critical flows step by step.

Can you set up CI/CD for WordPress?

Yes. Custom themes and plugins live in Git, dependencies are managed with Composer, assets are built in the pipeline and releases are deployed atomically, while uploads and the database stay out of the repository.

What does CI/CD cost to run?

Running costs depend on build minutes and runners. Many small and medium projects fit within the minutes included with their Git hosting plan; heavier workloads are often cheaper on a self-hosted runner on an inexpensive cloud server. We size this during the review.

What happens if a deployment fails?

The pipeline stops before switching traffic, so the previous version keeps running. If a problem appears after release, health checks or a single command roll back to the last good release.

Ready to Automate Your Releases?

Tell us how you deploy today and where it hurts, and we will propose a CI/CD pipeline that fits your stack and team. Contact us to talk about your project, or explore our CI/CD pipeline setup services.