Docs menu

Package managers

How Composer runs on Primcloud: supported registries, private repositories, credentials, lockfile requirements, and PHP extensions as packages.

On this page

PHP has one package manager: Composer. Every dependency you declare (a require entry, a required extension via ext-*, a require-dev tool) is resolved through Composer against one or more package repositories.

Primcloud runs Composer as part of every build. This page covers what gets installed, how to point at private repositories, and what the build does with your composer.lock.

Supported package sources#

By default, Composer resolves packages from Packagist, the primary PHP package repository.

To add or override sources, list them in composer.json under repositories. The Composer CLI reads this at both install and update time:

composer.json
{
  "repositories": [
    {
      "type": "composer",
      "url": "https://repo.packagist.com/acme/"
    },
    {
      "type": "vcs",
      "url": "[email protected]:acme/private-package.git"
    }
  ],
  "require": {
    "acme/private-package": "^1.0"
  }
}

The build doesn't modify your repositories list. Everything Composer supports (private Packagist, VCS repositories, path repositories, package repositories, composer proxies) works the same on Primcloud as it does locally.

Install behavior#

The build runs one Composer command during the build phase:

Terminal
composer install --no-dev --no-progress --no-interaction --optimize-autoloader --prefer-dist

Each flag matters:

  • --no-dev: dependencies in require-dev are skipped. Test frameworks, linters, and dev-only tooling don't ship in the runtime image.
  • --no-progress / --no-interaction: Composer produces one line per package instead of animated progress bars, and never prompts.
  • --optimize-autoloader: generates a classmap of every autoloadable class so the runtime autoloader is O(1) instead of stat-ing the filesystem per class lookup.
  • --prefer-dist: packages install from their prebuilt archive (Packagist's dist URL) rather than a git clone, cutting the install time and disk footprint.

If your composer.json defines a scripts.compile entry, Composer runs it after the install finishes. That's the hook for custom post-install build work; see Customizing the build → Build hooks.

The build also sets these Composer-facing environment variables at build time:

Variable Value Effect
COMPOSER_MEMORY_LIMIT Sized to pod memory Prevents Composer OOM during dependency resolution on large projects.
COMPOSER_NO_INTERACTION 1 Composer never prompts.
COMPOSER_MIRROR_PATH_REPOS 1 type: "path" repositories are copied into the layer, not symlinked.
COMPOSER_PROCESS_TIMEOUT 0 The default 300-second subprocess timeout is removed, so long-running post-install-cmd scripts don't die halfway.

Anything you set in the Secrets panel overrides these, including COMPOSER_AUTH and per-repository credentials (below).

Private packages and registries#

Composer reads credentials from either a COMPOSER_AUTH environment variable containing a JSON blob, or from auth.json in the same directory as composer.json.

The safer path is COMPOSER_AUTH, set in the Secrets panel. It takes a JSON string keyed by auth type:

{
  "http-basic": {
    "repo.packagist.com": { "username": "token", "password": "<token>" }
  },
  "github-oauth": {
    "github.com": "<token>"
  },
  "bearer": {
    "custom-registry.example.com": "<token>"
  }
}

Set that as a single line in the Secrets panel:

COMPOSER_AUTH={"http-basic":{"repo.packagist.com":{"username":"token","password":"..."}}}

Composer picks it up automatically at install time. Secrets are stored encrypted and never surfaced in build logs.

For GitHub-hosted private repositories referenced via type: "vcs", the github-oauth entry above is what Composer uses to clone them. A personal access token with repo scope is sufficient.

Warning

Avoid committing auth.json to your repo: it's readable to anyone with git access. COMPOSER_AUTH on the environment is the recommended shape.

Lockfiles#

composer.lock is required when your composer.json declares dependencies in require. The build rejects apps with a require section but no matching lockfile. Reproducible deploys mean the same version graph installed every time, and Composer can't guarantee that without a lock.

An empty composer.json ({}) or one with only require-dev entries is allowed without a lockfile, since there's nothing runtime-facing to install.

The lockfile also carries the resolved platform requirements: the exact PHP version and extension constraints Composer picked at composer update time. The build reads those before installing the runtime, so the PHP version you get on Primcloud matches what Composer resolved locally.

Updating. Run composer update locally whenever you change require, require-dev, or the php platform requirement. Commit both composer.json and the refreshed composer.lock.

Diagnosing failures. If the lockfile disagrees with composer.json (a manual edit to one without regenerating the other, an unresolved merge conflict), the build fails with a "lock file is out of sync" message. composer update --lock regenerates the lockfile without changing dependency versions unnecessarily.