Technology

Deploying WordPress and PHP Sites with Git: CI/CD on Shared Hosting and VPS

Deploying WordPress or custom PHP code with FTP feels fine until the first time you overwrite the wrong file, forget which version is live, or need to roll back a broken release in minutes. Git and a simple CI/CD (Continuous Integration / Continuous Deployment) workflow solve exactly these problems. Even if you are on classic shared hosting with cPanel, you can still use Git to version your code and automate deployments. On a VPS, you can go further and ship zero‑downtime releases with build steps, database migrations and health checks.

In this guide, we will walk through practical Git deployment patterns that we use and recommend for WordPress and general PHP applications on both shared hosting and VPS. We will start with how to structure your repository for WordPress, then look at Git workflows for cPanel, and finally move to more advanced CI/CD pipelines on a VPS. The goal is a workflow where you commit, push, and let the server take care of the rest – while keeping releases repeatable, auditable and easy to roll back.

Why Git and CI/CD Matter for WordPress and PHP Projects

Problems with classic FTP deployments

FTP and manual file uploads are still very common for WordPress and PHP sites, but they come with serious drawbacks:

  • No history: You cannot see what changed, when, or by whom. Debugging production issues becomes guesswork.
  • Risk of broken uploads: If a file upload fails mid‑transfer, your site may run with half‑updated code.
  • Hard to roll back: Restoring a previous version usually means uploading a backup ZIP or relying on hosting snapshots.
  • Team conflicts: Designers, developers and freelancers can easily overwrite each other’s changes.

What Git and CI/CD give you instead

By moving your WordPress or PHP code into a Git repository and wiring it to a deployment workflow, you gain:

  • Version history: Every change is tracked. You can see diffs, revert specific commits and understand what went into each deploy.
  • Atomic releases: Code is deployed as a consistent snapshot, not file‑by‑file uploads.
  • Safer collaboration: Branches and pull requests let multiple people work in parallel and review changes before production.
  • Reproducible environments: Build steps like Composer, npm or asset bundling run the same way on each deploy.
  • Zero‑downtime possibilities: On a VPS you can deploy to a new release directory and switch a symlink in a fraction of a second.

We have covered the big‑picture side of this in our article Git deployment workflows on cPanel, Plesk and VPS. In this post, we will go deeper into WordPress and PHP specifics, especially around repository layout and real‑world CI/CD flows.

Designing a Git Repository for WordPress and PHP

What to track in Git for a WordPress site

WordPress is not just code; it is also a CMS with content and uploads. You should never try to put everything (including the entire uploads folder and database contents) into Git. Instead, split your concerns:

  • Put in Git:
    • Custom theme(s)
    • Custom plugins
    • Must‑use plugins, mu‑plugins loader
    • Configuration scaffolding (example wp-config files, environment loader)
    • Composer.json, package.json and build scripts if you use them
    • Deployment scripts and documentation
  • Keep outside Git:
    • wp-content/uploads (user uploads, images, PDFs)
    • wp-config.php with actual credentials and salts
    • Database content (posts, pages, WooCommerce orders, etc.)

For many teams, a good starting point is to track the entire wp-content directory except uploads, plus a small bootstrap wp-config that loads environment‑specific keys from a separate file that is not committed.

Example .gitignore for WordPress

Here is a simplified .gitignore pattern that works well on both shared hosting and VPS:

# WordPress core (if you deploy via installer or auto-updates)
/wp-admin/
/wp-includes/
/wp-*.php

# Never commit uploads
/wp-content/uploads/

# Cache and backups
wp-content/cache/
wp-content/backups/

# Environment-specific config
wp-config.php
*.local.php

# Build artifacts
node_modules/
vendor/
.yarn/

You can adjust this based on your strategy. Some projects use Composer to install WordPress core itself; in that case, you will track less of the tree and rely more on composer install during deployment.

Repository layout for generic PHP apps

For a non‑WordPress PHP app (Laravel, Symfony, in‑house frameworks) the idea is simpler: put the whole application codebase into Git and exclude:

  • Generated vendor directory (if you plan to run Composer during CI/CD)
  • Runtime cache and log directories
  • Environment file with secrets (for example .env)
  • Upload storage directories with user data

If you are planning a bigger Laravel or custom PHP deployment on a VPS, you may also find our guide on deploying Laravel on a VPS with zero‑downtime releases useful. The same release structure works perfectly for WordPress and other PHP apps.

Git Deployment on Shared Hosting (cPanel)

Many people assume CI/CD is only for VPS or containers, but modern shared hosting can work surprisingly well with Git, especially when a control panel like cPanel is available. Let us look at two practical approaches.

Option 1: Use cPanel Git Version Control (pull‑based)

If your cPanel plan includes the Git Version Control feature, you can create a repository directly from the panel and let it pull from your remote Git hosting (for example GitHub or a private Git server):

  1. In cPanel, open the Git Version Control tool.
  2. Create a new repository and choose a document root (for example public_html or a subfolder like public_html/site).
  3. Set your remote Git URL and clone.
  4. Whenever you push new commits, use the cPanel interface (or a small hook) to pull updates to the live site.

This is a pull‑based deployment: the server pulls code from your Git remote. It is simple and works even if you cannot open SSH from CI/CD. The trade‑off is that build steps (Composer, npm, asset bundling) must either run on the shared hosting account, or you commit built assets into the repo.

Option 2: Bare repo and post‑receive hook (push‑based)

If you have SSH access to your shared hosting account, you can use a more automated Git flow:

  1. Create a bare Git repository in your home directory, for example ~/repos/site.git.
  2. Create a working directory (usually your document root), for example ~/public_html.
  3. Add a post‑receive hook in the bare repo that checks out the latest commit into the working directory after each push.
  4. Add this bare repo as a remote from your local machine and run git push hosting main to deploy.

A minimal post‑receive script might look like this:

#!/bin/bash
GIT_WORK_TREE=$HOME/public_html git checkout -f main

Make the hook executable (chmod +x hooks/post-receive). Now every push updates the files under public_html. On shared hosting, keep the script simple: avoid long‑running build steps that could hit limits. Instead, commit built CSS/JS artifacts, or run a very targeted composer install if necessary.

WordPress specifics on shared hosting

When deploying WordPress with Git on shared hosting, a few additional rules help keep the site stable:

  • Do not overwrite uploads: Ensure your .gitignore excludes wp-content/uploads, and your deployment script never deletes that directory.
  • Be careful with auto‑updates: If WordPress core or plugins auto‑update via the admin, they will drift from what is in Git. Prefer updating via Git commits or at least document how you handle plugin updates.
  • Staging environments: Create a staging subdomain (for example staging.example.com) under a separate document root, use another Git clone and a copy of the database. Our guide on WordPress staging on shared hosting shows this layout step by step.
  • Database changes: Code deployments are easy to track; database changes are not. Avoid direct SQL schema changes via phpMyAdmin without documenting them in your repo.

Safe updates with minimal downtime

On shared hosting, you rarely control low‑level web server configuration, so classic release‑directory + symlink tricks may not be available. You can still reduce the risk of broken deployments by:

  • Deploying to a temporary directory and then doing a quick move (for example rsync to public_html after a successful build).
  • Enabling a short maintenance mode during deployment using a maintenance.php or a simple .htaccess rule.
  • Taking a quick backup of your site files and database before big releases. Our article on WordPress backup strategies on shared hosting and VPS explains how to automate this safely.

From Git to Full CI/CD on a VPS

On a VPS you have full control: you can create system users, install Git, configure Nginx or Apache and design a robust release structure. This makes it much easier to use a true CI/CD pipeline with build steps and zero‑downtime deploys.

Recommended directory and user layout

We typically recommend:

  • Create a dedicated Unix user (for example deploy or siteuser) that owns the application files.
  • Use a base directory like /var/www/example.com with subfolders:
    • repo/ – a bare Git repository receiving pushes
    • releases/ – timestamped release directories
    • shared/ – files that persist across releases (uploads, .env, cache, logs)
    • current – a symlink to the latest good release
  • Point your web server (Nginx or Apache) to /var/www/example.com/current/public or similar, depending on your app.

Post‑receive hook for zero‑downtime releases

In the bare repo under repo.git/hooks/post-receive you can put a script that:

  1. Creates a new release directory with a timestamp.
  2. Checks out the new code into that directory.
  3. Runs build steps (Composer, npm, asset builds) if needed.
  4. Symlinks shared directories (uploads, .env, storage, logs).
  5. Performs final health checks.
  6. Updates the current symlink to point to the new release.

This pattern is very similar to what we describe in our guide on zero‑downtime CI/CD to a VPS with rsync and symlinked releases. For WordPress, steps 2 and 4 are the most critical: do not touch uploads, and keep wp-config or environment files under shared/.

Connecting Git hosting to the VPS

Rather than pushing directly from developer machines, we generally recommend using a central Git hosting service and letting CI/CD push to the VPS. For example:

  • Developers push to a main or release branch.
  • A CI workflow on Git hosting (for example Actions, Pipelines, or another system) runs tests and build steps.
  • If the pipeline passes, it connects to the VPS via SSH (using a deploy key) and either pushes to the bare repo or rsyncs artifacts into the next release directory.

On the VPS side, SSH access should be locked down: a dedicated deploy key limited to that user, no root login, and a firewall in place. Our checklist for securing a VPS server covers these basics in more depth.

CI pipeline structure for PHP and WordPress

A typical CI/CD pipeline for a WordPress or PHP project might have stages like:

  • Prepare: Check out code, set up PHP version, cache Composer and npm directories.
  • Install: composer install –no-dev –optimize-autoloader and npm install where relevant.
  • Build: npm run build or similar asset build commands.
  • Test: Run automated tests (PHPUnit, integration tests, linting).
  • Package: Create an artifact containing only what is needed on the server (public files, vendor, built assets).
  • Deploy: Upload the artifact to the VPS (rsync or scp), unpack into a new release directory, update symlink, run post‑deploy commands.

If you prefer, you can also skip packaging and instead let the VPS do composer install directly in each new release directory, as long as the server has sufficient resources. For a detailed walkthrough of this pattern, see our tutorial on zero‑downtime deployments to a VPS with GitHub Actions for PHP.

WordPress‑Specific CI/CD Patterns

Handling wp-config and secrets safely

One of the main concerns with WordPress deployments is how to manage wp-config.php and secrets (database password, salts, keys):

  • Do not commit real secrets: Keep concrete credentials out of the repo. Instead, commit a sample file like wp-config.example.php.
  • Use environment‑based includes: Let wp-config load an extra file from outside the repo or read from environment variables.
  • Store environment files in shared/: On a VPS, store sensitive config in /var/www/example.com/shared/ and symlink or include it from each release.

We also recommend reading our guide on managing .env files and secrets on a VPS safely if your PHP application is not WordPress but uses environment files.

Managing plugins and themes

There are two main strategies:

  • Git‑managed plugins and themes: All custom and carefully selected third‑party plugins and themes are committed into the repo. Updates happen via Git commits.
  • Hybrid approach: Only your custom theme and custom plugins live in Git; common marketplace plugins are installed and updated via the WordPress admin. This is easier but less deterministic.

For business‑critical sites, we lean toward Git‑managed plugins and themes plus a documented update process. That way, your CI pipeline always knows exactly which versions are in production, and rollbacks are straightforward.

Database and content changes

WordPress mixes code and content; many changes happen in the database (menus, widget settings, page builder layouts). To keep your CI/CD pipeline predictable:

  • Use staging for experiments and content previews, then move content to production with export/import or selective tools rather than copying the whole database over live data.
  • Be careful with schema‑changing plugins or direct SQL modifications. Document any such changes in the repo (for example in a migrations folder with SQL files and README notes).
  • For WooCommerce and other transaction‑heavy sites, avoid restoring old database snapshots except as a last resort, since that may lose orders or customer accounts.

When you plan larger structural changes, combine your Git deployment workflow with a maintenance window and a clear rollback strategy, as we discuss in our article on maintenance windows and downtime pages.

Operational Tips: Cron, Backups and Monitoring Around CI/CD

Replace wp-cron with real cron jobs

On both shared hosting and VPS, using real cron instead of wp-cron.php on each page load will make scheduled tasks more reliable and reduce page latency. In a Git‑managed setup:

  • Disable default wp-cron in wp-config.
  • Add a system cron job (or cPanel cron) to call wp-cron.php at defined intervals.
  • Track the cron configuration in documentation or infrastructure code so new servers get the same schedule.

For details, see our practical guide on replacing wp-cron with real cron on WordPress. The same logic applies to any PHP application that needs scheduled jobs.

Backups integrated with your deployment flow

Even with perfect CI/CD, mistakes happen. A solid backup routine is non‑negotiable:

  • Automate file and database backups on a schedule independent of deploys.
  • Optionally trigger an extra on‑demand backup before major releases.
  • Keep at least one backup copy off‑site (for example on object storage) and test restores regularly.

We have an in‑depth article on designing a backup strategy with realistic RPO/RTO targets that fits well with Git‑based deployments.

Monitoring and alerting after each deploy

Once your CI/CD pipeline is in place, a new class of incidents appears: a broken release that passed tests but fails under real traffic. To catch these fast:

  • Monitor uptime and response codes (for example with Uptime Kuma or another tool).
  • Watch PHP error logs and access logs for spikes in 5xx errors after deployments.
  • Set up resource monitoring (CPU, RAM, disk IO) on VPS to see if a new release suddenly increases load.

Our article on VPS monitoring and alerts with Prometheus, Grafana and Uptime Kuma goes through a starter setup that plays nicely with CI/CD.

Putting It All Together: A Realistic Workflow

Let us combine everything into a concrete, repeatable workflow you can adapt to your own WordPress or PHP project.

For shared hosting (cPanel)

  1. Initialize a Git repo locally with a clean .gitignore that excludes uploads and secrets.
  2. Push to a central Git hosting service.
  3. On cPanel, either create a Git Version Control repo that pulls from your remote, or create a bare repo plus post‑receive hook that checks out to public_html.
  4. Configure a staging subdomain with its own Git clone for safe testing.
  5. Disable automatic WordPress updates if you want code to stay in sync with Git; manage updates via commits instead.
  6. Set up real cron for wp-cron and schedule regular backups from the panel.

For VPS hosting

  1. Provision a VPS plan from dchost.com that matches your CPU, RAM and disk needs.
  2. Create a deploy user and /var/www/example.com with repo, releases, shared and current structure.
  3. Initialize a bare Git repo under repo.git and wire a post‑receive hook that builds into a new release and updates current.
  4. Set up your CI/CD pipeline on your Git hosting platform: run tests, build assets, then deploy via SSH to the VPS.
  5. Keep WordPress uploads, .env/config files and logs in shared/, symlinked into each release.
  6. Add monitoring, log aggregation and automated backups to complete the picture.

When you are ready to evolve further, you can build on this base with blue‑green deployments, canary releases or multi‑region setups, many of which we explore in our advanced hosting architecture articles.

Conclusion: A Calm, Repeatable Way to Ship WordPress and PHP

Moving WordPress and PHP deployments from FTP to Git and CI/CD is one of those changes that feels like extra work at first, but quickly becomes the safety net you wish you had years ago. Instead of manually dragging files into public_html, you commit, push and let your shared hosting or VPS execute a script that you have designed, tested and can easily improve over time.

On shared hosting with cPanel, even a simple Git pull or bare‑repo hook deployment already gives you version history and cleaner releases. On a VPS from dchost.com, you can take full control: dedicated users, release directories, Composer and npm builds, real cron, robust backups and monitoring. The result is a calmer release cycle where you are not afraid to update plugins, ship new features or optimize performance, because you know exactly how to roll forward or back.

If you want help matching this workflow to your current environment, our team at dchost.com can recommend the right combination of shared hosting, VPS or dedicated servers and guide you through a practical migration plan. Start by versioning your next WordPress theme or PHP feature in Git, and let the deployment pipeline grow with you, one step at a time.

Frequently Asked Questions

Yes. As long as your shared hosting account supports either SSH or the cPanel Git Version Control interface, you can use Git for deployments. With cPanel Git, the server simply pulls changes from your remote repository into the document root. With SSH, you can create a bare repository and a post‑receive hook that checks out new code into public_html whenever you push. You will not have all the flexibility of a VPS (for example, complex build steps or release directories), but you still gain version history, safer updates and the ability to roll back by checking out older commits.

Uploads and real configuration secrets should stay outside Git. Add wp-content/uploads to your .gitignore so user files are never overwritten by a deploy. For wp-config.php, commit only a template or bootstrap file (for example wp-config.example.php) that loads environment-specific settings from a separate file or environment variables. On a VPS, place that real configuration file under a shared directory outside releases and include it from wp-config. On shared hosting, you can keep wp-config.php in place and design your deploy script so it never touches that file.

Automated tests are strongly recommended but not a strict requirement to start using CI/CD. You can begin with a simple pipeline that just builds assets, runs composer install and deploys to a staging environment. As your project matures, add PHPUnit, integration tests or even basic linters to catch issues earlier. The key value of CI/CD in the early stages is repeatable, scriptable deployments and easy rollbacks. Over time, tests make the pipeline smarter, reducing the chance that a bad commit reaches production.

On a VPS, the most effective technique is to deploy to a fresh release directory and then atomically switch a symlink called current to point to the new release after all build steps succeed. Nginx or Apache is configured to serve from current, so the switchover takes milliseconds. If something goes wrong, you simply repoint the symlink to the previous release. Combining this with short health checks, proper PHP-FPM configuration and persistent directories for uploads and config gives you near zero downtime, even during large updates.

A gentle first step is to download your current WordPress files to your local machine, initialize a Git repository there and commit only the code you want to track (themes, plugins, configuration scaffolding). Configure a .gitignore to exclude uploads and environment files. Then, on your next change, update the files locally, commit, and deploy via FTP once more. After you are comfortable with Git history and branching, introduce a simple deployment script on your shared hosting or VPS so that git push replaces manual FTP uploads. This incremental approach avoids a big-bang migration while still moving you toward a safer workflow.