# composer/satis: generating a static Composer repository from a JSON config

> Satis turns a list of package sources into a static Composer repository you can host anywhere. It fits teams that need a controlled package index without running a server, and it stops being the right answer once you need per-user access control.

**composer/satis** — Simple static Composer repository generator - For a full private Composer repo use Private Packagist

- Repository: https://github.com/composer/satis
- Stars: 3,306 · Forks: 530
- Language: PHP
- License: MIT
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/composer-satis

## What problem composer/satis solves, and for whom

Satis is a static Composer repository generator. The README frames it as a way for PHP developers to create a private package repository for their projects' dependencies, with the stated benefits being control over package distribution, improved security and faster package installations. The mechanism behind all three is the same: instead of Composer asking Packagist or a live server for package metadata, it reads a set of files that Satis produced ahead of time.

The audience is narrower than "anyone with private packages". The README's own description points elsewhere for the full-service case: for a full private Composer repo, use Private Packagist. Satis is for teams whose private packages can be described by a config file and whose delivery can be a directory of generated files. If your packages change constantly, or different developers must see different subsets of them, the static model works against you.

## How the build works: config in, static repository out

The data flow is a single command. You write a configuration file, run php bin/satis build <configuration-file> <output-directory>, and Satis writes a Composer repository into the output directory. That directory is the artifact. The README says it can be hosted anywhere, including via Docker or locally, which is only possible because there is no application server involved: whatever can serve files can serve the repository.

Because the output is static, freshness is a property of your pipeline, not of the tool. Nothing regenerates itself. The README's purge command exists precisely because the output accumulates: if you choose to archive packages as part of your build, over time you can be left with useless files, and php bin/satis purge <configuration-file> <output-dir> deletes them. The README attaches a warning to that command, telling you not to run it unless you are certain your projects no longer reference any of these archives in their composer.lock files. That warning is the clearest statement of the model's cost: the repository is a snapshot, and consumers may still be pinned to parts of an older snapshot.

## Installing composer/satis and building a first repository

There are two supported paths. From source, the README gives a single create-project command. It fetches the dev-main branch, keeps the VCS metadata and skips development dependencies. The README notes that Satis requires a recent PHP version and does not run with unsupported PHP versions, and points at composer.json for the exact requirement, so check that file against your interpreter before you start.

```bash
composer create-project --keep-vcs --no-dev composer/satis:dev-main
```

Once the project is in place, the build command takes your configuration file and an output directory. The README does not reproduce a full example config, and it links to the Composer documentation for the detailed instructions, so treat the config format as something you read there rather than something you can infer from the README alone.

```bash
php bin/satis build <configuration-file> <output-directory>
```

If you would rather not install PHP tooling on the build host, the Docker image is the alternative. The README says to use composer/satis for Docker Hub and ghcr.io/composer/satis for the GitHub container registry. The documented invocation mounts the current directory at /build, mounts the host Composer cache at /composer, and runs as your own user id so the generated files are not owned by root.

```bash
docker run --rm --init -it \
  --user $(id -u):$(id -g) \
  --volume $(pwd):/build \
  --volume "${COMPOSER_HOME:-$HOME/.composer}:/composer" \
  composer/satis build <configuration-file> <output-directory>
```

The Dockerfile shows the container's default command is build with /build/satis.json and /build/output, so a bare docker run with the /build mount will look for those paths unless you pass your own arguments. If you want the image without Satis running implicitly, the README says to override the entrypoint with --entrypoint /bin/sh.

## Where the static model breaks: purge, pinning and access

The purge warning is not incidental. A generated repository is consumed through composer.lock files that pin exact versions, and those pins can point at archives your newer builds no longer need. Deleting them is safe only when you know no lock file still references them, and the README leaves that verification to you. There is no dry-run flag documented in the README, and no rollback procedure either; the README does not document rollback. If you run purge against the wrong output directory, the recovery path is to rebuild, assuming the upstream sources still exist.

The second limitation is access. The README describes a static registry that can be hosted anywhere, and nowhere in it is there a user, a token or an authentication step. That is a feature when the repository is public or sits behind network controls you already operate, and a blocker when the requirement is that only certain people can fetch certain packages. Satis gives you a package index, not an authorization layer.

## Satis compared with satisfy and with Private Packagist

The README's Community Tools list names one alternative: satisfy, described as a Symfony based composer repository manager with a simple web UI. The difference is where the repository lives. Satis produces files and exits; satisfy runs as an application with a user interface, which means a process to deploy, keep running and secure, in exchange for managing the repository interactively rather than through a build command.

The other reference point is in the project's own description: for a full private Composer repo, use Private Packagist. That is a hosted service rather than something you run, and the trade is the opposite of Satis's: you give up the static artifact and the self-hosted build step in return for a repository that is managed for you. Neither choice is wrong in the abstract. Satis wins when the constraint is "serve files from wherever we already serve files"; the hosted option wins when the constraint is "we do not want to operate the build pipeline at all".

## Upgrading Satis, its licence and the cost of staying current

The README's upgrade instructions are as short as the install ones: run git pull && composer install in the Satis directory, or pull the latest image if you run the container. That is cheap in effort but not free in attention. The repository's release list shows 1.0.0 from 2018-02-05 and two alphas before it, so the tags do not describe the current state of the code; the README's install command uses composer/satis:dev-main, which tells you where current work actually lives. If your process assumes tagged releases, this project does not match that assumption.

The last push to the repository was on 2026-09-18, so work is happening on main. The Dockerfile pins the base image to php:8.4-cli-alpine and installs a specific version of the PHP extension installer with a checksum, which gives you a reproducible container but also means the image's PHP version moves only when that file changes. Satis is MIT licensed, which is permissive and imposes no conditions on how you distribute the generated repository; that is a statement about the licence text, not advice about your own obligations, which depend on the packages you index.

## Conclusion

Adopt composer/satis if you already have a place to serve static files and you want a package index that is rebuilt by a command rather than served by a daemon: install it with composer create-project --keep-vcs --no-dev composer/satis:dev-main, point php bin/satis build at a config and an output directory, and serve the result. Do not adopt it if you need per-user credentials, download auditing or an on-demand package API; the README describes a static registry, and nothing in it suggests authentication. Before committing, verify two things yourself: that the PHP version in composer.json matches your build host, since the README says Satis does not run on unsupported PHP versions, and that your CI can rebuild the repository whenever a dependency is tagged, because a static index only changes when you run the build again.

## FAQ

### How do I install composer/satis?

The README gives composer create-project --keep-vcs --no-dev composer/satis:dev-main for a source install, or you can pull the composer/satis Docker image from Docker Hub or ghcr.io/composer/satis. Satis requires a recent PHP version and does not run on unsupported PHP versions, so check composer.json first.

### What is the difference between composer/satis and Private Packagist?

The project's own description says to use Private Packagist for a full private Composer repository. Satis generates a static repository from a configuration file and an output directory, whereas Private Packagist is the hosted option the description points to for the full-service case.

### How do I update composer/satis?

The README says updating is as simple as running git pull && composer install in the Satis directory, or pulling the latest image if you run it as a Docker container.

### Is it safe to delete old archives with php bin/satis purge?

The README warns not to run purge unless you are certain your projects no longer reference any of those archives in their composer.lock files. The command deletes files that older lock files may still pin.

## Sources

- [composer/satis on GitHub](https://github.com/composer/satis)
- [Issues](https://github.com/composer/satis/issues)
- [License: MIT](https://github.com/composer/satis/blob/main/LICENSE)
- [README](https://github.com/composer/satis/blob/main/README.md)
- [Releases](https://github.com/composer/satis/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/composer-satis
