How do you build CI/CD and safe rollbacks for LAMP apps?
answer
A production-ready LAMP CI/CD builds a single artifact, runs Pest/PHPUnit and integration tests (real MySQL), and packages assets for Apache + PHP-FPM. Deployments are automated (Deployer/Ansible) with blue/green symlink releases or load-balancer canaries. Schema changes follow expand–migrate–contract so code can roll back safely. Rollback simply re-points the symlink (or image tag) and disables risky features. Monitoring uses APM, logs, metrics, and SLO-based alerts to catch issues early.
Long Answer
A reliable LAMP pipeline turns code into repeatable, reversible releases. The keys are: build once, test thoroughly, automate deployments, make databases rollback-safe, and instrument production so problems surface before customers feel them.
1) Build once, promote everywhere
Create an immutable release artifact:
- Composer --no-dev, lockfile enforced, vendor cached.
- Frontend build (Vite/Webpack) with hashed assets; publish to /public.
- Package as a release directory (releases/2025-10-02T12-34-56) or container image if you run Apache + PHP-FPM in Docker.
- Warm caches (opcode, route/config caches for frameworks) and run static checks (PHPStan/Psalm, PHP CS Fixer, composer audit).
This artifact is promoted from dev → staging → prod without rebuilding, preserving parity and enabling quick rollback.
2) Test pyramid and quality gates
- Static gates: PHPStan/Psalm, CS Fixer, secret scanning, dependency audits.
- Unit tests: Pest/PHPUnit for domain logic, controllers, and helpers; mock external boundaries.
- Integration tests: run MySQL/MariaDB and Redis via Testcontainers or Docker Compose; apply migrations; seed factories/fixtures; test repository queries and transactions.
- Contract/API tests: validate request/response with OpenAPI (or JSON Schema) to prevent consumer breakage; add Pact provider verification if your app serves other services.
- E2E (optional): Behat/Codeception or Playwright/Cypress hitting an ephemeral Apache + PHP-FPM stack for golden paths (auth, checkout, subscription).
- Performance smoke: a brief k6/JMeter step for p95 latency budget on critical endpoints.
PRs fail on high-severity vulns, broken contracts, or coverage dips on critical modules.
3) Database migrations without fear
Relational safety applies to LAMP too:
- Follow expand–migrate–contract.
Expand: add nullable columns/indexes/new tables; ship code that can read/write old and new.
Migrate: backfill via idempotent jobs (CLI cron, Supervisor/systemd), throttled to reduce lock time; monitor progress.
Contract: only after verification, drop legacy columns and remove dual-write paths. - Use Phinx/Liquibase/Doctrine Migrations with transactional DDL where supported; avoid long locks; add online indexes when possible.
- Keep a schema version label in the artifact and expose a /healthz that includes it for quick mismatch diagnosis.
4) Deployment automation
Two proven patterns:
A) Symlinked releases (classic LAMP, VMs or bare metal)
- Use Deployer (deployer.org) or Ansible roles to:
- Upload the prepared release into releases/<timestamp>.
- Run migrations in expand mode.
- Warm caches and run smoke tests against the new directory.
- Atomically update current -> releases/<timestamp>.
- Reload php-fpm (reload) and Apache (graceful) to pick up new opcache files without dropping connections.
- Upload the prepared release into releases/<timestamp>.
- Rollback: re-point current to the previous directory and graceful reload.
B) Blue/green or canary (load balancer in front)
- Provision “blue” and “green” web pools behind HAProxy/Nginx/ALB.
- Deploy to the idle pool; run smoke and synthetic checks; flip traffic or gradually ramp (1% → 25% → 50% → 100%).
- Canary gates evaluate error rate, p95 latency, and saturation; auto-pause on breach.
Both approaches support zero-downtime when done with pre-warmed opcode caches, sticky sessions where needed, and graceful process reloads.
5) Feature flags and configuration
Decouple deploy from release:
- Use Flagsmith/Unleash or a slim database-backed toggle table with local caching.
- Ship code dark; enable by cohort/tenant/percentage; keep kill switches for risky paths (new search, external payment).
- Manage environment with .env templates plus secrets in Vault/KMS; never bake secrets into artifacts.
6) Observability and SLO-gated promotion
- APM: New Relic/Datadog/Elastic APM or OpenTelemetry for PHP to trace requests, DB queries, external calls; include route/controller names and user/session IDs (non-PII) as attributes.
- Metrics: RED/USE—request rate, error %, duration; php-fpm queue length, opcache hit ratio; MySQL slow queries, buffer pool hit rate, replication delay.
- Logs: ship Apache access/error + app logs to ELK/Graylog; parse structured JSON.
- Dashboards & Alerts: SLOs such as error rate <1%, API p95 <300–500 ms, 5xx budget, and checkout success. During canary/blue-green, automation checks these signals and auto-pauses or rolls back.
7) Rollback that is boring and fast
- Application: revert symlink or image tag; never rebuild under pressure.
- Configuration: Git-revert infra or Ansible inventory; re-apply to converge.
- Data: because you used expand–migrate–contract, code rollbacks remain safe even mid-backfill; contract steps are executed only after stability.
- Flags: disable the offending feature instantly to reduce blast radius while investigating.
8) Operational hygiene
- Cron/supervisor jobs versioned with the release; health-checked.
- Blue/green DB changes (read replicas, promotion runbooks).
- Backups tested and restore drills documented.
- Security gates (SAST, dependency scan, image signing if containerized).
Bottom line: LAMP CI/CD is about repeatability (build once), realism (integration tests), safety (zero-downtime + reversible DB), and visibility (APM + SLOs) so that rollbacks are routine, not heroic.
Table
Common Mistakes
- Rebuilding artifacts per environment (drift) instead of build once, promote.
- Running only unit tests; skipping integration with real MySQL and slow-query analysis.
- Long-locking migrations or dropping columns first, making rollback unsafe.
- Apache hard restarts (killing traffic) instead of graceful reloads and php-fpm reload.
- One-shot cutovers without smoke tests or SLO gates.
- No feature flags, so mitigation requires hotfixes, not toggles.
- Logs only on servers; no centralization or correlation with APM.
- Alerting on averages instead of percentiles and burn-rate SLOs.
Sample Answers
Junior:
“I run PHPUnit on push, build the artifact with Composer --no-dev, and deploy using a symlink switch on the server. I restart services gracefully and, if errors increase, I switch the symlink back to the previous release.”
Mid:
“My pipeline runs PHPStan, Pest, integration tests against Docker MySQL, and OpenAPI contract checks. We deploy with Deployer into releases/<timestamp> and flip current atomically. DB changes use expand–migrate–contract with queued backfills. We monitor with APM and Prometheus; canaries gate on error rate and p95 latency. Rollback is a symlink revert and feature-flag disable.”
Senior:
“I standardize build-once-promote artifacts, enforce static analysis, unit/integration/E2E, and contract verification. Deployments use blue/green behind HAProxy with smoke and synthetic checks; php-fpm and Apache are reloaded gracefully. Schema evolution is expand–migrate–contract; rollback is instant via image/symlink re-point. OpenTelemetry traces, slow-query logs, and SLO burn-rates drive automated pause/rollback and post-release reports.”
Evaluation Criteria
Look for:
- Immutable artifacts and environment parity; no env-specific builds.
- Test pyramid with real MySQL integration and optional E2E; contract checks for APIs.
- Automated deployments (Deployer/Ansible) with symlinked releases or blue/green/canary.
- Safe migrations via expand–migrate–contract and monitored backfills.
- Fast rollback (symlink/image tag) and feature flags.
- Observability with APM, central logs, metrics, and SLO-gated promotions.
Red flags: manual SCP deploys, destructive migrations first, no integration testing, hard restarts, and alerting without SLOs.
Preparation Tips
- Convert tests to Pest/PHPUnit; add Testcontainers MySQL + fixtures; capture slow queries.
- Add PHPStan/Psalm, CS Fixer, and composer audit to CI.
- Learn Deployer recipes (shared dirs, writable, cache warmers) and Ansible roles for Apache/php-fpm.
- Practice symlinked releases and blue/green behind HAProxy or Nginx.
- Implement expand–migrate–contract with Phinx and a backfill CLI; add progress metrics.
- Integrate OpenTelemetry/New Relic, ELK, and Prometheus; create dashboards for p95, error %, php-fpm queue, MySQL slow-query rate.
- Add a feature-flag layer and rehearse a one-command rollback + flag disable.
Real-world Context
A publisher moved from ad-hoc rsync to Deployer with symlinked releases; rollback time dropped from 30 minutes to under 90 seconds. A retailer introduced integration tests against real MySQL and caught a locking migration locally, switching to an online index path. A SaaS team adopted blue/green; a cache-key bug surfaced at 5% canary, triggering an auto-pause and instant flip-back. After adding OpenTelemetry and slow-query dashboards, they targeted a N+1 query that cut p95 latency by 28%. Feature flags helped disable a risky upsell flow without a redeploy.
Key Takeaways
- Build once, promote immutable artifacts; warm caches.
- Use a test pyramid with real MySQL/MariaDB integration and focused E2E.
- Evolve schema with expand–migrate–contract and monitored backfills.
- Ship zero-downtime via symlink releases or blue/green/canary.
- Make rollback boring: re-point symlink/image; disable with feature flags.
- Let APM + logs + metrics and SLOs gate promotion and trigger auto-rollback.
Practice Exercise
Scenario:
You maintain a LAMP e-commerce site adding “loyalty points,” requiring a new points_balance column and checkout logic. Releases must be zero-downtime with rapid rollback.
Tasks:
- CI: PHPStan level high, CS Fixer, composer audit; run Pest unit tests.
- Integration: Start MySQL/Redis with Testcontainers; run migrations; seed fixtures; verify repository and transaction behavior.
- Contracts: Generate OpenAPI for checkout endpoints; compare schemas; fail on breaking diffs.
- Artifact: Build release dir with Composer --no-dev, hashed assets, warmed caches.
- DB Safety: Implement expand (nullable points_balance), deploy dual-write reads/writes, queue backfill; expose progress metrics.
- Deploy: Use Deployer to upload, run expand migrations, warm caches, run smoke tests, and flip current symlink; Apache/php-fpm graceful reload.
- Gates: Canary 10% behind HAProxy; gate on p95 latency, 5xx <1%, and checkout success; auto-pause on breach.
- Flags: Wrap loyalty application with a feature flag; enable for employees first; keep a kill switch.
- Rollback drill: At 25% canary, simulate error spike; revert symlink to the previous release and disable the flag; confirm dashboards recover; keep schema expanded.
- Report: Produce a post-mortem with root cause, added tests, and timeline.
Deliverable:
A repo and runbook demonstrating LAMP CI/CD, safe migrations, zero-downtime deploys, SLO-gated canaries, and instant rollback with feature-flag mitigation.

