Orchestrating Laravel Deployments via GitLab CI/CD and Docker

The integration of Laravel, Docker, and GitLab CI/CD represents a paradigm shift in how modern PHP applications are delivered from a local development environment to a production-ready state. By leveraging containerization, developers can eliminate the "it works on my machine" phenomenon, ensuring that the environment where the code is written is identical to the environment where it is tested and eventually executed. This architectural approach utilizes GitLab CI/CD as the central nervous system, automating the pipeline of building images, executing rigorous test suites, and deploying to diverse infrastructures ranging from lightweight Kubernetes clusters (K3s) on VPS providers like Hetzner to scalable cloud environments such as AWS Elastic Beanstalk.

The core of this ecosystem is the containerization of the Laravel application. By using Docker, the application and its systemic dependencies—such as PHP extensions, Apache, and Composer—are packaged into a single, portable image. When this image is integrated into a GitLab CI/CD pipeline, every code push triggers a series of automated stages. These stages ensure that only code that has passed unit tests (via Pest or PHPUnit) and quality checks (via PHP CodeSniffer) is permitted to move forward. The deployment phase then varies based on the target infrastructure: it may involve a direct SSH push to an Ubuntu server, an update to a Kubernetes manifest monitored by ArgoCD, or an API-driven deployment to AWS.

Infrastructure and Technology Stack Requirements

Depending on the target deployment strategy, the required technology stack varies. The following table outlines the specific components used across the different implementation paths discussed in these technical workflows.

Component Ubuntu Server Path K3s/Kubernetes Path AWS Elastic Beanstalk Path
OS Ubuntu Debian AWS Linux/Amazon Linux
Container Engine Docker / Docker Compose K3s / Docker Docker
Orchestration Manual / SSH Kubernetes / ArgoCD AWS Beanstalk
CI/CD Tool GitLab CI/CD GitLab CI/CD GitLab CI/CD
Registry GitLab Container Registry GitLab Container Registry AWS ECR / GitLab Registry
Hardware/Cloud Ubuntu VPS Hetzner VPS (4GB RAM/2 CPU) AWS Cloud
Network/DNS Manual DNS Cloudflare DNS + SSL AWS Route 53 / Cloudflare
Visibility Terminal / SSH Portainer / ArgoCD Dashboard AWS Management Console

Containerizing the Laravel Application

The foundation of a robust CI/CD pipeline is a well-defined Dockerfile. The goal is to create an image that contains all necessary binaries and configurations to run Laravel in a production-ready state.

The standard Docker configuration for Laravel typically starts from a PHP image with Apache integrated. For instance, using php:8-apache as the base image provides a stable foundation. The build process involves several critical layers:

  1. System Dependencies: The installation of system-level packages such as curl, libpng-dev, libjpeg-dev, libfreetype6-dev, libzip-dev, zip, unzip, and supervisor. These are essential for handling image processing, file compression, and process management.
  2. PHP Extensions: The configuration and installation of specific extensions including gd (configured with freetype and jpeg), bcmath, pdo, pdo_mysql, and zip. These extensions allow Laravel to communicate with databases and handle complex mathematical operations and image manipulation.
  3. Testing Tools: The installation of pcov via PECL. This is critical for the CI/CD pipeline to generate test coverage reports, allowing developers to see which parts of the codebase are not being exercised by the test suite.
  4. Web Server Configuration: Enabling the Apache rewrite module via a2enmod rewrite, which is mandatory for Laravel's routing system to function correctly.
  5. Application Integration: Copying the source code from the local directory into /var/www/html and integrating Composer by copying the binary from the official Composer image.
  6. Virtual Host Setup: Overwriting the default Apache configuration with a custom 000-default.conf to ensure the document root points to the /public directory of the Laravel application.

The resulting image exposes port 80 and uses apache2-foreground as the primary command to keep the container running and serving traffic.

GitLab CI/CD Pipeline Configuration

The .gitlab-ci.yml file acts as the blueprint for the entire automation process. This file defines the stages the code must pass through before it is considered deployable.

Build and Test Stages

In a comprehensive Laravel pipeline, the "Build" stage is not just about compiling assets but ensuring the environment is stable.

  • Unit Testing: The pipeline executes tests using PHPUnit or Pest. To facilitate this, a .env.ci file is used during the build and test stages. This ensures that the CI environment uses a specific set of credentials and configurations (such as a dedicated testing database) rather than production secrets.
  • Code Quality: The use of PHP CodeSniffer (PHP-CS) ensures that the code adheres to defined styling standards, preventing "dirty" code from entering the main branch.
  • Image Creation: Once tests pass, the pipeline builds the Docker image. The image is tagged with a version (e.g., v0.0.1) and pushed to the GitLab Container Registry. This registry acts as a secure, private storage for images that the deployment server can pull from.

Deployment Strategies

The deployment stage is where the pipeline interacts with the external infrastructure. There are three primary methods utilized depending on the architecture.

Deployment to Ubuntu Servers via SSH

This method is common for smaller projects or those requiring direct control over the VPS.

  • SSH Key Management: A private SSH key is generated (usually ~/.ssh/id_rsa). The public key is added to the server, while the private key is stored as a GitLab CI/CD variable named SSH_PRIVATE_KEY.
  • Network Configuration: The server's IP address is stored as a masked variable named PRODUCTION_IP to prevent the IP from being exposed in the pipeline logs.
  • Execution: The pipeline uses these credentials to SSH into the server and trigger a pull of the latest Docker image and a restart of the containers.

Deployment via Kubernetes and ArgoCD

For high-availability environments, a GitOps approach using K3s and ArgoCD is implemented.

  • Image Tagging: The pipeline updates the image tag in the kubernetes/apps/lci/values.yaml file.
  • GitOps Workflow: A commit is made back to the repository with the updated image tag.
  • Automatic Synchronization: ArgoCD monitors the repository for changes. Once it detects a new tag in the values.yaml file, it automatically applies the change to the K3s cluster, pulling the new image from the GitLab Registry and performing a rolling update.
  • Management Tools: Portainer and the ArgoCD dashboard are used for infrastructure visibility, allowing administrators to monitor pod health and container status.

Deployment to AWS Elastic Beanstalk

Deploying to AWS requires a different set of authentication and integration steps.

  • IAM Configuration: An IAM user must be created in the AWS Console with an Access Key ID and Secret Access Key.
  • GitLab Variable Integration: The following variables must be configured in the GitLab project settings:
    • AWS_ACCESS_KEY_ID
    • AWS_SECRET_ACCESS_KEY
    • AWS_DEFAULT_REGION
  • Pipeline Logic: The .gitlab-ci.yml file coordinates the upload of the application bundle to AWS. A specific feature in some configurations allows users to skip testing by adding [skip tests] to the commit message, which saves CI minutes during minor documentation updates.

Practical Implementation Steps

To implement these systems, developers must follow a sequence of configuration and command executions.

Local Setup and Image Preparation

Before the pipeline can run, the image must be successfully built and pushed to the registry.

  1. Authentication: Obtain the CI_REGISTRY, CI_REGISTRY_USERNAME, and CI_REGISTRY_TOKEN (created via Profile > Edit profile > Access tokens with read_registry and write_registry permissions).
  2. Registry Login:
    bash docker login registry.gitlab.com
  3. Building the Image:
    bash docker build -f ./docker/Dockerfile --target prod -t registry.gitlab.com/<username>/laravel-cicd:v0.0.1 ./
  4. Pushing to Registry:
    bash docker push registry.gitlab.com/<username>/laravel-cicd:v0.0.1

GitLab Variable Configuration

The security of the pipeline depends on the correct use of GitLab CI/CD variables.

  • For AWS deployments, users must navigate to Settings > CI/CD and add the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
  • For Ubuntu server deployments, the SSH_PRIVATE_KEY must be pasted into the value field, and the PRODUCTION_IP must be marked as "masked" and "hidden" to ensure security.
  • For database testing, the MYSQL_DATABASE variable should be set to match the testing database defined in the phpunit.xml file.

Detailed Analysis of the CI/CD Workflow

The transition from a manual deployment process to an automated GitLab CI/CD pipeline fundamentally changes the development lifecycle. In a traditional manual setup, a developer would SSH into a server, run git pull, run composer install, and restart the server. This is prone to human error and creates significant downtime.

The automated approach solves this by utilizing an "immutable infrastructure" pattern. When a Docker image is built, it is locked. The exact same bytes that were tested in the CI stage are the bytes that are deployed to production. This eliminates the risk of environment drift, where the production server has a different version of a PHP extension than the development machine.

The inclusion of ArgoCD in the Kubernetes path further enhances this by introducing GitOps. In GitOps, the state of the infrastructure is defined in Git. If a pod in the K3s cluster fails or is manually changed, ArgoCD detects the drift from the values.yaml file in the repository and automatically reverts the cluster to the desired state. This provides a level of self-healing that is impossible in standard VPS deployments.

Furthermore, the integration of AWS Elastic Beanstalk allows for effortless scaling. By utilizing GitLab's provided Docker images with AWS libraries, the pipeline can trigger deployments that scale the underlying EC2 instances based on traffic, while still maintaining the rigorous testing standards defined in the .gitlab-ci.yml file.

Conclusion

The implementation of a GitLab CI/CD pipeline for Laravel, supported by Docker and various deployment targets, creates a professional-grade software delivery pipeline. By moving from simple script-based deployments to containerized workflows, organizations achieve greater consistency and reliability. Whether using a low-cost Hetzner VPS with K3s and ArgoCD for cost-efficiency or AWS for global scalability, the core principle remains the same: automate everything.

The use of a structured pipeline—starting from the php:8-apache base image, moving through automated Pest/PHPUnit testing and PHP-CS quality checks, and ending with a secure deployment via SSH or GitOps—minimizes the risk of production failures. The ability to use a .env.ci file separates the concerns of testing and production, while the use of GitLab's masked variables ensures that sensitive credentials like AWS_SECRET_ACCESS_KEY or SSH_PRIVATE_KEY are never exposed. Ultimately, this setup allows developers to focus on writing features rather than managing servers, transforming the deployment process from a high-stress event into a non-event.

Sources

  1. Build With Code
  2. 9ovind Projects
  3. GitLab Documentation
  4. Dev.to - MJSarfatti

Related Posts