Using Local Composer Repositories in Adobe Commerce

When using Magento, there are a number of times where an extension within a composer repository is preferred.

Previously, when working with agencies (and we’re no exceptions here), we’ve found that they have hosted or internal repositories for extensions that have been developed in-house. This means quick and consistent deployment across environments while allowing the flexibility of composer to manage dependencies.

However, an issue often arises when a client moves between agencies, and access is restricted to the repository. It’s unlikely that the agency would continue to give access to these repositories once the client moves away, as they no longer have control to release updates or manage infrastructure changes on their side, without breaking legacy clients’ solutions.

Assuming you have access to the production codebase, you will have a copy of the extension version that is in use on the site at the time of client onboarding. At this point, there are a number of options:

Move the extension to app/code

There are structural differences between how extensions are put together in app/code versus vendor and it’s not always appropriate to migrate extensions from one to the other when there is no integral requirement.

Copy the extension into your own repository

If you have your own repository available to push a copy of the extension to, you can do this, and then update the composer.json / composer.lock file to look at the new repository location instead. Then, you can fix the version to the version you have. It’s likely that you’re now supporting a codebase that will never be altered and you have the same issues as an existing agency supporting it.

Use a “local” version of the extension

If you wanted to keep the extension within composer, but you were happy that the extension was only ever going to be used within that client’s project, then you could be best keeping the extension within the codebase. This ensures it is contained and relevant to the project.

To achieve this, you’ll need to set up composer.json to recognise a “local” repository within the existing codebase. There are a number of steps that need to be taken to do this:

  1. Create a new project directory within your source control to put this extension in. In our example, we’re going to make vendor-legacy
  2. Copy a known working version of this extension (from production) into the new directory. This should include the namespace as well as the extension directory, ie. vendor-legacy/pinpoint/module-example
  3. Add a new repositories key at the top of your list so that it is loaded in first
"legacy-packages": {
"type": "path",
"url": "./vendor-legacy/*/*",
"options": {
"symlink": false

4. Ensure that your vendor-legacy extension has a fixed version number within the composer.json of the extension.

5. Ensure that the main composer.json file references the specific fixed version of your extension

6. Run a composer update

You should now find that on composer install the package is copied from vendor-legacy into vendor as a normal composer package. You can change "symlink":false to "symlink:true if you’d prefer to have a symlink deployed into your vendor directory.

With this in place, you can support the client with the existing known-good version of the extension, while also having a format that means you can update it in the future, or swap it out for a third-party supported version.


Team Pinpoint hold a lot of knowledge when it comes to Adobe Commerce. We don’t just share that knowledge with our clients, but also other developers and those who are part of the Magento ecosystem. If you want to chat more about this topic, please get in touch with our experts.