Self-hosted service
vtsykun/packeton avatar
vtsykun/packeton

Packeton and a private Composer repository you host yourself

:package: Private, self-hosted Packagist/Composer/Satis repository with unlimited private repos.

553 stars92 forksPHPMIT

At a glance

What is it?
Packeton is a fork of Packagist, the public PHP package registry, turned into a private repository for vendors: it keeps the Composer API v2 protocol, adds customer accounts with per-vendor and per-version access control, and can mirror upstream package archives so your builds keep working when Packagist or GitHub does not. The interesting engineering is the ACL and mirroring layer, and the interesting constraint is that a vendor is expected to run a PHP application and a Redis instance themselves.
Who is it for?
Packeton is a good fit for a PHP vendor who sells a commercial package to customers and needs to control who can install which version, because the customer and ACL model is the reason the fork exists rather than a side feature, and because mirroring means a customer build does not depend on Packagist or GitHub being reachable.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why a vendor would run a package registry at all

Packeton is a private PHP package repository for vendors. It is a fork of Packagist, the public registry, and is described as the open source alternative to Private Packagist for vendors, based on Satis and on Packagist itself.

The problem it addresses is specific to a business model rather than a general one. A vendor selling a commercial PHP package to paying customers faces two problems at once. The first is access control: a customer should install the version their licence entitles them to, and not the version they upgraded to after their licence expired. The second is availability: a build server that cannot reach Packagist or GitHub cannot install anything, and for a paying customer that outage is your problem rather than theirs.

Packeton answers the first with customer users, ACL groups, and access limited by vendor and by version. It answers the second with mirroring, so that package zip files and downloads are served from your own host rather than fetched from GitHub on every install.

The licence is MIT, the primary language is PHP, and the documentation lives at docs.packeton.org, with a public administration demo at demo.packeton.org. The last push was on 2026-04-29, and the recent releases are v2.7.0 from 2025-03-02, v2.6.0 from 2024-09-15 and v2.5.1 from 2023-12-27.

The audience is therefore a PHP vendor with paying customers, not a developer who wants a private mirror of public packages. That distinction runs through the whole feature list, and it is where the fork diverges from Packagist most sharply.

What the fork changed, and what it dropped

The difference between Packeton and upstream Packagist is listed explicitly, and it is worth reading in both directions.

What was added: anonymous access is disabled, as are registrations and the spam and antispam machinery, and ACL permissions were added. The database layer was made pluggable, with support for MySQL, PostgreSQL or SQLite.

What was removed: HWIOBundle, Algolia, GoogleAnalytics, other unused dependencies and other metrics collectors.

That is a coherent trade. Packagist is built for a public index of the entire PHP ecosystem, so it needs search, ranking, spam defence and analytics, and a private instance for a few hundred customers needs none of that. Removing Algolia and the metrics collectors also removes a large amount of surface area and some weight from the container.

The other changes are about compatibility rather than removal. The Composer API v2 is supported, and the project is based on Symfony 6. The repository layout shows the PHP side of that: a config/ directory, a doctrine/ directory, templates/, translations/, a swagger/ directory for the API description, and a book.toml, which is the source file for a rendered documentation book rather than a set of loose Markdown files.

The web features that distinguish it are the ones aimed at a vendor's workflow. Update webhooks are supported for GitHub, Gitea, Bitbucket and GitLab, or a custom format, and the documentation has separate sections for each provider plus a manual hook and a custom webhook format transformer. There is a Pull Request composer.lock change review feature, so a customer can see exactly which dependency versions a pull request would change before it merges. Updates can be frozen for new releases after a customer licence expires, which is the feature that turns the access control into something commercially meaningful. There is security monitoring, OAuth2 integrations for GitHub, Bitbucket and GitLab, and support for monolithic repositories such as symfony/symfony. SSH key access and composer OAuth token access are both documented, and there is an LDAP authentication path.

The documentation index also lists an import path from Packagist.com, which is the migration route for a vendor moving off the public service.

Running it in Docker, the recommended path

The Docker route is the one the project recommends, and there are two published images: packeton/packeton on Docker Hub and ghcr.io/vtsykun/packeton:latest in the GitHub container registry. The single container run is:

bash
docker run -d --name packeton \
    --mount type=volume,src=packeton-data,dst=/data \
    -p 8080:80 \
    packeton/packeton:latest

The named volume on /data is the persistent part, since the database, the Composer home and the mirrored archives all live there, and the environment variable list below confirms the layout with defaults of /data/composer, /data/app.db and /data/zipball.

With the container running, the first thing to do is create an administrator, and the console command is:

bash
docker exec -it packeton bin/console packagist:user:manager admin --password=123456 --admin

The same command is used from source, with a username of your choice, for example php bin/console packagist:user:manager us.

There are two compose files, and the documentation is upfront about the difference. docker-compose.yml is a single container example where the container runs a supervisor that starts the other jobs, nginx, redis, php-fpm, cron and a worker. The documentation notes that this does not follow the Docker best practice of one service per container, but that it is very easy to use and follows the KISS principle. docker-compose-split.yml is the multiple container version with one service per container. The commands are the same shape for both:

bash
docker-compose build

docker-compose up -d # Run with single supervisor container 
docker-compose up -f docker-compose-split.yml -d # Or split 

The single-container option is the pragmatic choice for an evaluation or a small vendor. The split option is the one to use in production, because the worker queue and the cron process need to be restartable independently of the web server, and because that is where the supervisor abstraction stops paying for itself.

The environment variables that decide your deployment

The variable list is the practical configuration surface, and two entries carry more risk than the rest.

APP_SECRET must be static, and it is used for encrypting SSH keys in the database. The value is generated automatically and lives in the .env file in the data volume. That is worth understanding carefully, because it means the secret that protects the stored customer SSH keys is inside the same volume as the database it protects. If you back up the data volume, back up the key with it, and if you lose the key, the SSH keys in the database are unrecoverable.

DATABASE_URL is the DSN, with a default of sqlite:////data/app.db, and the example given for PostgreSQL uses a serverVersion and charset parameter. The compose file in the repository ships a MySQL DSN with the password !ChangeMe!, which is a placeholder you must not keep.

REDIS_URL defaults to redis://localhost, and Redis is not optional in practice. The requirements list it as needed for some functionality, namely favorites, download statistics and the worker queue, and the Dockerfile installs the redis PHP extension and the redis server itself.

APP_COMPOSER_HOME defaults to /data/composer and PACKAGIST_DIST_PATH defaults to /data/zipball, the path to storage for the zipped versions. PACKAGIST_DIST_HOST is the hostname used in the download URLs and, by default, uses the current host header from the request, which is what makes the instance work behind any domain without configuration.

Two variables are security-relevant rather than functional. TRUSTED_HOSTS should be set if you have enabled public access and your nginx configuration uses no server_name, and the documentation explains why: otherwise a DDoS attack with generated a big cache size for each host. TRUSTED_PROXIES is for the reverse proxy case and points at the Symfony documentation.

PUBLIC_ACCESS defaults to false and allows anonymous users to read package metadata. MAILER_DSN is disabled by default and exists for password reset, with MAILER_FROM as the sender.

The order of the two access variables is the design. A private vendor instance wants TRUSTED_HOSTS set and PUBLIC_ACCESS false, and the documentation makes the DDoS consequence of skipping the first explicit.

Installing from source, and what the Docker image contains

The from-source path is short. The requirements are PHP 8.1 or newer, Redis for some functionality covering favorites, download statistics and the worker queue, git, svn or hg depending on which repositories you want to support, Supervisor to run a background job worker, and optionally MySQL or PostgreSQL for the main data store with SQLite as the default.

The five steps are: clone the repository, install dependencies with composer install, create .env.local and copy the needed environment variables into it, run bin/console doctrine:schema:update --force --complete to set up the database, and create the admin user via the console.

The Dockerfile is more interesting than the step list, because it shows the shape of the application. The base is php:8.4-fpm-alpine, and the layer above it installs nginx, curl, runit, subversion, git, bash, openssh-client, zip, unzip, redis and shadow, and then installs Composer itself from getcomposer.org. The SSH configuration it writes disables host key checking for all hosts, which is a convenience for a container that clones repositories over SSH, and also a setting you should be aware of.

The PHP extensions compiled in are the interesting part: sockets, ldap, xsl, zip, pdo, pdo_pgsql, pdo_mysql, sysvsem and opcache, plus bz2 and pcntl, and the redis extension through pecl. The ldap extension is what makes the LDAP authentication path work, and pcntl is what lets the worker and cron processes run inside the container. The build also strips test directories from several vendor packages, which is the kind of housekeeping that keeps an image small.

The build uses the Composer files copied first so the dependency layer is cached separately from the source layer, and the run dependencies are computed with scanelf so that only the shared libraries actually needed are installed. The application is copied with ownership 82:82, which is the nginx user in the Alpine image.

Where Packeton is the wrong tool

Three limits follow from what the project is.

The first is operational weight for what many people actually want. If the goal is a private mirror of public Packagist packages, with no customers and no commercial licensing, then Satis does it and it does it with a configuration file and a static repository. Packeton is a web application with a database, a Redis instance, a worker queue, a cron process and a set of console commands, and the operational cost of that is not small for a team whose product is PHP code rather than infrastructure.

The second is the fork's own cost. Packeton is a fork of Packagist, and a fork inherits work. The repository carries an UPGRADE.md, which is the artefact of a project that has to reconcile upstream changes with its own modifications, and the removal of Algolia, GoogleAnalytics and HWIOBundle means that merging upstream changes is not a clean operation. You are running a divergence, and the question for any adopter is how much of upstream Packagist you still need. The GitLab organization and group hooks support and the OAuth2 integrations are all features that exist because the fork has to be maintained alongside a moving upstream, and that is a tax.

The third is that the value of the access control model depends on your licensing being enforceable through it. The feature that freezes updates for new releases after a customer licence expires is only useful if the customer authenticates against the instance rather than against Packagist, which in turn means your customers have to point their composer.json at your server. That is a change to your customer's build configuration, and for a team whose customers are enterprises with their own artifact mirrors, that negotiation is harder than the technical work.

The honest summary is that Packeton earns its keep for a vendor with paying customers and a real entitlement model. For everyone else, a lighter tool does the same job, and the difference in what you have to operate is the deciding factor.

Editorial conclusion

Packeton is a good fit for a PHP vendor who sells a commercial package to customers and needs to control who can install which version, because the customer and ACL model is the reason the fork exists rather than a side feature, and because mirroring means a customer build does not depend on Packagist or GitHub being reachable. It is a poor fit if you just want a private mirror of public packages with no customer model, since Satis already does that with far less to run, and a poor fit if you do not want to operate a PHP application, a Redis instance and a background worker. Before deploying, read the security model in SECURITY.md and the UPGRADE.md notes, and set APP_SECRET to a static value before the first boot, because it is what encrypts the stored SSH keys.

Frequently asked questions

What is Packeton and how does it relate to Packagist?

Packeton is a fork of Packagist, the public PHP package registry, turned into a private repository for vendors. It is based on Satis and Packagist, supports the Composer API v2 on Symfony 6, and adds customer accounts, ACL groups and access limits by vendor and version.

How do I run Packeton with Docker?

Run `docker run -d --name packeton --mount type=volume,src=packeton-data,dst=/data -p 8080:80 packeton/packeton:latest`, then create an administrator with `docker exec -it packeton bin/console packagist:user:manager admin --password=123456 --admin`. Two compose files are also provided, one single container and one split per service.

Which databases does Packeton support?

MySQL, PostgreSQL or SQLite, set through the DATABASE_URL variable, whose default is sqlite:////data/app.db. Redis is also required for favorites, download statistics and the worker queue.

What can a customer do with an expired licence in Packeton?

Updates can be frozen for new releases after a customer licence expires, and access is limited by vendor and version, so an expired customer can keep the version they are entitled to without receiving newer ones.

What does the APP_SECRET environment variable do?

It must be static and is used for encrypting SSH keys in the database. The value is generated automatically and lives in the .env file inside the data volume, so it has to be preserved with the volume it protects.

What licence is Packeton released under?

MIT, with the licence file at the repository root. The most recent release is v2.7.0 from 2025-03-02, and the last push to the repository was on 2026-04-29.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. vtsykun/packeton on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vtsykun-packeton.svg)](https://hysenlabs.com/projects/vtsykun-packeton)

Was this useful?