Managing Your WordPress Site with Git and Composer Part 2 – Using Composer to Manage Themes and Plugins


In part 1 we looked at how to store and manage your WordPress site in Git, however we deliberately didn’t include anything in the wp-content folder in our Git repo. This means that you would need to manually migrate any themes and plugins that you have installed on your site.

One way to get around manually having to migrate your themes and plugins using our method is to use Composer (a package manager for PHP), so in this post we’ll be looking at how you can use it to manage the themes and plugins we didn’t store in our Git repository last time.

What is Composer?

Composer is the defacto package manager for PHP. Package managers have become very popular in recent years in the web development scene as they allow developers to package and distribute their work in an easy and free manner. If you’ve ever worked with Node.js, npm is the equivalent package manager for Node.js. Composer allows you to install and update packages from the command line, using a single composer.json file to specify which packages to manage.

We are going to use Composer combined with a special WordPress-specific package repository called WordPress Packagist to install our themes and plugins.

Install Composer

Let’s get started by installing Composer:

curl -sS | php
mv composer.phar /usr/local/bin/composer

You should now be able to run composer from the command line. If for whatever reason something didn’t work, check out the Composer installation instructions for help.


The WordPress Packagist site helps us out here by mirroring the WordPress plugin and theme directories as a Composer repository. We can specify which plugins and themes we want to install in our composer.json file. So lets get started by creating our composer.json file in the root of our project:

touch composer.json
nano composer.json

Next let’s define our project and specify which plugins and themes we want to install. Add the following to the composer.json:

    "repositories": [
            "type": "composer",
            "url": ""
    "require": {
        "wpackagist-plugin/akismet": "3.1.1",
        "wpackagist-theme/evolve": "*"

What’s going on here? First we add as a custom composer repository. We need to do this so composer can find the WordPress Packagist plugins and themes. Then in the “require” section we define the plugins and themes we want to install. In this case we’re installing the Akismet plugin and the Evolve theme. You can install a specific version (e.g. "3.1.1") or just let composer install the latest version (e.g. "*").

Save and exit composer.json. Now lets actually install our plugins and themes by running

composer install

Hopefully you should see something like this:


Great, now you have a good way to manage themes and plugins without having to store them in Git. Remember that you should store your composer.json file in Git.

Deployments and Updates

It’s worth noting at this point if you deploy your Git repository you will need to run composer install (the first time you deploy) or composer update (every subsequent time you deploy) on every server that you deploy your site to. Deployment strategies are outside the scope of this article, suffice to say you will need to be able to SSH to your server.

Once your site is deployed there are two ways you can keep your themes and plugins up-to-date. You can continue to use the WordPress admin as you would normally to update any themes and plugins you have installed (you don’t have to worry about breaking your install or being “out-of-sync” with your composer.json), or you can manually login (SSH) to your server and run composer update. Both methods will achieve the same outcome and you can mix and match without concern.

Another option you have is to do what I have done on my own personal site recently and created a must-use plugin to automatically run composer update twice a day on my server. See the source code and feel free to use it on your own sites. Note that to make the script work you will need to add the following to your wp-config.php to let the script know where composer is installed:

define( 'COMPOSER_PATH', ‘/path/to/composer' );

Warning: If you are using this method to update plugins there is a possibility that breaking changes might be introduced if a plugin is updated. To get around this you can either be very specific about the versions of plugins you are installing, or just make sure you check your site regularly for breaking changes. Also this plugin uses PHP Namespaces which means your server will need to be running PHP 5.3+.

Installing Premium Themes and Plugins

If the theme/plugin author supports it, you can install certain premium WordPress themes and plugins using Composer too. For example we allow users to install WP Migrate DB Pro via Composer. To do this you need to add extra repositories to the composer.json file for each theme or plugin you want to install, and you need to add some extra information so Composer knows where to install the themes and plugins.

"require": {
    "deliciousbrains/wp-migrate-db-pro": "*"
"repositories": [
        "type": "package",
        "package": {
            "name": "deliciousbrains/wp-migrate-db-pro",
            "type": "wordpress-plugin",
            "version": "1.5",
            "dist": {
                "type": "zip",
                "url": "<LICENSE_KEY>&"
            "require": {
                "composer/installers": "v1.0.7"
"extra": {
    "installer-paths": {
        "wp-content/plugins/{$name}/": ["type:wordpress-plugin"],
        "wp-content/themes/{$name}/": ["type:wordpress-theme"]

Custom Themes & Plugins

“What if I have a custom theme or plugin?” I hear you ask? Well that situation might be simpler than you think. You can simply store your custom theme or plugin in your Git repository like any other files. Using composer to install official themes and plugins will have no effect on your custom themes and plugins and both can live together in complete harmony (even though one is stored in Git and the other isn’t). I know earlier we talked about not storing anything in the wp-content folder, however this is one situation where it makes sense to break that rule.

One brilliant example of when to do this is when you need to customise a theme you have installed using Composer. Let’s say you have installed the Evolve theme using Composer and the theme now resides in:


Instead of customising the theme code (and losing your changes when you run composer update) you can create a child theme and store the child theme in your Git repo. This would give you a folder structure like:

wp-content/themes/evolve       # installed via composer
wp-content/themes/evolve-child # stored in git

As we talked about in part 1, to store the “evolve-child” theme folder in Git you will need to modify your .gitignore file so that it doesn’t ignore this folder and its files but does continue to ignore everything else in wp-content. In this case the lines you would need to add to .gitignore would look like this:

# Don’t ignore themes dir, but ignore everything inside

# Don’t ignore evolve-child theme

Now you can run composer update as much as you like and not lose and changes you make, as all of the customised code is stored in Git.

That’s it for part 2. In the next post we’ll be looking at how to use Git Submodules (as an alternative to Composer) to manage your plugins and themes for your Git stored WordPress site.

About the Author

Gilbert Pellegrom

Gilbert loves to build software. From jQuery scripts to WordPress plugins to full blown SaaS apps, Gilbert has been creating elegant software his whole career. Probably most famous for creating the Nivo Slider.

  • andrea duquette

    I’ve been obsessed with creating a solid WordPress development/deploy stack for months/years/decades. And finally, this week, I was able to install an entire WordPress site (configured the way I want it) with a single Composer.json file, and then loaded in my base theme with a single Bitbucket Git command. And it actually worked.

    To accomplish this incredible feat I used heavily modified versions of Bedrock and Sage from the guys.

    So happy you are keeping your WP Migrate pro plugins in the WP Packagist repository. But I noticed in the article that you also host the package via Delicious Brains. Can you let me know what the benefits are?

    Thanks for the perfectly timed article and an awesome plugin that makes my life sooo much simpler .

    • Thanks Andrea. So the free WP Migrate DB plugin is on WP Packagist as its a free plugin hosted on the plugin repository (which WP Packagist mirros). The premium WP Migrate DB Pro plugin is not on WP Packagist (we host it on but we still allow you to install it via composer as I demonstrated above.

  • Great writeup. Thanks for sharing, Gilbert. One of the biggest pain points my team currently has is managing plugin updates across all of our hosted and client sites. We use ManageWP or the likes however, things then get out of sync on local or other environments. When using git to store the plugins, we then can’t use ManageWP as a push from local will revert anything done.

    This seems like a good step forward and I look forward to playing with setting up Composer to manage plugins and themes, at least locally, but the problem of keeping plugins and themes in sync from local to staging to production still seems to be at large.

    Any thoughts or suggestions on how to mitigate this? Would love to see another extension from the Delicious Brains team that does the same thing it does for Media but for plugins and themes.

    • Thanks Justin. Migrating plugins and theme is indeed a pain point and you’re not the first person to ask us for a solution. Composer is a good first step though because as long as you run “composer update” say, at least once a day, on all of your environments most things should remain up-to-date and in-sync. Not ideal but better than storing them in Git.

  • Regarding the ‘composer install’ verses ‘composer update’ scenario. ‘composer install’ installs everything up to the versions specified in composer.json. A composer.lock file is created which contains details of what was installed. Running ‘composer install’ from then on will bring things up to date with what is in the composer.lock file. If there are new versions to be updated then you run ‘composer update’, which, again, updates the composer.lock file.

    With this in mind, on the server I only ever run ‘composer install’ to bring it up to date with what is in composer.lock and therefore what I’ve tested locally. I deploy via git and then run the install command in the git post-update hook.

    Of course nothing gets updated automatically as per your way of doing an update on the server twice a day. I may explore your way in the future though I’d suggest locking versions down to point releases so as to not introduce a breaking change. (e.g. 1.* ). An article on version constraints here is good

    And finally, I’ve created a Composerised WP starter pack. You’ll end up with WordPress installed in public/wp and your working files in public/app along with Advanced Custom Fields Pro and Custom Post Type Class installed. It also selects a config file based on environment variables.

    It’s a new area for WordPress so I love reading other people’s take on it all. Thanks for the article.

  • Sheena Douglas

    Hello, great article of setting up git. Just have one question. where does the PHP file go to set up the cron job and what should it be named? Did I miss that somewhere?


  • Jull Weber

    Thanks so much for the script!!

  • Hi Gilbert,

    Thanks again for this tutorial.

    I had an issue with the .gitignore file when trying to replicate this repo. Using your example I couldn’t get my theme to be included in the repo, whilst ignoring everything else inside wp-content/. I think (but I’m still a newbie at GIT so maybe I’m wrong) you may be missing something from your file, or it doesn’t work the same on my system so hopefully this helps someone else:


    # don’t ignore the wp-content dir, but ignore everything inside

    # don’t ignore the themes dir, but ignore everything inside

    # Don’t ignore my preface starter theme

    If I didn’t have that extra wp-content check, I couldn’t get the repo to track the wpgit folder.

    Anyways I hope this helps in case anyone else has issues like I did.

  • Fernando Miguel

    Hi there Gilbert! Great article! Thank you so much.

    For security reasons and to avoid the message “Your configuration does not allow connections to…”, I suggest you to change the wpackagist url to “” in composer.json. My best regards!

  • Peter

    Great article! thank you.

    What about plugins like woocommerce which need to alter database (adding tables)?

  • Why, what about paid plugins?

    • Marcelo

      I use to create my own “versioning” of paid plugins, using Bitbucket or another private git account.
      For example:
      1. Create a project in Bitbucket for “Revolution Slider” plugin.
      2. Add a composer.json:
      “name”: “YOURPROJECT/revslider”,
      “description”: “Revolution Slider Plugin”,
      “type”: “wordpress-plugin”,
      “dist”: {
      “url”: “”,
      “type”: “git”
      “license”: “GPL”,
      “authors”: [
      “name”: “YOURPROJECT”,
      “email”: “”
      “require”: {}
      3. Create a TAG for you current revision of plugin, for example “3.2.1”
      4. In composer.json of your WP Project, add the following lines under “respositories” array:
      “type”: “vcs”,
      “url”: “”
      5. And under “require” object add:
      “YOURPROJECT/revslider”: “3.2.1”
      6. Run “composer install” and be happy.

      PS: You can do the same for paid themes.

    • Marcelo

      I use to create my own “versioning” of paid plugins, using Bitbucket or another private git account.
      For example:
      1. Create a project in Bitbucket for “Revolution Slider” plugin.
      2. Add a composer.json:

      "name": "YOURPROJECT/revslider",
      "description": "Revolution Slider Plugin",
      "type": "wordpress-plugin",
      "dist": {
      "url": "",
      "type": "git"
      "license": "GPL",
      "authors": [
      "name": "YOURPROJECT",
      "email": ""
      "require": {}

      3. Create a TAG for you current revision of plugin, for example “3.2.1”
      4. In composer.json of your WP Project, add the following lines under “respositories” array:

      "type": "vcs",
      "url": ""

      5. And under “require” object add:

      "YOURPROJECT/revslider": "3.2.1"

      6. Run “composer install” and be happy.

      PS: You can do the same for paid themes.

  • Mike McLin

    Thanks for the article. I do have a couple of issues…

    If you are updating via the WordPress Admin area, you are definitely “out of sync” with composer. Your composer.lock file is pointing to exact versions of packages, and when updates happen outside of composer, the lock file is essentially invalid.

    I would argue that unless you are using composer just for initial setup, you should never use the UI to update anything. It is sort of all or nothing, if you want to use Composer to maintain your project (which you certainly should if you are running it locally too). A typical WordPress plugin update should use the exact same process as a modern PHP application. You update the composer dependencies locally. Then if everything looks good, you commit your composer.lock file and push your repo. This will probably trigger a hook that deploys to your server (perhaps a staging server first, if you’re into that sort of thing). Your deployment process should be pulling down your git repo and the running composer install. Now your server will have the exact same files as your local dev environment.

  • Максим «maxwellf» Фролов

    Child theme is not “custom”. What about my really custom theme in separate git repo? What about versioning my theme?

  • ailien

    Excellent! Many thanks from

  • Alejandro Paciotti


    I want to install the plugin at

    I have successfully installed composer according to instructions in:

    My wordpress site is installed on:

    And I have redirected the entries to that folder:

    DocumentRoot /var/www /html/wordpress

    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Order allow, deny
    Allow from all

    ErrorLog $ {APACHE_LOG_DIR} /error.log
    CustomLog $ {APACHE_LOG_DIR} /access.log combined

    I followed the following steps:

    cd /var/www/html/wordpress
    composer require mohiohio/graphql-wp
    And everything seems to be installed correctly:

    Using version ^ 0.1.3 for mohiohio / graphql-wp
    ./composer.json has been updated
    Loading composer repositories with package information
    Updating dependencies (including require-dev)
    Package operations: 4 installs, 0 updates, 0 removals
    – Installing webonyx / graphql-php (v0.6.4): Loading from cache
    – Installing mohiohio / wordpress-lib (0.1.4): Loading from cache
    – Installing ivome / graphql-relay-php (v0.1.3): Loading from cache
    – Installing mohiohio / graphql-wp (0.1.3): Loading from cache
    Writing lock file
    Generating autoload files
    And then I also modified the index.php by adding this line:

    require (dirname (__ FILE__). ‘/vendor/autoload.php’);
    But when I go to my site:

    I get a 404 error.