The Composer logo

Installing Premium WordPress Plugins with Composer

If you’ve Google’d how to install or update premium plugins, like Advanced Custom Fields or Gravity Forms, with Composer, you’ve probably found solutions that want you to configure package data locally. Examples include this tutorial from Roots and this library on GitHub.

The problem with these solutions is that they require defining the plugin version in your composer.json‘s repositories block. If a new release comes along, you have to bump this number manually. Fine for one site, but a massive pain for many. It also means ensuring correct environment variables are set up anywhere you deploy or develop.

A good Composer setup should allow you to define a version constraint for the plugin and simply run composer update to get the latest version compatible with your constraints, just like any other dependency. Most folks using plugins like Advanced Custom Fields or Gravity Forms are likely managing many sites, and doing it any other way is, simply, painful.

We’ve created a GitHub project template that allows you to manage your premium WordPress plugin installs with Composer and never have to worry about managing version numbers.

Note: This tutorial uses Advanced Custom Fields as the example plugin. While these instructions are still a great guide for using the GitHub template linked above, ACF now has a canonical Composer repository for installation. We recommend installing ACF from that repository rather than creating and maintaining your own.

How does it work

The repository template includes a GitHub action that, on a regular schedule, will:

  1. Fetch a plugin zip from a remote URL
  2. Read the plugin version from the zip
  3. Compare that to the latest tag
  4. If the version is the same, quit early
  5. Overwrite the repository’s contents with the zip’s contents
  6. Commit
  7. Tag
  8. Push

The included composer.json then provides other necessary information to turn this repository into a valid Composer VCS repository.

Getting started

The repository is just a starting point. You’ll need to configure a few things to get your plugin working. This makes the repository very flexible.

Start from the provided template

Get started with the “Use This Template” button on the private WordPress plugin Composer provider repository.

Determine the download URL for your plugin

You can download the latest version of most private plugins from a fixed URL, so long as you provide authentication of some kind. For example, Advanced Custom Fields Pro can be downloaded from this URL:

https://connect.advancedcustomfields.com/index.php?a=download&p=pro&k={{your_acf_license}}Code language: JavaScript (javascript)

Set up license keys as GitHub secrets

You shouldn’t be committing any license keys or API keys to your repository. Instead, save any sensitive information needed to authenticate and download your plugin as GitHub Secrets.

Update .github/workflows/update.yml

Next, you need to update the GitHub action configuration in your new repository. You can find this at .github/workflows/update.yml.

Find the “Fetch” step and update the URL with your plugin’s endpoint, substituting in the secret you configured in the previous step for any sensitive information. For example, ACF Pro might end up looking like this:

# Fetch latest version
- name: Fetch
  run: wget 'https://connect.advancedcustomfields.com/index.php?a=download&p=pro&k=${{secrets.ACF_PRO_LICENSE}}' -O package.zipCode language: PHP (php)

Depending on the contents of the zip getting pulled down from the plugin’s author, some moving around may be required. For example, if you download ACF Pro, the zip contains a directory that contains the plugin files. This is a common pattern, and the plugin files need to be moved up to the root directory.

That particular pattern is enabled by default. Simply configure the plugin slug (which should correspond to the nested directory’s name) in the env section of the action to get it to work:

  PACKAGE_SLUG: advanced-custom-fields-pro

If this behavior isn’t desirable, comment out the “Move” step entirely. Tweak according to your own needs.

Update composer.json

Finally, update the included composer.json with an appropriate package name and, optionally, author credits. This is what all those other examples want you to put in each project’s composer.json file, but we’re setting it up at GitHub instead. For example:

    "name": "elliotcondon/advanced-custom-fields-pro",
    "type": "wordpress-plugin",
    "authors": [
            "name": "Elliot Condon",
            "email": "[email protected]"
            "name": "Ethan Clevenger",
            "email": "[email protected]"
    "require": {},
    "require-dev": {
        "tutv95/wp-package-parser": "^1.0"
}Code language: JSON / JSON with Comments (json)

The only parts you shouldn’t change are the package type and the tutv95/wp-package-parser dependency.

Push and test

Commit and push to GitHub. You should now see a new “Actions” tab across the top of your repository. Click into it, find the “Updater” workflow, and run it on the main branch. The action should pull and tag the latest available version of the plugin.

If you encounter errors, some manual tweaking may be required.


Confident that everything is in working order, you should now uncomment the schedule configuration of the on section in update.yml. By default, the package will auto-check for updates twice a day. Configure to your liking.


You can now require this library in your WordPress install. In your WordPress’s composer.json, all you need to do is tell Composer where to find your package by configuring the GitHub repo as a VCS repository:

    "name": "roots/bedrock",
    "type": "project",
    "repositories": [
            "type": "composer",
            "url": "https://wpackagist.org"
            "type": "vcs",
            "url": "[email protected]:sterner-stuff/advanced-custom-fields-pro.git"
    "require": {
}Code language: JavaScript (javascript)

And finally, run composer require {{ package-name }} using the package name you configured. Given the previous example:

composer require elliotcondon/advanced-custom-fields-proCode language: JavaScript (javascript)

Included Examples

We’ve included example workflows for a variety of plugins. Use these examples to manage plugins like Gravity Forms, Gravity Forms Add-Ons, Advanced Custom Fields Pro and The Events Calendar Pro via Composer. We’re always accepting pull requests for additional examples from the community.


The WordPress license, and how it impacts paid plugins, will probably always be a point of contention. Ignoring that, we believe that plugin authors that choose to charge for their work should get paid, considering the cost savings gained by using their work. Therefore, we strongly discourage using this workflow to make self-updating public mirrors of private WordPress plugins and themes. Please create a private repository for use in-line with the author’s terms of use.


You may need to make tweaks to the updater workflow unique to the plugin you’re trying to make work. Feel free to open a PR for more general improvements.

Known Incompatibilities

  • Easy Digital Downloads: EDD seems to use some kind of nonce system to create download links, so there is no fixed download link for items sold via EDD to my knowledge. Happy to be proven wrong. Impacts FacetWP, for example.


[Plugin X] authenticates a different way

See the wget documentation for a variety of options that should allow you to authenticate in whatever way required. Tinker with the “Fetch” step in .github/workflows/update.yml appropriately.

Can I use this for a theme?

Yes, but you’ll want to update composer.json to be use the package type wordpress-theme. The existing metadata parser should support WordPress themes.

The zip contents from my plugin download are unexpected (deeply nested, zip within a zip, etc)

This will require your own tinkering with the GitHub action to move directories and files around appropriately.

Opportunities for Improvement

This starter template could probably be tweaked to act as a standalone GitHub Action, accepting args for the package name and full download URL. PRs welcome.

See also

  • Bedrock for an introduction to Composer-managed WordPress
  • SatisPress, for using a WordPress install as a Composer repository and exposing its installed plugins/themes as packages
  • Package Peak, a SaaS that provides all your Envato purchases as Composer-compatible packages


Leave a Reply

Your email address will not be published. Required fields are marked *