
August 20, 2026
10 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Shipping code without automated testing and deployment is a liability for any production web system. This Bitbucket Pipelines CI/CD Guide provides the exact configuration patterns I use to automate Laravel and PHP application delivery, eliminating manual FTP uploads and inconsistent server states. Whether you are maintaining a legal-tech portal or a high-traffic eCommerce store, proper pipeline configuration ensures your production environment matches your local development setup every single time.
bitbucket-pipelines.yml file at your repository root with specific Docker images (PHP 8.4), service containers for MySQL/Redis, and step-level caching for Composer and npm dependencies to achieve fast, reliable automated builds.For teams evaluating their infrastructure options, understanding the difference between platform-managed pipelines and self-hosted runners is critical before writing your first YAML line. If you are also exploring backend architecture decisions alongside your DevOps setup, reading about modern Laravel architecture best practices will help align your pipeline stages with your application's actual structural needs.
How do you configure Bitbucket Pipelines for Laravel applications?
Laravel 12.x requires PHP 8.2 minimum, but in 2026 most production systems should target PHP 8.4 for performance and security updates. Your bitbucket-pipelines.yml must specify an image that includes the necessary PHP extensions for Laravel: mbstring, xml, zip, bcmath, pdo_mysql, and redis. Using the official Atlassian PHP images or community-maintained Laravel-specific images prevents extension mismatch errors that commonly break builds.
Base pipeline configuration
The following configuration runs on every push to main and develop branches. It uses services for MySQL and Redis, matching the typical Laravel stack. Note that the MySQL service uses a temporary filesystem (tmpfs) to speed up database operations during testing — this is safe for CI because data does not persist between steps.
<?php
# bitbucket-pipelines.yml
image: php:8.4-cli
definitions:
services:
mysql:
image: mysql:8.4
variables:
MYSQL_DATABASE: 'testing'
MYSQL_ROOT_PASSWORD: 'secret'
MYSQL_USER: 'laravel'
MYSQL_PASSWORD: 'secret'
redis:
image: redis:7.4
pipelines:
branches:
main:
- step:
name: Test & Build
size: 2x
caches:
- composer
- node
services:
- mysql
- redis
script:
- apt-get update && apt-get install -y libzip-dev unzip git nodejs npm
- docker-php-ext-install zip pdo_mysql bcmath opcache
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
- composer install --no-interaction --prefer-dist --optimize-autoloader
- cp .env.ci .env
- php artisan key:generate
- php artisan migrate:fresh --force
- ./vendor/bin/phpunit --coverage-clover=coverage.xml
- npm ci && npm run build
artifacts:
- vendor/
- public/build/
- bootstrap/cache/** A common mistake I see on client projects is forgetting to copy a CI-specific .env.ci file before running Artisan commands. Laravel will fail silently or throw cryptic errors if the environment file is missing. Always include this step explicitly rather than relying on default behaviour.
Environment variable management
Never commit secrets to your repository. Use Bitbucket workspace or repository variables for sensitive values like APP_KEY, database passwords for staging databases, API keys, and SSH private keys. Mark sensitive variables as "secured" in the Bitbucket UI so they are masked in logs and cannot be retrieved via the API. For Laravel projects, store the base64-encoded APP_KEY as a secured variable and decode it in your pipeline script:
script:
- echo $APP_KEY_BASE64 | base64 -d > .env.app_key
- cat .env.ci .env.app_key > .env How do you optimise Bitbucket Pipelines build times for PHP projects?
Unoptimised PHP pipelines frequently exceed 10–15 minutes per build, primarily due to redundant dependency installation. Caching is the single most impactful optimisation for Laravel and Symfony projects. Bitbucket Pipelines supports built-in cache definitions for Composer and npm, but you must configure them correctly to avoid stale dependencies.
Correct cache key configuration
The default cache key in many tutorials uses branch names, which causes unnecessary cache misses. Instead, use file checksums to create deterministic cache keys. Define custom caches in your pipeline definitions:
definitions:
caches:
composer: vendor
node: node_modules
pipelines:
default:
- step:
caches:
- composer
- node
script:
- composer validate --strict
- composer install --no-interaction --prefer-dist --optimize-autoloader Bitbucket automatically generates cache keys based on the cache name and branch by default. For better hit rates across branches, consider using custom cache keys based on lock file hashes. This ensures that feature branches with identical dependencies share the same cache as main, reducing build times from 8+ minutes to under 3 minutes on cache hits.
Parallel step execution
For larger Laravel applications with extensive test suites, split testing and asset compilation into parallel steps. Both steps can restore from the same Composer cache independently:
- Step A (Test): Runs PHPUnit/Pest with database migrations against MySQL service
- Step B (Assets): Runs Vite build for production CSS/JS bundles
- Step C (Deploy): Depends on both A and B completing successfully
This parallelism reduces total pipeline duration significantly for projects where asset compilation takes 60+ seconds. On one legal-tech portal I maintain, parallelising these steps cut the feedback loop from 12 minutes to 7 minutes.
What is the best deployment strategy for zero-downtime Laravel releases?
Zero-downtime deployment means serving requests continuously during releases. The symlink-swap pattern is the industry standard for PHP applications and works reliably with Bitbucket Pipelines. Tools like Deployer 7 automate this process, creating timestamped release directories, sharing persistent storage and environment files, and atomically swapping the current symlink.
Integrating Deployer with Bitbucket Pipelines
Add Deployer as a dev dependency and invoke it from your pipeline. Store the SSH private key as a secured Bitbucket variable and write it to a temporary file during deployment:
- step:
name: Deploy to Production
deployment: production
script:
- apt-get update && apt-get install -y openssh-client rsync
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
- echo $SSH_PRIVATE_KEY | base64 -d > ~/.ssh/id_ed25519
- chmod 600 ~/.ssh/id_ed25519
- ssh-keyscan -H your-server.com >> ~/.ssh/known_hosts
- curl -LO https://deployer.org/deployer.phar
- php deployer.phar deploy production --no-interaction Always use the deployment keyword in Bitbucket Pipelines. This enables environment-specific variable scoping and provides deployment tracking in the Bitbucket UI. Production deployments should require manual approval or be restricted to the main branch only.
Post-deployment verification
After the symlink swap, verify the deployment succeeded before marking the pipeline as complete. Add a health-check step that curls your application's health endpoint and validates the response:
- step:
name: Verify Deployment
script:
- RESPONSE=$(curl -sf https://your-app.com/api/health)
- echo "$RESPONSE" | grep -q '"status":"ok"' || exit 1
- echo "Deployment verified successfully" This catches scenarios where the deployment completed technically but the application is broken due to misconfiguration, missing environment variables, or failed migrations. I have prevented multiple production outages on client sites by including this simple verification gate.
How do Bitbucket Pipelines compare to GitHub Actions and GitLab CI for PHP?
Choosing a CI/CD platform depends on your existing code hosting, team size, budget, and specific workflow requirements. Each platform has distinct trade-offs for PHP and Laravel projects.
| Criteria | Bitbucket Pipelines | GitHub Actions | GitLab CI/CD |
|---|---|---|---|
| Free tier minutes | 50 min/month (workspace) | 2,000 min/month (public repos) | 400 min/month (shared runners) |
| Docker flexibility | Full Docker support, custom images | Extensive marketplace actions | Native container registry integration |
| Self-hosted runners | Available (Linux, macOS, Windows) | Available (all platforms) | Available (most flexible) |
| Integrated issue tracking | Jira integration (native) | GitHub Issues/Projects | GitLab Issues/Milestones |
| PHP ecosystem support | Good, fewer pre-built actions | Excellent (setup-php action) | Excellent, strong PHP templates |
| Nepal payment/billing | Credit card only (USD) | Credit card only (USD) | Credit card only (USD) |
| Best for | Jira-using teams, Atlassian stack | Open source, large communities | Self-hosted, enterprise, DevOps-heavy |
For Nepal-based teams already using Jira for project management, Bitbucket Pipelines offers seamless integration that reduces context switching. However, if your primary concern is maximising free CI minutes for open-source Laravel packages or learning projects, GitHub Actions provides significantly more generous free tiers. For teams requiring full infrastructure control or operating behind corporate firewalls, GitLab CI with self-managed runners remains the most flexible option.
If you are a freelancer or agency owner evaluating costs, remember that all three platforms bill in USD. At current exchange rates, exceeding free tiers can cost Rs 1,500–5,000/month (~USD 11–37). Budget accordingly and monitor usage dashboards regularly. For more context on managing freelance infrastructure costs, see the Nepal income tax guide for freelancers.
How do you troubleshoot common Bitbucket Pipelines failures?
Pipeline failures fall into predictable categories. Understanding these patterns saves hours of debugging time.
Memory and resource limits
Laravel applications with large test suites or complex asset builds frequently exceed the default 1GB memory allocation. When you see "Out of memory" or "Killed" errors, increase the step size:
- step:
size: 2x # 4GB RAM, 2 CPU cores
script:
- ./vendor/bin/phpunit Sizes available in 2026: 1x (default, 1GB), 2x (4GB), 4x (8GB), 8x (16GB). Start with 2x for Laravel projects; only escalate if profiling confirms genuine memory pressure. Larger sizes consume build minutes faster.
Service container connectivity
MySQL and Redis services take several seconds to initialise. If your tests fail with connection refused errors immediately after starting, add a wait loop:
script:
- until mysqladmin ping -h127.0.0.1 -ularavel -psecret --silent; do sleep 1; done
- php artisan migrate:fresh --force This pattern is essential. I have seen countless pipelines that pass intermittently because the database was sometimes ready before tests ran and sometimes not. Deterministic waits eliminate this flakiness entirely.
Permission and ownership issues
Bitbucket Pipelines runs as root inside containers. When deploying artefacts to servers running as www-data or another non-root user, file permissions can prevent PHP-FPM from reading cached views or writing logs. Always set correct ownership during deployment:
script:
- chown -R www-data:www-data storage/ bootstrap/cache/
- chmod -R 775 storage/ bootstrap/cache/ For teams managing multiple Laravel applications or integrating CI/CD into broader DevOps workflows, working with a CI/CD pipeline setup expert in Nepal can prevent weeks of trial-and-error configuration.
Implementing Reliable Bitbucket Pipelines CI/CD Guide Workflows
Effective CI/CD is boring, predictable, and invisible when working correctly. Start with the base Laravel configuration shown above, add caching immediately, implement zero-downtime deployments with Deployer, and always include post-deployment verification. Monitor your build times weekly — if they creep above 10 minutes, profile and optimise before developer frustration sets in.
Your pipeline is production infrastructure. Treat it with the same rigour as your application code: version control your YAML, review changes in pull requests, and document non-obvious decisions. The Bitbucket Pipelines CI/CD Guide patterns here reflect real production systems serving Nepali businesses daily. Adapt them to your specific constraints, but do not skip the fundamentals.
Ready to automate your Laravel deployments or need help configuring pipelines for an existing project? Contact me to discuss your CI/CD requirements.

