Troubleshooting Laravel CI/CD Pipelines: A DevOps Case Study

Introduction: When Laravel Assets Disappear in Deployment
Continuous Integration and Deployment (CI/CD) pipelines save developers countless hours, but when they break, debugging can be frustratingly complex. This case study documents a real-world troubleshooting journey that DevOps engineers and Laravel developers will find familiar.
My challenge: compiled assets were visible in local builds but missing on the production server after deployment. A seemingly simple issue that took a full day to resolve.
The Project Setup
My setup included:
- Framework: Laravel application with Laravel Mix
- CI/CD: Bitbucket Pipelines
- Server: RunCloud-managed VPS
- Asset Pipeline: NPM scripts building assets into “dist”
- Environment: Separate staging and production environments
The Symptom: Empty “dist” Directory
Despite successful builds in our pipeline, our application’s dist directory on the staging server contained only a fonts
folder—all other compiled assets (JS, CSS) were missing.
$ ls -l /home/runcloud/webapps/application/public/dist
total 4
drwxrwxr-x+ 2 runcloud runcloud 4096 Jun 20 13:06 fonts
Yet the pipeline logs showed a properly populated directory after the build step:
$ ls -l public/dist
total 32
drwxrwxrwx 2 root root 12288 Jun 20 13:05 chunks
drwxrwxrwx 2 root root 4096 Jun 20 13:05 css
drwxrwxrwx 3 root root 4096 Jun 20 13:05 fe-js
drwxrwxrwx 2 root root 4096 Jun 20 13:04 fonts
-rw-rw-rw- 1 root root 517 Jun 20 13:05 mix-manifest.json
drwxrwxrwx 3 root root 4096 Jun 20 13:05 sdk
What happened to my assets between build and deployment?
Phase 1: Environment Configuration
The .env Conundrum
Our first suspicion was the build environment. Laravel Mix requires certain environment variables (MIX_*
) to properly build assets.
Problem: Environment variables in .env file were present on the server but not in the Bitbucket Pipeline.
Solution: Add environment variables to the build step:
- echo "MIX_MEDIA=$MIX_MEDIA_STAGING" >> .env
- echo "MIX_URL=$MIX_URL_STAGING" >> .env
# More environment variables...
Lesson: CI/CD environments need the same build-time variables as local development.
Phase 2: SSH and Rsync Configuration
Deployment required SSH keys and proper rsync
commands. I encountered several issues:
- SSH Authentication: Keys needed to be generated and added to Bitbucket
- Rsync Missing: The deployment container didn’t have
rsync
installed - Permission Issues:
rsync
with default flags changed permissions
Solution:
- apt-get update && apt-get install -y rsync openssh-client
- rsync -avz --no-perms --no-owner --no-group --delete […]
Lesson: Always use --no-perms --no-owner --no-group
flags with rsync
in CI/CD to preserve server permissions.
Phase 3: Multi-Container Pipeline Architecture
After addressing environment and SSH issues, we still had missing assets. The breakthrough came when we examined Bitbucket Pipelines architecture.
The Critical Discovery: Bitbucket Pipelines runs each step in a separate container with its own filesystem. Files don’t persist between steps unless explicitly defined as artifacts.
My pipeline had separate steps for:
- Building assets with Node.js
- Deploying with PHP
But the built assets weren’t being passed between containers!
Solution: Add artifacts to the build step:
- step:
name: Build for staging
# ... build commands ...
artifacts:
- public/**
Lesson: In multi-step pipelines, files don’t automatically persist between steps.
The Solution: Working Pipeline Configuration
My final working configuration included:
- Environment variables properly passed to the build step
- SSH and rsync properly configured
- Artifacts to pass built assets between pipeline steps
- Debugging commands to verify files at each stage
- step:
name: Build for staging
image: node:23.5.0
script:
- npm install
# Environment variables
- echo "MIX_MEDIA=$MIX_MEDIA_STAGING" >> .env
# ... more env vars ...
- npm run build
- ls -l public/dist # Verify build output
artifacts:
- public/**
Followed by proper deployment:
- step:
name: Deploy to staging
script:
- apt-get update && apt-get install -y rsync openssh-client
- ls -l public/dist # Verify artifacts were received
- rsync -avz --no-perms --no-owner --no-group --delete [...] public/ runcloud@server:/path/to/public/
Key Insights for DevOps Engineers
- Container Isolation: Each pipeline step is isolated—use artifacts to share files
- Environment Variables: Build environments need the same variables as development
- Rsync Options: Use
--no-perms --no-owner --no-group
to preserve server permissions - Troubleshooting: Add commands like
ls -l
at each step to track file presence - Deployment Strategy: For Laravel apps, consider separate syncs for public and the full application
Conclusion: DevOps Is Detective Work
This case study illustrates the complexity of modern web application CI/CD pipelines. What seemed like a simple deployment issue required investigating:
- Build environments
- SSH configuration
- File transfer settings
- Container architecture
- File permissions
For teams implementing CI/CD for Laravel applications, understanding these elements is crucial for a smooth deployment pipeline.