Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

PHP Composer Private Packages via Satis and Repman

By Kokil Thapa | Last reviewed: August 2026

Managing proprietary code across multiple projects requires a reliable method for distributing internal libraries without exposing them to the public Packagist registry. Setting up PHP Composer private packages via Satis and Repman solves this by creating a self-hosted metadata repository that authenticates developers and serves zip archives directly from your infrastructure. Whether you are building legal-tech portals or multi-tenant eCommerce systems, this approach keeps intellectual property secure while maintaining standard Composer workflows.

For teams working on sensitive applications, such as those described in my Laravel development services, relying on public registries is not an option. You need full control over who can access specific versions of your internal SDKs, shared components, or domain-specific logic. The following sections break down exactly how to architect, deploy, and maintain these systems in production environments using current 2026 tooling.

How do you configure Satis for PHP Composer private packages?

Satis is the original open-source tool for generating static Composer repositories. It scans your configured Git repositories, reads their composer.json files, and generates a packages.json index along with individual package metadata files. Because the output is purely static HTML and JSON, it can be served by Nginx or Apache with minimal overhead, making it ideal for high-read, low-write environments where security and simplicity are paramount.

Private Git Reposinternal/auth-sdkshared/legal-utilsecommerce/coreSatis Builder(Cron / CI Job)Scans Tags & BranchesStatic Web Serverpackages.jsonp/internal/auth-sdk.jsondist/*.zip (Archives)
Satis workflow: Git repositories are scanned periodically to generate static metadata consumed by Composer clients

Creating the satis.json configuration

Your satis.json file defines which repositories to scan and how to output the metadata. For a typical legal-tech or eCommerce project sharing internal libraries, you will list each private Git URL explicitly. Avoid using wildcards in production; explicit lists prevent accidental inclusion of test repositories.

{
    "name": "My Organization Private Repo",
    "homepage": "https://packages.example.com",
    "repositories": [
        { "type": "vcs", "url": "git@gitlab.example.com:internal/auth-sdk.git" },
        { "type": "vcs", "url": "git@gitlab.example.com:shared/legal-utils.git" }
    ],
    "require-all": true,
    "archive": {
        "directory": "dist",
        "format": "zip",
        "prefix-url": "https://packages.example.com"
    },
    "config": {
        "secure-http": true
    }
}

The archive section is critical. Without it, Composer clones the entire Git repository for every install, which is slow and exposes your full Git history to every developer machine. Enabling archives tells Satis to create zip snapshots of each tagged release, allowing Composer to download only the necessary source code via HTTP.

Automating builds with cron or CI

Satis does not run as a daemon. You must trigger rebuilds when code changes. In my experience managing deployments for sister sites like notarykathmandu.com and translationnepal.com, tying the Satis build to your CI pipeline ensures metadata stays synchronized with releases. Alternatively, a simple cron job running every five minutes works for smaller teams.

# Run Satis build every 5 minutes
*/5 * * * * www-data /usr/bin/php /var/www/satis/bin/satis build /var/www/satis/satis.json /var/www/html/public --no-interaction 2>&1 | logger -t satis-build

Ensure the user running the build (often www-data or a dedicated satis user) has SSH keys configured with read-only access to your private Git repositories. Permission errors here are the most common cause of silent failures in PHP Composer private packages via Satis and Repman setups.

What advantages does Repman offer over Satis for private registries?

While Satis handles metadata generation excellently, it lacks user management, a web interface, and proxy caching. Repman (Repository Manager) fills these gaps by providing a full-featured application layer on top of Composer distribution. For agencies or product teams managing multiple client projects with different access levels, Repman reduces operational friction significantly.

FeatureSatisRepman
Metadata GenerationStatic JSON (Fast)Dynamic + Cached
User AuthenticationWeb Server Level (Basic/Auth)Built-in Token/OAuth/LDAP
Proxy/CachingNoYes (Packagist Mirror)
Web UISimple ListFull Dashboard
Organization/Team MgmtNoYes
Setup ComplexityLowMedium (Docker/PHP App)
Best ForSingle-team, Read-heavyMulti-team, Agency, Enterprise

Repman’s proxy feature is particularly valuable in regions with inconsistent internet connectivity, a reality I frequently navigate when deploying infrastructure in Nepal. By caching public Packagist packages locally, Repman ensures that composer install succeeds even if the upstream registry is slow or blocked, while still enforcing authentication for your private packages.

Developercomposer require(Bearer Token)Repman ServerAuth & ACL LayerPrivate Package IndexPackagist Proxy CachePrivate GitGitLab / GiteaPublic PackagistUpstream Mirror
Repman acts as a unified gateway, authenticating requests before routing to private Git sources or cached public mirrors

How do you authenticate Composer against a private repository?

Authentication is where most implementations fail. Composer needs credentials to fetch both the metadata index and the actual zip archives. For PHP Composer private packages via Satis and Repman, you should avoid embedding passwords in composer.json. Instead, use token-based authentication stored in the global auth.json or environment variables.

Configuring auth.json for CI and developers

When using Repman, each user or CI runner receives a unique API token. This allows granular revocation without rotating shared secrets. Add the repository and token to your project’s auth.json (never commit this file) or the global ~/.composer/auth.json.

{
    "bearer": {
        "packages.example.com": "repman-token-xxxxxxxxxxxx"
    }
}

For Satis without Repman, authentication typically happens at the web server level using HTTP Basic Auth. In this case, store credentials under the http-basic key. If you are integrating this into a broader deployment workflow, consider reading my guide on CI/CD pipeline setup for patterns on injecting these secrets securely during automated builds.

Handling archive authentication

A frequent pitfall is securing the metadata but leaving the /dist directory open. Since Satis generates static zip files, your Nginx or Apache configuration must enforce the same authentication rules on the archive path. If using Repman, this is handled automatically by the application router. For pure Satis, ensure your web server config protects the entire document root, not just packages.json.

What are the best practices for maintaining private Composer repositories in 2026?

Running a private registry is an ongoing operational responsibility. Neglecting maintenance leads to stale metadata, broken deploys, and security vulnerabilities. Based on years of maintaining production PHP systems, these practices prevent common failures.

  1. Enforce HTTPS Everywhere: Composer 2.x refuses to connect to non-HTTPS repositories by default. Ensure valid TLS certificates via Let’s Encrypt. Self-signed certificates cause intermittent failures in CI environments that are difficult to debug.
  2. Tag Releases Religiously: Both Satis and Repman rely on Git tags to generate stable package versions. Branch-based installs (dev-main) are slower and less predictable. Enforce tagging policies in your team’s workflow, especially for shared libraries used across multiple client projects.
  3. Monitor Disk Space: Archive directories grow indefinitely. Implement a cleanup strategy that retains only the last N versions of each package. For Repman, configure retention policies in the admin panel. For Satis, add a post-build script to prune old zips.
  4. Separate Metadata from Source: Never serve your private registry from the same server hosting your Git repositories. Compromise of the registry should not grant access to source code history. Use object storage (S3/MinIO) for archives when possible.
  5. Test Restores Quarterly: Verify that a fresh composer install works with only the credentials and repository URL. Documentation rot is real; automated smoke tests catch configuration drift before it blocks production deployments.
Start: Need Private Repo?Multiple Teams / User Mgmt?NoYesNeed Packagist Proxy?Use RepmanFull Features + UINoYesUse SatisLightweight StaticUse RepmanProxy Benefit
Decision tree for selecting the appropriate private Composer registry solution based on organizational needs

How do you troubleshoot common Composer private repository failures?

Even well-configured systems break. When composer update fails against your private registry, systematic diagnosis saves hours. These are the issues I encounter most frequently when auditing client infrastructure or supporting teams adopting PHP Composer private packages via Satis and Repman.

Version resolution conflicts

If Composer reports that a package "could not be found in any version," verify that the tag exists and follows semantic versioning. Satis ignores malformed tags. Check the generated p/vendor/package.json file directly on the server to confirm the version appears. If missing, manually trigger a rebuild and inspect the build logs for Git fetch errors.

Authentication loops and 401 errors

A 401 error during archive download usually means the web server protecting /dist has different credentials than the metadata endpoint. Ensure both paths share the same auth configuration. For Repman, regenerate the API token; tokens can expire or be revoked. Clear Composer’s cache (composer clear-cache) to remove cached 401 responses.

SSL certificate verification failures

In local development environments or misconfigured CI runners, you may see SSL certificate problem: unable to get local issuer certificate. Never disable SSL verification in production. Instead, update the system CA bundle or specify the correct certificate path in COMPOSER_CAFILE. This is especially relevant when using internal CAs for corporate registries.

Stale metadata after new releases

If a new tag isn’t appearing, check the cron schedule or CI trigger. For Satis, verify the build user still has SSH access to the repository. For Repman, check the webhook configuration; missed webhooks mean delayed updates until the next scheduled sync. Monitoring build success rates prevents silent staleness.

Implementing Secure PHP Composer Private Packages via Satis and Repman

Establishing a self-hosted Composer registry is a foundational step for any organization serious about code reuse and security. Whether you choose the simplicity of Satis or the feature richness of Repman, the key is treating the registry as production infrastructure with proper monitoring, backups, and access controls. For teams in Nepal or globally, this eliminates dependency on external services while keeping proprietary logic contained. If you need assistance architecting secure package distribution for your Laravel or Symfony applications, contact me to discuss your specific requirements.

Frequently Asked Questions

Satis is a static repository generator requiring cron jobs to rebuild metadata, while Repman is a dynamic proxy and registry with a web UI. I choose Satis for simple, low-maintenance setups on basic VPS hosting, but prefer Repman when teams need package analytics, vulnerability scanning, or fallback proxies for Packagist without managing build scripts.

Add your credentials to auth.json using composer config --global --auth http-basic.satis.example.com username password. Never store these in version control. In my experience deploying Laravel applications via Deployer 7, I inject this file during deployment or use environment-specific auth.json files to keep production credentials separate from local development environments securely.

Yes, Repman offers a free open-source edition for self-hosting that supports unlimited private packages and users. The paid cloud version starts around USD 29/month (approx. NPR 3,800). For most Nepal-based agencies or small teams, the self-hosted Docker version provides sufficient functionality without recurring costs, provided you have basic Linux administration skills.

This usually happens because the satis.json configuration lacks the correct VCS URL or the cron job has not rebuilt the packages.json metadata file. Verify your repository URLs are accessible by the server running Satis. On production servers, I have also seen SSH key permission issues prevent git cloning during rebuilds, causing silent failures that leave the index stale.

Technically yes, but it forces every developer and CI runner to authenticate against GitHub API, hitting rate limits quickly. Satis or Repman acts as a mirror, fetching once and serving locally. For projects like legal-tech portals where multiple developers deploy frequently, a private registry eliminates redundant API calls and decouples package availability from external provider uptime.

Run bin/satis build every five to fifteen minutes for active development teams. For stable maintenance-only projects, hourly or daily suffices. In practice, I set five-minute intervals on GitLab CI runners or server crons for active Laravel eCommerce builds. Avoid running it every minute; git cloning and metadata generation consume CPU and can overlap if repositories are large.

Yes, Repman includes a built-in proxy feature that caches public Packagist packages on first request. This accelerates installs on slow connections common in parts of Nepal and ensures builds continue if Packagist goes offline. Unlike Satis, which requires explicit configuration to mirror public deps, Repman handles this transparently, reducing storage overhead through lazy caching.

Both require PHP 8.2 minimum as of their current stable releases in 2026. Satis 3.x and Repman 2.x align with Laravel 12 and Symfony 7 baselines. When provisioning Ubuntu 24.04 servers, I install PHP 8.3 or 8.4 via Ondřej Surý PPA to ensure compatibility. Running older PHP versions will cause dependency resolution failures during installation.

Place Satis behind Nginx with HTTP Basic Auth or IP whitelisting, enforce HTTPS via Let's Encrypt, and restrict filesystem permissions on the output directory. Never expose the git repositories directly. On client projects, I additionally configure fail2ban to block brute-force attempts on the auth endpoint and disable directory listing to prevent metadata enumeration attacks.

Satis treats each git repository as one package. For monorepos, you must either split packages into separate repos or use a tool like monorepo-builder to tag sub-packages individually before Satis indexes them. In my experience, maintaining true separate repositories per package is simpler for Satis than trying to automate monorepo splitting within the rebuild pipeline reliably.

A fresh Satis mirror of fifty private packages uses under 500MB. Repman with Packagist proxy cache grows over time; expect 2-5GB after six months of active use. Monitor disk usage on smaller VPS instances. I routinely add logrotate rules and cache cleanup commands to maintenance scripts for Repman deployments to prevent /var/lib/repman from filling root partitions unexpectedly.

Repman can import Satis configurations via its CLI or UI wizard. Point it to your existing satis.json and git repositories; Repman will re-index them dynamically. Existing composer.lock files remain valid since package names and versions do not change. Update only the repository URL in composer.json. Test thoroughly in staging before switching production Laravel deployments to avoid downtime.

Significantly. Local mirrors eliminate repeated git clones and Packagist API calls. On GitLab CI runners sharing a Repman cache, I have observed install times drop from forty-five seconds to under ten for medium Laravel projects. Configure your CI runner to use the private registry as primary and Packagist as fallback only. Persist the Composer cache directory between pipeline runs for maximum benefit.

Deployments fail immediately unless you configured a fallback repository in composer.json. Always list Packagist after your private Satis entry so public dependencies resolve even if Satis is unreachable. For critical systems, I replicate Satis output to a secondary location or use Repman's high-availability mode. Relying on a single point of failure for package resolution is unacceptable in production environments.

GitLab Package Registry and GitHub Packages offer integrated Composer support without separate infrastructure. However, they vendor-lock you to their ecosystem and lack advanced features like vulnerability scanning or Packagist proxying. For Nepal-based clients wanting data sovereignty and platform independence, self-hosted Satis or Repman remains preferable despite higher initial setup effort compared to managed solutions.

Share this article

Quick Contact Options
Choose how you want to connect me: