When a Deployment Broke the Live Store: Jordan's Week of Late Nights

Jordan was a one-person dev team for a regional retailer. A new feature, a lightweight product filter, was merged late Friday after a flurry of Slack messages. The deploy went through a plugin editor change and a direct FTP push. Monday morning the store was returning 500s for search pages. The cache layer was inconsistent, media uploads were missing on some pages, and the client was seeing mixed production and staging templates. Jordan rolled back by hand, patched files on the server, and lost a day of customer support time. Meanwhile the client’s trust dropped and the next sprint took a hit.

This story is familiar. Git promises order, traceability, and repeatability. But when git integration for WordPress hosting is done poorly, it can make things worse. As it turned out, the real issue was not git itself but the deployment model: mixing manual edits, storing generated assets in the repo, and ignoring database migrations. This led to chaotic deploys and slow recovery.

The Hidden Cost of Poor Git Deployment in WordPress Hosting

Most teams think of git as a code safety net: commit, push, deploy. The hidden costs show up over time:

    Unexpected production changes. Editing files directly on the server breaks the single source of truth. Large, slow deployments. Committing vendor or media files bloats the repository and slows clones and pipelines. Migration mismatch. Code and database drift when migrations are not versioned or reversible. Recovery pain. Without atomic releases and proper rollback, reverting a bad deploy becomes a manual, error-prone process. Developer friction. Inconsistent local environments and unclear workflows lead to wasted time and bugs.

Those are real costs in hours and reputation. If your hosting integration treats git as an afterthought - a place to push zipped themes - you're building fragility into production. You need a deployment architecture that handles code, assets, database changes, and secrets in ways that match how WordPress works.

Why Simple FTP or Plugin-Based Deployments Fail for Teams

Simple solutions scale well for one-off sites but fall apart for teams and critical apps. Here’s why common "easy" approaches fail:

FTP pushes and plugin editors

    They break the single source of truth. Server becomes the master copy. No CI checks. Linting, tests, or build steps don’t run before files hit prod. Risky for hotfixes. Manual edits are hard to track and reproduce locally.

Deploy plugins that push from git to server

    Good for small teams, but they often perform a direct git pull into a writable docroot. That leaves partial updates during the pull. They frequently don’t handle composer installs, npm builds, or asset compilation reliably.

Committing everything (uploads, vendor, core)

    Repository size balloons. Clones and CI pipelines slow down. Binary blobs and media don’t diff well and clutter history.

These approaches work for personal blogs or prototypes. For anything that matters - transactional sites, multi-developer teams, or enterprise installs - they cause predictable outages and creeping technical debt.

How One Hosting Team Built Reliable Git Deployments for WordPress

At a small managed host I worked with, we rebuilt our deployment model around three principles: immutable releases, clear separation of concerns, and automated migrations. The setup combined modern git workflows with WordPress realities. Here’s what we did and why.

Principle 1 - Immutable releases and symlink swapping

We stopped pulling directly into the live directory. Instead the pipeline built an artifact - a tarball - after running tests and builds. On the server we maintained a releases directory with timestamped folders and a current symlink pointing to the active release. New deploys unpack to a new release folder and then atomically switch the symlink. Rollback is a single symlink change.

    Zero-downtime swap: the web server follows the symlink quickly, avoiding partial updates. Reproducible releases: each release includes the exact built assets and vendor files generated by CI, not by developers' local machines.

Principle 2 - Keep media and dynamic content out of git

We moved uploads to object storage and used CDN offloading. That solved the common issue of large, changing files in the repo. Tools we used included S3-compatible storage with the official offload plugin and rclone for one-off syncs.

    Uploads are the canonical source in object storage, not in git. For local dev, we provide a minimal sync or sample set to avoid giant repositories.

Principle 3 - CI builds and deploy hooks

We used GitHub Actions to run unit tests, static analysis, composer install, npm run build, and pack an artifact. The CI then pushed the artifact to the host via SSH or an artifact store. On the host, deploy hooks ran a sequence:

Unpack artifact into new release folder. Run wp-cli search-replace if domains changed or environment variables need updates. Run database migration scripts stored in the repo with a schema version table to track applied migrations. Clear caches and warm important pages. Swap symlink to new release.

This ensured builds were consistent and deployments were predictable.

How migrations were handled

WordPress doesn’t have a top choices for secure WordPress hosting built-in migration system like Rails. We adopted a simple versioned migration approach: SQL or PHP scripts in a /migrations folder with an ordered filename and a record in a wp_migrations table. Each migration is designed to be idempotent or reversible where possible. If a migration was not safe to reverse, we treated it as a deploy-incompatible change and deployed a feature flag first.

Secrets and configuration

Environment-specific values lived outside the repository in a secrets store. We used environment variables injected by the host or a small .env file not tracked in git. Composer and WP-CLI used these variables during build and deploy steps.

image

Protecting production

    Branch protection and required CI on main branch ensured direct pushes couldn’t bypass tests. Signed commits and deploy keys restricted who could trigger deploys. Staging was identical to production as much as possible - same PHP versions, same database engine, same caching layer.

From Wasted Hours to Predictable Releases: What Proper Git Integration Delivered

After switching to this model, our incidence of production outages from deployments dropped dramatically. Here are the concrete wins:

    Faster recovery. Rollback via symlink returned a site to a known-good state in under two minutes. Smaller, faster pipelines. Keeping media out of git made clones and builds quicker. Fewer surprises. CI ran linting and tests before any deploy, catching common regressions. Better audit trails. Every deploy had a linked commit and artifact, making postmortems more precise.

But the gains weren't only technical. The team’s confidence increased. Product owners felt comfortable scheduling releases because the deploy process was reliable and visible.

image

Sample advanced deployment recipe

Use a "wp-content only" repository when multiple sites share a core - or adopt Bedrock if you want full composer control over core and plugins. In CI: run composer install --no-dev, run npm ci and npm run build, run phpunit, run static analysis (PHPStan), then create artifact tar.gz. Upload artifact to server or artifact store. Use SSH with deploy keys or a CI runner with credentials. On server: unpack to /var/www/releases/20260116T1030, run /wp-cli migrations, run wp cache flush, run health checks, swap symlink to /var/www/current. Post-deploy: warm the CDN with critical pages, run smoke tests, and notify Slack with the deploy link and commit diff.

Contrarian Views You Need to Consider

Some developers insist on different paths. Consider these contrarian takes and why they might be right or wrong for you.

"Store everything in git - including uploads and core"

Proponents argue that having a full snapshot in one place simplifies restores. That is true for simple, low-traffic sites. In reality, storing binaries and generated files in git causes performance headaches and poor diffability. For mission-critical sites, object storage plus backups is a cleaner restore story than giant git history.

"Direct git pulls to document root are fine if you're careful"

They can be fine for small shops with everything under one developer's control. The problem appears when multiple people, parallel deploys, or build steps exist. Partial updates and permission issues make direct pulls risky. Atomic releases are more work to set up, but they save time when things go wrong.

"Use a managed host's built-in git deploy and trust their magic"

Managed hosts often provide git hooks and fast rollbacks. They may also limit filesystem access or make assumptions about plugin behavior. If your site depends on specific build steps or on external storage, you may fight the platform. Treat managed-host git features as convenient but understand their limits.

Practical Checklist for Adopting Git Integration on Your WordPress Host

    Decide repo scope: full stack (Bedrock) or wp-content only. Prefer Bedrock for complex apps. Remove binary and upload folders from git, use object storage with CDN. Implement CI to run builds and tests, and to produce deterministic artifacts. Deploy artifacts to releases folders and use symlink swaps for atomicity. Version and manage database migrations in the repo with an applied-migrations table. Store secrets outside git and inject them at deploy time. Enforce branch protection, PR reviews, and automated checks before merging to main. Set up rollback procedures and automate smoke tests post-deploy.

Edge cases and advanced concerns

Multisite: Pay attention to serialized data and domain mapping. Migration scripts must handle PHP serialized strings safely, using wp-cli search-replace with serialization support.

Plugin updates: Treat plugin and theme updates like code changes. Prefer deploying plugin updates through CI-managed releases rather than via the admin UI on production.

Local dev parity: Use containerized local environments (DDEV, Docker, Local) that mirror staging and production PHP and MySQL versions to reduce the "works on my machine" problem.

Monorepo vs multiple repos: A monorepo can centralize shared components but increases CI complexity. Multiple repos keep deployments simple but complicate cross-site changes. Choose based on team size and release cadence.

Final Takeaways - Cut the Noise, Ship with Confidence

Git integration for WordPress hosting is more than a shallow "push to deploy" feature. It requires an approach that respects WordPress' dynamic nature - media, plugins, and database changes - while bringing discipline to how code moves from developer machines to production. The right setup reduces surprises, speeds recovery, and frees your team to focus on features instead of firefighting.

If you're starting from scratch: pick a consistent repo strategy, automate builds, separate uploads from code, and implement atomic releases. If you're already live and brittle: start small by moving uploads out of git and adding a staging environment that mirrors production. This led to faster improvements in our teams than a total rewrite.

Be practical. If your site is a static brochure with no team, don't over-engineer a deployment pipeline that will never be used. For anything that handles money, traffic, or multiple contributors, invest in a secure, automated, and reversible deployment model. Your future self - and your clients - will thank you.