Skip to main content

Private Magento Composer Package Repository Guide

Private Magento Composer Package Repository Guide

This guide explains how Example Development creates, publishes, versions, and distributes proprietary Magento 2 modules using a self-hosted GitLab Composer Package Registry.

The examples use a fictional Magento module named Example_Core, published as the Composer package example/module-core.

  • GitLab Server: https://git.example.com
  • GitLab Group: example-magento
  • GitLab Repository: module-core
  • Project ID: 123
  • Composer Package: example/module-core
  • Initial Version: 1.0.0

1. Create the Repository

1.1 Create the GitLab Group

  1. Log in to https://git.example.com.
  2. Navigate to Groups and create a new group.
  3. Set the group name to Example Magento.
  4. Set the group path to example-magento.
  5. Set visibility to Private.

All reusable Magento modules should be maintained in this group. Each module should have its own GitLab repository.

1.2 Create the Module Repository

  1. Open the example-magento group.
  2. Select New project.
  3. Create a blank project named module-core.
  4. Set visibility to Private.
  5. Create the project.

Repository URL:

https://git.example.com/example-magento/module-core.git

In this example, the project ID is 123. Replace it with the actual numeric ID assigned by GitLab.

1.3 Clone the Repository

git clone https://git.example.com/example-magento/module-core.git
cd module-core

Copy the contents of the Magento module into the repository root. Do not copy the entire app/code/Example/Core directory hierarchy.

Expected repository structure:

module-core/
    composer.json
    registration.php
    etc/
    Helper/
    view/
    .gitignore

1.4 Create composer.json

Every Composer package must contain a composer.json file. The package name follows the format vendor/package-name.

Example:

{
    "name": "example/module-core",
    "description": "Example Core Module for Magento 2",
    "type": "magento2-module",
    "license": "proprietary",
    "authors": [
        {
            "name": "Example Development",
            "homepage": "https://www.example.com/"
        }
    ],
    "require": {},
    "autoload": {
        "files": [
            "registration.php"
        ],
        "psr-4": {
            "Example\\Core\\": ""
        }
    }
}

The Magento registration.php file must register the Example_Core module, and etc/module.xml must declare the same module name.

1.5 Define PHP Version Requirements

Composer allows packages to specify supported PHP versions through the require section.

Require PHP 8.2 specifically:

"require": {
    "php": ">=8.2 <8.3"
}

Allow PHP 8.2 and PHP 8.3:

"require": {
    "php": ">=8.2 <8.4"
}

Allow PHP 8.1 and later compatible versions below PHP 9:

"require": {
    "php": "^8.1"
}

Composer checks these requirements during installation and updates. If the server's PHP version does not satisfy the requirement, dependency resolution fails.

PHP version constraints must reflect the versions actually supported and tested by the module and its Magento dependencies.

1.6 Add Dependencies Between Modules

Suppose Example_Integration requires Example_Core. Add the following dependency to the integration module's composer.json file.

{
    "name": "example/module-integration",
    "description": "Example Magento Integration Module",
    "type": "magento2-module",
    "license": "proprietary",
    "require": {
        "php": ">=8.2 <8.4",
        "example/module-core": "^1.0"
    },
    "autoload": {
        "files": [
            "registration.php"
        ],
        "psr-4": {
            "Example\\Integration\\": ""
        }
    }
}

With this dependency, installing example/module-integration automatically installs example/module-core when required.

composer require example/module-integration

The version constraint ^1.0 allows compatible releases starting at 1.0.0 but excludes version 2.0.0 and higher.

1.7 Create .gitignore

Create a .gitignore file to prevent operating-system files, editor configurations, credentials, and temporary files from being committed.

# macOS
.DS_Store
._*
.AppleDouble
.Spotlight-V100
.Trashes

# Windows
Thumbs.db
Desktop.ini

# IDEs
.idea/
.vscode/
*.iml
*.swp
*.swo

# Composer
/vendor/
auth.json

# Secrets
.env
.env.*
!.env.example
*.pem
*.key

# Temporary files
*.log
*.tmp
*.bak

# Testing
.phpunit.cache/
/coverage/

# macOS archives
__MACOSX/

Remove unwanted macOS metadata files:

find . -path './.git' -prune -o -name '.DS_Store' -type f -delete
find . -path './.git' -prune -o -name '._*' -type f -delete

1.8 Validate and Push the Repository

Validate the Composer configuration:

composer validate --no-check-publish

Commit and push the source code:

git add .
git commit -m "Initial release of Example_Core"
git push -u origin HEAD

The source code is now stored in GitLab. Pushing the source code does not automatically publish a Composer package.

2. Configure the Package Registry and Access Tokens

2.1 Verify the GitLab Package Registry

  1. Open the module-core GitLab project.
  2. Navigate to Deploy > Package Registry.
  3. Verify that the Package Registry is available.

Initially, the registry may display no packages. Packages appear after publishing through the Composer API.

2.2 Create the Publisher Deploy Token

The publisher credential is used internally to publish new package versions.

  1. Open the example-magento GitLab group.
  2. Navigate to Settings > Repository > Deploy tokens.
  3. Create a group deploy token.
  4. Configure the following settings.
  • Name: Example Composer Publisher
  • Username: example-composer-publisher
  • Permission: write_package_registry
  • Expiration: According to the organization's security policy

Save the generated username and token securely. Do not commit credentials to Git or share publisher credentials with customers.

2.3 Enter Publisher Credentials on macOS

These commands use the default macOS Zsh shell.

read "GITLAB_USER?GitLab deploy token username: "
read -s "GITLAB_TOKEN?GitLab deploy token: "
echo

GITLAB_USER stores the deploy token username. GITLAB_TOKEN stores the secret. The secret is hidden while being entered.

2.4 Create the Initial Version Tag

git tag -a 1.0.0 -m "Example_Core version 1.0.0"
git push origin 1.0.0

A tag identifies a specific version of the source code. Unlike the main branch, a release tag should not move.

2.5 Publish the Initial Composer Package

Publish version 1.0.0 using the GitLab Composer API.

curl --request POST \
  --user "$GITLAB_USER:$GITLAB_TOKEN" \
  --data-urlencode "tag=1.0.0" \
  --write-out "\nHTTP Status: %{http_code}\n" \
  "https://git.example.com/api/v4/projects/123/packages/composer"

Expected successful response:

{"message":"201 Created"}
HTTP Status: 201

The package example/module-core version 1.0.0 is now published in the private Composer Package Registry.

2.6 Verify the Published Package

Open the GitLab project's Package Registry and verify:

  • Package: example/module-core
  • Version: 1.0.0
  • Type: Composer

Verify package metadata through the API:

curl --user "$GITLAB_USER:$GITLAB_TOKEN" \
  --write-out "\nHTTP Status: %{http_code}\n" \
  "https://git.example.com/api/v4/group/example-magento/-/packages/composer/p2/example/module-core.json"

A successful response returns HTTP 200 and the package metadata.

2.7 Create a Customer Deploy Token

Each customer should receive a separate read-only credential. Customers must not receive the publisher token.

  1. Open the example-magento GitLab group.
  2. Navigate to Settings > Repository > Deploy tokens.
  3. Create a new token for the customer.
  4. Enable only read_package_registry.
  5. Save the generated username and secret.

Example customer token:

  • Name: Demo Customer
  • Username: example-demo-customer
  • Permission: read_package_registry
  • Publishing permissions: Disabled
  • Repository permissions: Disabled

Important: A group deploy token can access package registry resources across the group. It does not enforce per-package customer licensing. Package-specific entitlements require a different access structure or a dedicated distribution service.

2.8 Configure the Customer Composer Repository

Navigate to the customer's Magento project root, where composer.json is located.

composer config repositories.example composer \
  https://git.example.com/api/v4/group/example-magento/-/packages/composer/

This adds the private Composer repository to the customer's composer.json file.

2.9 Configure Customer Authentication

composer config --auth http-basic.git.example.com example-demo-customer

Enter the generated customer deploy token when prompted. Composer stores the credentials in its authentication configuration.

Keep auth.json private and excluded from source control.

2.10 Install the Package

Install the core module:

composer require "example/module-core:^1.0"

Composer retrieves the metadata, downloads the package archive, and installs a compatible version.

If the module is being installed into Magento, enable it after Composer installation:

php bin/magento module:enable Example_Core
php bin/magento setup:upgrade
php bin/magento cache:flush

Perform installation and upgrade testing on staging before deploying changes to production.

2.11 Customer Token Security

  • Create separate credentials for each customer.
  • Use read_package_registry for customer credentials.
  • Keep publisher credentials internal.
  • Set expiration dates and rotate tokens regularly.
  • Revoke compromised or unused tokens.
  • Never store credentials in source control.

3. How to Push a New Version

3.1 Versioning Strategy

Use semantic versioning for Composer packages.

  • 1.0.0: Initial stable release.
  • 1.0.1: Bug fixes.
  • 1.1.0: Backward-compatible new features.
  • 2.0.0: Breaking changes.

The main branch contains ongoing development. Git tags identify specific published releases.

3.2 Update the Source Code

Open the local repository:

cd module-core
git checkout main
git pull --ff-only origin main

Make the required changes to the Magento module. Update composer.json if PHP requirements or package dependencies have changed.

3.3 Change PHP Requirements

Example: A future release supports PHP 8.3 and 8.4.

"require": {
    "php": ">=8.3 <8.5"
}

This permits PHP 8.3 and 8.4 but excludes PHP 8.2 and 8.5. Use only requirements that match tested compatibility.

3.4 Update Package Dependencies

Suppose Example_Integration now requires Example_Core version 1.2.0 or newer.

"require": {
    "php": ">=8.2 <8.4",
    "example/module-core": "^1.2"
}

Composer will select a compatible Example_Core release. The required dependency version must already be published in a configured repository.

3.5 Validate the Package

composer validate --no-check-publish

Resolve any validation errors before releasing.

3.6 Commit and Push the Changes

git add .
git commit -m "Prepare Example_Core version 1.0.1"
git push origin main

This updates the main branch but does not yet create or publish a new Composer version.

3.7 Create the New Version Tag

git tag -a 1.0.1 -m "Example_Core version 1.0.1"
git push origin 1.0.1

Verify the published Git tag:

git ls-remote --tags origin

3.8 Publish the New Version to Composer

Enter the publisher credentials if they are not already available in the current Terminal session.

read "GITLAB_USER?GitLab deploy token username: "
read -s "GITLAB_TOKEN?GitLab deploy token: "
echo

Publish version 1.0.1:

curl --request POST \
  --user "$GITLAB_USER:$GITLAB_TOKEN" \
  --data-urlencode "tag=1.0.1" \
  --write-out "\nHTTP Status: %{http_code}\n" \
  "https://git.example.com/api/v4/projects/123/packages/composer"

Expected response:

{"message":"201 Created"}
HTTP Status: 201

The new release is now available through the private Composer Package Registry.

3.9 Update the Package on Customer Websites

Customers can update an installed package to a newer compatible version using:

composer update example/module-core --with-dependencies

To explicitly change the required version:

composer require "example/module-core:^1.1"

If dependency constraints require related packages to change, use Composer's --with-all-dependencies option where appropriate.

After updating the Magento module, run the relevant Magento deployment commands.

php bin/magento setup:upgrade
php bin/magento cache:flush

Depending on deployment mode and module changes, dependency injection compilation and static content deployment may also be necessary.

3.10 Release Checklist

  1. Update and test the Magento module.
  2. Review supported PHP and Magento versions.
  3. Update Composer dependencies if necessary.
  4. Validate composer.json.
  5. Commit and push changes to GitLab.
  6. Create a new release tag.
  7. Push the tag to GitLab.
  8. Publish the tag through the Composer API.
  9. Verify the package in GitLab's Package Registry.
  10. Test installation or upgrade on staging.
  11. Release the version to customers.

3.11 Future Improvement: Automatic Publishing

The current workflow publishes Composer packages manually through the GitLab API.

GitLab CI/CD can later automate package publishing whenever a new release tag is pushed.

The CI/CD pipeline should use a securely stored credential with the minimum required publishing permissions.

Conclusion

This process provides a private Composer package distribution workflow using a self-hosted GitLab server.

The example package example/module-core is published as version 1.0.0 and can be installed using Composer.

The same approach can be reused for other proprietary Magento modules, including packages with dependencies on shared core modules.

Customer-specific read-only deploy tokens allow package installation without exposing publisher credentials. More granular customer licensing can be implemented later through a dedicated Composer distribution service.

↑