Private Composer packages with Satis

Sat 06 July 2019

One of the greatest additions to PHP environment is Composer. Together with provides enormous amount of packages for every PHP developer. I know, PHP had package manager before it was cool (I'm talking about you PEAR) but Composer was something everyone was waiting for.

Despite all positives, composer (and packagist) has one flaw - you cannot host private packages (in fact there is a paid version of packagist but... it's paid). If for some reason you need a way to use private packages, there is a way. It's called Satis.

What is Satis?

Satis is a private repository generator. It allows you to collect all your packages from private repositories and use those packages the same way as you would use a public repository. Major downside is that Satis is a static repository generator which means you are charge of updates. On top of that you are responsible for security (after all private repository needs to be available to your project in order to install packages).

Creating repository

Using Satis is really simple. You need to install Satis on your server, run build script that collects all your private packages and add few lines of code into your composer.json file. Let's start with installation.

composer create-project composer/satis --stability=dev --keep-vcs

This command will create project using Satis package. Next we need to create satis.json file.

    "name": "Secret Packages",
    "homepage": "",
    "repositories": [
        { "type": "vcs", "url": "" },
        { "type": "vcs", "url": "" }
    "require-all": true

Next we need to build our repository

php satis/bin/satis build satis.json web/

Now we need to set document root to web directory, and we are ready to go. Last step is to update our project's composer configuration.

"repositories": [{
    "type": "composer",
    "url": ""

Updating repository

As I mentioned before, in order to build a private repository we need to run Satis build command. It's really inconvenient to run it manually every time we've updated any of our packages. One way to handle this issue is to set cron job. It will work fine unless our packages get updated very often.

Webhook to the rescue!

We can configure our repository (e.g. Bitbucket) to use webhook every time we push changes. Satis allows updating specific package, so we won't be fetching all packages from all remote repositories.

php satis/bin/satis build satis.json web/ vendor/package

The webhook below is just proof of concept. In real application we need to implement better security, error handling and so on. Don't use this code in production!

if (!$_GET['package']) {
    header("HTTP/1.0 404 Not Found");

$package = $_GET['package'];

$php = '/path/to/php';
$satis = '/path/to/satis/executable';
$satisConfig = '/path/to/satis.json';
$destination = '/path/to/document/root';

    sprintf('%s %s build %s %s %s', $php, $satis, $satisConfig, $destination, $package)

Wrapping up

What I have presented is just scratching a surface. Satis has much more to offer - you can adjust what version are you want to fetch, secure repository with ssh keys, keep local copies of used repositories and many more. You'll find it all in official documentation.