A deploy should not be a moment of hope. Ours is a pipeline: it checks the code, builds the release, backs up the database, switches versions in one step and rolls back by itself if the site does not answer. This page describes the workflows that deploy 4wp.dev.

Two workflows, two environments

StagingProduction
TriggerPush to the staging branchManual run, only from main
Database backupNoYes, before every release
Smoke test and rollbackYesYes
SecretsGitHub Environment stagingGitHub Environment production

The pipeline

flowchart TB
    Q["Quality gate<br/>bin/checks.sh"] --> B["Composer install<br/>--no-dev --optimize-autoloader"]
    B --> S["Check secrets"]
    S --> BK["Backup database<br/>production only"]
    BK --> U["Upload release<br/>releases/timestamp"]
    U --> L["Link shared .env and uploads<br/>switch current symlink"]
    L --> W["Post-deploy WP-CLI<br/>update-db, rewrite flush"]
    W --> T{"Smoke test"}
    T -->|ok| K["Keep last 5 releases"]
    T -->|fail| R["Roll back to previous release"]
  1. Quality gate. The same bin/checks.sh as the pre-commit hook: forbidden files, secrets, PHP syntax, composer validate. A local hook can be skipped; this step cannot.
  2. Build. composer install --no-dev --optimize-autoloader; staging caches Composer between runs.
  3. Backup. Production exports the database with WP-CLI into shared/backups before the release.
  4. Atomic release. The code goes into a new releases/<timestamp> folder. .env and uploads are linked from shared/, and the current symlink switches in one step: visitors never see a half-uploaded site.
  5. Post-deploy. WP-CLI checks the install, updates the database and flushes rewrite rules. Staging also resets OPcache after the symlink switch.
  6. Smoke test. WP-CLI confirms WordPress is installed and answers; production can also check the home page over HTTP.
  7. Rollback. If the smoke test fails, current points back to the previous release.
  8. Cleanup. The last 5 releases are kept.

Secrets

Secrets live in GitHub Environments, separately for staging and production: host, user, path, SSH key, known hosts and Composer credentials. The .env files stay on the servers in shared/ and never enter Git.

What is next

Tests are the next stage of our pipeline: PHPUnit for our plugins and Playwright for end-to-end checks.

FAQ

Why is production manual?
A person decides when production changes. Everything else is automated.

What makes a deploy atomic?
The new release is complete before the current symlink switches to it, in one operation.

What if the database update breaks something?
Code rolls back automatically; the database backup taken before the release is the way back for data.

Related