The implementation of Continuous Integration and Continuous Deployment (CI/CD) for Laravel applications represents a critical shift from manual server management to automated software delivery. In a modern development lifecycle, the objective is to ensure that every code push is systematically validated through automated testing and quality gates before reaching a production environment. This process eliminates the "it works on my machine" syndrome by utilizing containerization and orchestrated pipelines. By integrating GitLab CI/CD, developers can create a seamless bridge between the version control system and the target infrastructure, whether that infrastructure is a lightweight Kubernetes cluster, a traditional Ubuntu server, or a managed cloud environment like AWS Elastic Beanstalk.
The primary goal of these architectures is to automate the building, testing, and deployment phases. When a developer pushes code to a branch, GitLab CI/CD triggers a series of jobs defined in a configuration file. This ensures that unit tests are executed and code quality is verified using tools like PHP CodeSniffer. If these stages pass, the application is packaged into a Docker image and deployed to the server. This automation reduces human error and ensures that updates are delivered smoothly without breaking the live application.
Infrastructure and Technology Stack Configurations
The choice of technology stack for a Laravel CI/CD pipeline significantly impacts the scalability and cost-efficiency of the deployment. Depending on the organizational needs, different combinations of tools are employed to achieve the desired deployment frequency and reliability.
Kubernetes and Cloud-Native Stack
For high-availability environments, a cloud-native stack involving Kubernetes is often preferred. This approach leverages the orchestration capabilities of K3s, a lightweight distribution of Kubernetes, which allows for efficient resource utilization even on low-cost hardware.
| Component | Technology | Purpose |
|---|---|---|
| Application Framework | Laravel | Core PHP web framework |
| Containerization | Docker | Environment isolation and packaging |
| Repository & CI/CD | GitLab | Version control and pipeline orchestration |
| Container Registry | GitLab Container Registry | Storage for built Docker images |
| Orchestration | K3s (Kubernetes) | Container management and scaling |
| Deployment Automation | ArgoCD | GitOps-based continuous delivery |
| Infrastructure | Hetzner VPS | Hosting server (Example: 4GB RAM / 2 CPU cores) |
| Operating System | Debian | Base OS for the server |
| Networking/Security | Cloudflare | DNS management and SSL termination |
| Management UI | Portainer | Visual container management |
The use of a Hetzner server priced at approximately $5 provides a cost-effective entry point for deploying a K3s cluster. Cloudflare is integrated to handle the custom domain and provide HTTPS via SSL, which is a mandatory requirement for modern web security. Portainer and ArgoCD provide the necessary visibility into the infrastructure, allowing administrators to monitor the state of the pods and the health of the deployment without relying solely on the command line.
AWS Elastic Beanstalk Stack
An alternative approach involves utilizing AWS Elastic Beanstalk, which abstracts the underlying EC2 instances and provides a managed environment for deploying Laravel. This setup focuses on branch-based deployments, where specific actions are tied to the state of the git branch.
- Master branch: Used for production. Deployments to this branch are typically set to manual to prevent accidental pushes from triggering a production update.
- Staging branch: Used for final verification. If PHPUnit tests pass on this branch, the application is automatically deployed to the staging environment on Elastic Beanstalk.
This architecture requires an IAM user with Elastic Beanstalk Full Access permissions, utilizing Access and Secret Keys for authentication. The EB CLI is used to initialize the environment through the eb init command, which creates a .elasticbeanstalk folder in the project root to store environment configurations.
Containerization Strategy for Laravel
Containerizing a Laravel application ensures that the environment remains consistent across development, staging, and production. This is achieved by defining a Dockerfile that encapsulates the PHP runtime and all necessary system dependencies.
The Dockerfile Specification
A robust Docker environment for Laravel requires a base image that includes PHP and the Apache web server. The following configuration illustrates the professional standard for a Laravel Docker image:
```dockerfile
Base image with PHP and Apache
FROM php:8-apache
Install system dependencies and PHP extensions
RUN apt-get update && apt-get install -y \
curl \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libzip-dev \
zip \
unzip \
supervisor \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install gd bcmath pdo pdo_mysql zip
Install pcov for laravel test coverage
RUN pecl install pcov && \
docker-php-ext-enable pcov
Enable Apache modules
RUN a2enmod rewrite
Set working directory
WORKDIR /var/www/html
Copy existing application directory contents
COPY ./src .
Install Composer
COPY --from=composer /usr/bin/composer /usr/bin/composer
Copy custom Apache virtual host configuration
COPY conf/apache/000-default.conf /etc/apache2/sites-available/000-default.conf
Expose port 80
EXPOSE 80
Start Apache in the foreground
CMD ["apache2-foreground"]
```
The impact of this configuration is twofold: first, it ensures that all required PHP extensions like gd, bcmath, pdo_mysql, and zip are present, preventing runtime errors during the execution of Laravel's core functions. Second, the installation of pcov allows the CI/CD pipeline to generate accurate test coverage reports, which is essential for maintaining high software quality.
Project Directory Structure
A well-organized project structure is vital for the CI/CD pipeline to locate the source code and configuration files. The standard layout for a containerized Laravel project is as follows:
- laravel-app/
- app/
- artisan
- bootstrap/
- components.json
- composer.json
- composer.lock
- config/
- database/
- eslint.config.js
- node_modules/
- package.json
- package-lock.json
- phpunit.xml
- public/
- resources/
- routes/
- storage/
- tests/
- tsconfig.json
- vendor/
- vite.config.ts
- docker/
- compose.yaml
The docker/ folder separates infrastructure concerns from application logic, while the laravel-app/ folder contains the entire framework and custom code.
GitLab CI/CD Pipeline Configuration
The heart of the automation process is the .gitlab-ci.yml file. This file defines the stages, jobs, and environment variables required to move code from a commit to a live server.
Pipeline Stages and Environment Variables
The pipeline is divided into four primary stages: build, test, quality, and deploy. To facilitate these, a default image of docker:latest is used, with docker:dind (Docker-in-Docker) as a service to allow the building of images within the pipeline.
The following configuration snippet defines the environment and the build stage:
```yaml
default:
image: docker:latest
services:
- docker:dind
beforescript:
- echo "$CIREGISTRYPASSWORD" | docker login -u $CIREGISTRYUSER $CIREGISTRY --password-stdin
variables:
DOCKERDRIVER: overlay2
MYSQLDATABASE: laravel
MYSQLROOTPASSWORD: secret
MYSQLUSER: laravel
MYSQLPASSWORD: secret
TESTDBHOST: mysql
TESTDBPORT: 3306
IMAGE: $CIREGISTRYIMAGE/app:latest
stages:
- build
- test
- quality
- deploy
build:
stage: build
script:
- docker pull $IMAGE || true
- docker run --rm -v $PWD/src:/app composer install --optimize-autoloader --prefer-dist --no-scripts
- docker build -f php.dockerfile --build-arg BUILDKITINLINECACHE=1 --cache-from $IMAGE --tag $CIREGISTRYIMAGE/app:$CICOMMITSHA --tag $IMAGE .
- docker push $IMAGE
- docker push $CIREGISTRYIMAGE/app:$CICOMMITSHA
only:
- main
cache:
key: ${CICOMMITREF_SLUG}-composer
paths:
- src/vendor/
policy: push
```
The build stage performs several critical actions. First, it attempts to pull the existing image to use as a cache, which significantly speeds up subsequent builds. Second, it runs composer install within a temporary container to resolve dependencies. Third, it builds the final image using the php.dockerfile and tags it with both the commit SHA (for traceability) and the latest tag. Finally, the image is pushed to the GitLab Container Registry.
Testing and Quality Assurance
Once the build is complete, the pipeline moves to the test and quality stages. This ensures that no regression bugs are introduced into the production environment.
- Unit Testing: The pipeline runs tests using either Pest or PHPUnit. A special
.env.cifile is utilized during this stage to provide the necessary database credentials and application settings specifically for the testing environment. - Code Quality: PHP CodeSniffer (PHP-CS) is integrated to enforce coding standards. This step prevents the codebase from becoming inconsistent, which is vital for long-term maintainability in team environments.
Advanced Deployment Strategies
Depending on the target environment, the deployment mechanism varies from direct server pushes to sophisticated GitOps workflows.
Deployment to Ubuntu Servers
In a traditional setup, the application is deployed to an Ubuntu server with Docker installed. The pipeline handles the pulling of the latest image from the registry and the restarting of the containers. This is often managed via Docker Compose to ensure that the application and its dependencies (like MySQL) are started in the correct order.
AWS Elastic Beanstalk and .ebextensions
For AWS deployments, the process leverages the EB CLI. To manage server-side configurations, the .ebextensions folder is used. This folder contains YAML files that instruct AWS to run specific commands during the deployment lifecycle.
To set up these extensions, the following command can be used to import a proven configuration:
bash
svn export https://github.com/mjsarfatti/laravel-eb-gitlab-ci/trunk/.ebextensions
These extensions handle critical tasks such as:
- Running composer install to ensure dependencies are present.
- Executing php artisan migrate to update the database schema.
- Configuring Apache to serve the application from the /public folder.
- Implementing HTTP to HTTPS redirection to ensure secure traffic.
GitOps with ArgoCD and K3s
In the most advanced setup, ArgoCD is used to implement a GitOps workflow. Instead of the CI pipeline pushing code to the server, ArgoCD monitors the Git repository (or a separate config repo) and "pulls" the state of the cluster to match the desired state defined in the manifests.
This setup provides several advantages:
- Automatic Synchronization: Any change to the Kubernetes manifests in Git is automatically reflected in the K3s cluster.
- Visibility: The ArgoCD dashboard provides a visual representation of the application's health and deployment status.
- Rollback Capability: Reverting a deployment is as simple as reverting a commit in Git.
Comprehensive Summary of CI/CD Workflows
The implementation of these pipelines varies based on the desired level of automation and the underlying infrastructure.
- For Rapid Prototyping: Use GitLab CI with a simple Docker Compose setup on an Ubuntu server.
- For Enterprise Scaling: Use GitLab CI, K3s, and ArgoCD for a full GitOps experience.
- For Managed Cloud: Use GitLab CI combined with AWS Elastic Beanstalk and
.ebextensions.
The impact of these workflows is a drastic reduction in the time between "code complete" and "production live." By enforcing a strict sequence of build, test, quality, and deploy, organizations can maintain a high velocity of feature delivery without sacrificing system stability.
Conclusion
The architectural transition to a fully automated Laravel CI/CD pipeline via GitLab is not merely a technical upgrade but a strategic necessity for modern software engineering. By integrating Docker for environmental consistency, K3s or AWS Elastic Beanstalk for scalable orchestration, and GitLab CI/CD for process automation, developers can achieve a state of continuous delivery. The use of specialized tools such as ArgoCD for GitOps and PHP CodeSniffer for quality assurance ensures that the deployment process is transparent, repeatable, and resilient. The integration of a dedicated testing environment using .env.ci and the automation of database migrations via .ebextensions further solidifies the reliability of the deployment. Ultimately, these systems move the burden of deployment from manual, error-prone scripts to a deterministic, code-driven pipeline, allowing developers to focus on building features rather than managing infrastructure.