Skip to main content

Guide

How to Set Up CI/CD for a Laravel Project

Automated testing, Pint linting, PHPStan analysis, and deployment pipelines for Laravel -- parallel jobs, environment config, and the checks that catch production bugs early.

Category Guide
Read Time 8 min read
Updated April 2026
Steps 5 steps

Who This Guide Is For

This guide is for Laravel developers who want to automate their testing, linting, and deployment pipeline so that bugs and code quality issues are caught before they reach production. You have a Laravel application in version control and you are deploying manually (or semi-manually), and you want a pipeline that runs your checks and deploys with confidence. You understand PHP and Laravel basics and want to set up the infrastructure that professional teams rely on.

Before You Start

You should have a Laravel application in a Git repository hosted on a platform that supports CI/CD pipelines (Bitbucket Pipelines, GitHub Actions, GitLab CI, or similar). Your application should have at least some tests written. If you have no tests, start with How to Set Up Automated Testing first — a pipeline that only deploys without testing is an automated way to ship bugs faster.

You will also need SSH access to your production server and a clear understanding of what commands run during a manual deployment. The CI/CD pipeline automates what you already do by hand, so document your current deployment steps before building the pipeline.

Step 1: Define the Check Suite

A robust Laravel CI pipeline runs four checks, and they should run in parallel to keep the feedback loop fast.

Automated tests are the foundation. Your pipeline runs PHPUnit (or Pest, if you prefer) against your test suite. Configure a test-specific database — either an in-memory SQLite database for speed or a MySQL/PostgreSQL service container for production parity. The choice depends on whether your application uses database-specific features. If you use JSON columns, full-text search, or database-specific query syntax, use the same database engine in CI that you use in production. Otherwise, SQLite is faster and simpler.

Code formatting with Laravel Pint enforces consistent code style. Pint is Laravel’s opinionated code formatter built on PHP-CS-Fixer. In CI, run Pint in test mode (the –test flag), which checks formatting without modifying files and returns a non-zero exit code if any files are improperly formatted. This prevents style arguments in code reviews and ensures every file in the repository follows the same conventions.

Static analysis with PHPStan catches type errors, undefined variables, impossible conditions, and other bugs that tests might miss. Start at level 5, which catches the most impactful issues without requiring extensive type annotations across your entire codebase. Use a baseline file to suppress existing violations so that the pipeline only fails on new errors. As you fix existing issues over time, regenerate the baseline to tighten the bar.

Frontend build verifies that your JavaScript and CSS assets compile without errors. Even if your CI pipeline does not deploy frontend assets, building them confirms that no syntax errors or missing dependencies have been introduced. For a typical Laravel application with Vite, this means running npm install followed by npm run build.

Running these four checks in parallel means the pipeline completes in the time of the slowest check rather than the sum of all checks. A typical Laravel pipeline with parallel execution finishes in under three minutes.

Step 2: Configure the CI Environment

The CI environment must mirror production closely enough that tests are meaningful, while remaining fast enough that developers are not waiting five minutes for feedback.

PHP version should match production exactly. If production runs PHP 8.2, CI should run PHP 8.2. Version mismatches cause tests to pass in CI and fail in production (or vice versa) due to behaviour changes between PHP versions.

Environment variables in CI should be configured specifically for testing. Set APP_ENV to testing, APP_DEBUG to true (so test failures give maximum information), and configure a test database connection. For services like Redis, either run a service container in CI or use the array driver. For mail, use the array driver so emails are captured in memory rather than sent.

Composer dependencies should be installed with the –no-interaction flag and cached between runs. Most CI platforms support caching the vendor directory based on a hash of composer.lock. This turns a 30-second composer install into a sub-second cache restore on subsequent runs.

Database setup for tests requires running migrations before the test suite. If your tests use RefreshDatabase, they handle this automatically. If not, include an explicit migrate command in the pipeline before running tests. Seed data only if your test suite requires it — most well-written test suites set up their own data via factories.

Step 3: Structure the Pipeline Stages

A production pipeline has at minimum two stages: checks and deployment. The checks stage runs on every push to every branch. The deployment stage runs only on the main branch, and only after the checks stage passes.

Checks stage (all branches): run the four parallel checks from Step 1. If any check fails, the pipeline stops and the developer gets feedback. This is the gate that prevents broken code from being merged.

Deployment stage (main branch only): after all checks pass, deploy to production. The deployment should be automated end-to-end with no manual steps. A typical Laravel deployment sequence is: sync files to the server (via rsync, Git pull, or artifact upload), install production dependencies (composer install –no-dev –optimize-autoloader), run database migrations, clear and rebuild caches (config, route, view, event caches), and restart queue workers.

Ordering matters. Install dependencies before running migrations, because migrations may depend on package code. Clear caches after installing dependencies, because cached config may reference old class paths. Restart queue workers last, because they hold the old application code in memory until restarted.

For applications with a frontend build step, compile assets locally (or in CI) and sync the compiled output to the server. Do not run npm install and npm run build on the production server — it is slow, requires Node on the server, and risks build failures in production.

Step 4: Handle Deployment Safely

Deployment is the riskiest part of the pipeline. An automated deployment that fails halfway through can leave your application in a broken state. Build safety mechanisms into the deployment step.

Atomic deployments (using tools like Deployer, Envoyer, or a symlink-based release structure) keep the previous version running until the new version is fully prepared. The switch is a single symlink change, which is effectively instant. If the new version fails, rolling back means switching the symlink back to the previous release.

Database migrations in deployment require caution. A migration that drops a column or changes a column type will break the running application if the old code is still serving requests. For zero-downtime deployments, split destructive migrations into two deployments: first deploy code that no longer references the column, then deploy the migration that removes it.

Health checks after deployment verify that the application is actually working after a deployment completes. Hit a health-check endpoint that verifies the application boots, can connect to the database, and can reach critical external services. If the health check fails, alert the team immediately.

Environment variable management should be handled separately from the deployment pipeline. Never store production credentials in your CI configuration where they are visible in logs. Use your CI platform’s secret management (encrypted variables, secret stores) or a dedicated secrets manager on the server.

Step 5: Monitor and Iterate

A CI/CD pipeline is not a set-and-forget system. Monitor its performance and reliability, and adjust as your application grows.

Pipeline duration directly affects developer productivity. If the pipeline takes ten minutes, developers context-switch between pushing code and getting feedback. Track pipeline duration over time and investigate when it increases. Common culprits: slow tests that hit external services (mock them), uncached dependencies (enable caching), and unnecessary sequential steps that could run in parallel.

Flaky tests — tests that sometimes pass and sometimes fail without code changes — erode trust in the pipeline. Developers start ignoring failures, assuming they are flaky rather than real. Identify and fix flaky tests aggressively. The most common causes are tests that depend on execution order, tests that rely on real time (use Carbon::setTestNow or equivalent), and tests that share database state.

Branch protection enforces that the pipeline must pass before code can be merged to the main branch. Enable this in your Git platform’s settings. Without branch protection, developers can merge code that fails the checks, which defeats the purpose of the pipeline.

Deployment notifications keep the team informed. Send a notification to your team’s chat channel when a deployment starts, succeeds, or fails. Include the commit hash and author so the team knows what changed and who to ask if something breaks.

Common Mistakes

  • No tests in the pipeline. A pipeline that only lints and deploys is a faster way to ship untested code. Tests are the foundation — without them, the pipeline provides a false sense of security.
  • Running the full pipeline only on the main branch. Checks should run on every branch so developers get feedback before merging. Deployment runs only on main, but checks run everywhere.
  • Sequential checks that could be parallel. Running tests, then Pint, then PHPStan, then the frontend build takes four times as long as running them in parallel. Most CI platforms support parallel jobs within a stage.
  • Not caching Composer dependencies. Every pipeline run that downloads all dependencies from scratch wastes time and bandwidth. Cache based on composer.lock.
  • Deploying without a rollback plan. Automated deployment without automated rollback means a bad deployment requires manual intervention to fix. Have a rollback mechanism before you need one.

What Good Looks Like

A well-configured Laravel CI/CD pipeline runs four parallel checks (tests, Pint, PHPStan, frontend build) on every push to every branch, completing in under three minutes. The main branch has branch protection requiring all checks to pass before merging. Deployment to production is fully automated, uses atomic releases for zero-downtime deployment, and includes a post-deployment health check. The team receives deployment notifications and has a documented rollback procedure. Pipeline duration is monitored and flaky tests are fixed within days of appearing.

Next Steps

For the test suite that your pipeline runs, How to Set Up Automated Testing covers testing strategies in depth. For the security checks that should be part of your pipeline, How to Secure a Laravel Application covers the patterns your tests should verify. For a broader look at software project delivery, see How to Run a Successful Software Sprint.

Need Hands-On Help?

Our guides give you the thinking. If you want someone to do the building, we should talk.

Start a Project Browse Case Studies