Private Composer packages with Satis

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 of 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 public repository. Major downside is that Satis is static repository generator which means you need to handle 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 added 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 you noticed, in order to build 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 if our packages doesn't change very often. But what if we need to update repository every time when changes occurs in private packages?

Webhook to the rescue!

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

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

This webhook if very simple and its idea is to show how it works. In real life application we need to implement better security, error handling and so on.

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 present to you 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.