Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Satis Laravel Package

composer/satis

Satis is a simple static Composer repository generator. Point it at a composer.json and it builds an index of your packages and dependencies for fast, low-maintenance private or public Composer hosting, with optional archive downloads and mirroring.

View on GitHub
Deep Wiki
Context7

Getting Started

Satis is a build-time static Composer repository generator. It is not a Laravel package and never runs inside your application; it reads a satis.json, resolves packages from VCS or Composer repositories, and writes a packages.json (plus optional dist archives) into an output directory that you host as plain static files. A Laravel app only ever sees it as a composer repository URL.

Minimal setup, kept out of the app:

# Isolated tool repo — do not add this to your Laravel composer.json
composer create-project composer/satis:dev-master satis --no-interaction

The release cadence is slow (the last tagged release is from 2018), so most teams run dev-master; pin a commit in CI if you need reproducibility, and verify it runs on your PHP version.

Smallest useful satis.json:

{
  "name": "acme/packages",
  "homepage": "https://packages.acme.test",
  "repositories": [
    { "type": "vcs", "url": "[email protected]:acme/laravel-billing.git" }
  ],
  "require": { "acme/laravel-billing": "*" },
  "archive": {
    "directory": "dist",
    "format": "zip",
    "prefix-url": "https://packages.acme.test"
  }
}

Build and serve locally:

satis/bin/satis build satis.json public --no-interaction
php -S 127.0.0.1:8080 -t public

Consume it from a Laravel app:

{
  "repositories": [
    { "type": "composer", "url": "https://packages.acme.test" }
  ],
  "require": { "acme/laravel-billing": "^2.0" }
}

Look first at: the README, res/satis-schema.json (it is the authoritative list of config keys), and Composer's "Handling private packages" article. First real use case: publish a single internal package (e.g. a shared acme/laravel-support module) so composer require acme/laravel-support works without giving every developer git access to the source.

Implementation Patterns

Dedicated repository, not the app. Keep satis.json and the build in a packages repo or a dedicated CI job. The Laravel app should only carry the repositories entry.

Curated set vs. full mirror.

  • Curated: an explicit require map ({"acme/billing": "^2.0", "acme/ui": "*"}) for internal packages.
  • Mirror: "require-all": true, or {"require": {"vendor/*": "*"}} — useful for vendoring a slice of Packagist for air-gapped or high-availability installs.
  • Pull transitive deps with "require-dependencies": true and "require-dev-dependencies": true when you mirror, otherwise consumers hit Packagist for the leaves.

Always generate dist archives for private VCS packages. Without archive, every composer install clones from GitHub/GitLab — slow in Docker builds and easy to rate-limit. With archives, installs are plain HTTP downloads. prefix-url must be reachable by consumers (a CDN URL is fine).

Local development override. While iterating on a package, skip rebuilding Satis in the app:

"repositories": [
  { "type": "path", "url": "../laravel-billing", "options": { "symlink": true } }
]

Swap back to the composer repo URL in CI/production.

CI build + publish. A representative GitHub Actions shape:

- uses: shivammathur/setup-php@v2
  with: { php-version: '8.2', extensions: 'zip, openssl' }
- run: composer create-project composer/satis satis --no-interaction
- run: satis/bin/satis build satis.json public -n --skip-errors
  env:
    COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"},"gitlab-token":{"gitlab.com":"${{ secrets.GL_TOKEN }}"}}'
- run: aws s3 sync public s3://acme-composer --delete

Trigger on tag pushes in package repos (via repository_dispatch) rather than polling; Satis does full rebuilds, not incremental diffs.

Auth belongs at the edge. Satis has no authentication. Serve from S3+CloudFront with signed URLs, nginx basic auth, or an IP allowlist. Keep a separate public output for packages that are genuinely open.

Dev branches. "require": {"acme/billing": "dev-main"} works, but set "minimum-stability": "dev" and "prefer-stable": true in satis.json so resolution succeeds. These settings affect the build, not the consuming app's stability policy.

Enable output-html (on by default) for a browsable package index — cheap discovery for the team, and useful for verifying a build landed.

Gotchas and Tips

  • Don't add Satis to the app's require/require-dev. It's tooling; putting it in the app drags Composer's internals and its own dependency tree into your runtime lockfile.
  • Composer 2 compatibility is the big one. Satis writes a Composer 1-style packages.json with includes/provider files. Composer 2 reads this fine, but it will never expose metadata-url, available-packages, or lazy per-package metadata. If you need those, Satis is the wrong tool — look at Private Packagist, Repman, or a small custom repo. Test with composer show -a acme/billing --all from a scratch app against the built repo.
  • homepage must exactly match the public root URL. If it doesn't, the include/all$hash.json paths and dist URLs in the output point somewhere wrong and installs fail with 404s that look like auth errors.
  • Empty repository output usually means auth, not config. Private VCS with no token, missing SSH deploy key in CI, or a token without repo scope silently yields zero packages. Re-run with satis build -vvv and inspect the output directory.
  • Rate limits. Unauthenticated GitHub API access will bite on any non-trivial repo set. Pass github-oauth through COMPOSER_AUTH in CI and locally.
  • --skip-errors is a footgun. It keeps a broken package from failing the whole build, but the result is a repository quietly missing packages. Use it only in scheduled, non-blocking builds, and alert on package-count deltas.
  • Archives need ext-zip and a git binary on the build runner. The failure mode is a build that appears to succeed but publishes metadata pointing at nonexistent dist files.
  • prefix-url is baked into packages.json. Changing your CDN domain means rebuilding, not just re-uploading.
  • CDN caching: packages.json can be cached briefly, but the include files are content-hashed and change every build — invalidate packages.json aggressively, let the hashed files cache forever. Don't enable long TTLs on the index.
  • Serving from a subpath (e.g. https://acme.test/composer) requires that full path as homepage; relative resolution will otherwise break includes.
  • satis.json schema drift. Config keys have been added over time (require-dependencies, only-best-candidates, strip-hosts, twig-template, include-filename). Check res/satis-schema.json in the checked-out version rather than trusting blog posts — especially on dev-master.
  • Debugging checklist: satis build -vvv → inspect public/packages.json for the expected package and version → curl one dist URL → run composer diagnose and composer require acme/pkg -vvv in a throwaway app with only that repository configured.
  • Extension point: "twig-template": "views/index.html.twig" customizes the HTML index only — it does nothing to the Composer metadata. Everything else you might want to customize means post-processing the generated JSON.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager