<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: otcommerce</title>
    <description>The latest articles on DEV Community by otcommerce (@otcommerce).</description>
    <link>https://dev.to/otcommerce</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4113456%2Ff8556297-ce7f-4083-b939-c6a05cc2570b.png</url>
      <title>DEV Community: otcommerce</title>
      <link>https://dev.to/otcommerce</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/otcommerce"/>
    <language>en</language>
    <item>
      <title>How We Simplified Private Composer Packages at OT Commerce with Satis</title>
      <dc:creator>otcommerce</dc:creator>
      <pubDate>Mon, 07 Sep 2026 08:15:34 +0000</pubDate>
      <link>https://dev.to/otcommerce/how-we-simplified-private-composer-packages-at-ot-commerce-with-satis-36a</link>
      <guid>https://dev.to/otcommerce/how-we-simplified-private-composer-packages-at-ot-commerce-with-satis-36a</guid>
      <description>&lt;p&gt;At &lt;a href="https://otcommerce.com/" rel="noopener noreferrer"&gt;OT Commerce&lt;/a&gt;, we maintain a large PHP ecosystem consisting of multiple services and dozens of internal Composer packages.&lt;/p&gt;

&lt;p&gt;Most of these packages are stored in private Bitbucket repositories.&lt;/p&gt;

&lt;p&gt;For a long time, our applications accessed them directly through Composer:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
    "repositories": [&lt;br&gt;
        {&lt;br&gt;
            "type": "git",&lt;br&gt;
            "url": "&lt;a href="mailto:git@bitbucket.org"&gt;git@bitbucket.org&lt;/a&gt;:company/internal-package.git"&lt;br&gt;
        }&lt;br&gt;
    ]&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;This works well when you have a few packages.&lt;/p&gt;

&lt;p&gt;But as our infrastructure grew, every project accumulated a long list of private repositories. Developer machines, Docker builds and CI/CD agents also needed SSH access to Bitbucket just to install PHP dependencies.&lt;/p&gt;

&lt;p&gt;We decided to simplify this architecture by introducing a private Composer repository based on Satis.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Architecture
&lt;/h2&gt;

&lt;p&gt;The package distribution flow at OT Commerce now looks like this:&lt;/p&gt;

&lt;p&gt;Bitbucket&lt;br&gt;
    ↓ SSH&lt;br&gt;
Satis&lt;br&gt;
    ↓&lt;br&gt;
Composer metadata + ZIP archives&lt;br&gt;
    ↓&lt;br&gt;
Nginx&lt;br&gt;
    ↓ HTTPS + Basic Auth&lt;br&gt;
Composer&lt;/p&gt;

&lt;p&gt;Satis is deployed as a service in our Docker Swarm and periodically scans our private repositories, generates Composer metadata and builds dist archives for available package versions.&lt;/p&gt;

&lt;p&gt;We also use Bitbucket webhooks to trigger builds immediately when packages are updated, while periodic builds remain as a fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Persistent Storage
&lt;/h2&gt;

&lt;p&gt;Two persistent volumes are used by the service.&lt;/p&gt;

&lt;p&gt;One stores the generated Composer repository and package archives.&lt;/p&gt;

&lt;p&gt;The other stores the VCS cache, preventing Satis from cloning dozens of repositories from scratch whenever the container is recreated.&lt;/p&gt;

&lt;p&gt;This makes repository rebuilds significantly faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTPS and Authentication
&lt;/h2&gt;

&lt;p&gt;The generated repository is served through Nginx and protected with HTTPS and Basic Authentication.&lt;/p&gt;

&lt;p&gt;TLS certificates are issued and automatically renewed using Let's Encrypt with an AWS Route53 DNS-01 challenge.&lt;/p&gt;

&lt;p&gt;This means we don't need to expose port 80 just for certificate validation.&lt;/p&gt;

&lt;p&gt;Composer credentials are stored separately in auth.json, so application repositories contain neither Bitbucket SSH credentials nor private Composer repository passwords.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Repository Instead of Dozens
&lt;/h2&gt;

&lt;p&gt;The biggest visible change for our PHP projects is very simple.&lt;/p&gt;

&lt;p&gt;Instead of maintaining dozens of VCS declarations:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
    "type": "git",&lt;br&gt;
    "url": "&lt;a href="mailto:git@bitbucket.org"&gt;git@bitbucket.org&lt;/a&gt;:company/internal-package.git"&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;applications now use a single repository:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
    "repositories": [&lt;br&gt;
        {&lt;br&gt;
            "type": "composer",&lt;br&gt;
            "url": "&lt;a href="https://packages.example.com" rel="noopener noreferrer"&gt;https://packages.example.com&lt;/a&gt;"&lt;br&gt;
        }&lt;br&gt;
    ]&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;The responsibility is now clearly separated:&lt;/p&gt;

&lt;p&gt;Satis → knows how to access source code&lt;br&gt;
Composer → knows how to access packages&lt;/p&gt;

&lt;p&gt;Developer machines, Docker builds and CI/CD agents no longer need direct SSH access to every private Bitbucket repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Made This Change
&lt;/h2&gt;

&lt;p&gt;OT Commerce has been developed for many years, and our PHP infrastructure has grown together with the platform.&lt;/p&gt;

&lt;p&gt;Small infrastructure decisions that work perfectly with five packages don't necessarily scale well when there are dozens of packages and multiple services consuming them.&lt;/p&gt;

&lt;p&gt;Moving package distribution behind Satis was not a major architectural rewrite. It was a relatively small change that gave us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simpler composer.json files;&lt;/li&gt;
&lt;li&gt;fewer distributed SSH credentials;&lt;/li&gt;
&lt;li&gt;cleaner Docker builds;&lt;/li&gt;
&lt;li&gt;simpler CI/CD configuration;&lt;/li&gt;
&lt;li&gt;faster dependency installation through dist archives;&lt;/li&gt;
&lt;li&gt;centralized package distribution;&lt;/li&gt;
&lt;li&gt;clearer security boundaries between source code and package consumers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes improving infrastructure isn't about introducing a more complex technology.&lt;/p&gt;

&lt;p&gt;It's about removing unnecessary connections between existing components.&lt;/p&gt;

&lt;p&gt;For us, Satis provided exactly that separation.&lt;/p&gt;

&lt;h1&gt;
  
  
  php #composer #devops #docker
&lt;/h1&gt;

</description>
      <category>architecture</category>
      <category>devops</category>
      <category>infrastructure</category>
      <category>php</category>
    </item>
  </channel>
</rss>
