Build the pigsty-infra Repository
pgsty/infra-pkg is the upstream build source for Pigsty Infra packages.
This tutorial assumes the dual-architecture RPMs and DEBs already exist and covers the second half of the job: turning that pile of packages into a real, consumable, maintainable SOW repository named infra.
1. Put the packages under ~/repo
This tutorial uses the fixed path ~/repo throughout. Copy the existing packages into two input directories:
Confirm that all four format-by-architecture cells contain real payloads:
All four results must be greater than zero. At this point, the tree is only an input package pool:
2. Create the infra Repository and two Dists
Initialize a Workspace and create the Repository named infra:
The model is now fixed:
Open ~/repo/sow.yml and reduce it to this configuration:
limit: 1 keeps only the newest version for each package name and native architecture. The rpm and deb
Dists are therefore rolling latest channels while still retaining both x86-64 and ARM64.
3. Ingest once and build once
Update Desired Membership first, then build a single time:
Initialization is complete only when sow check returns 0. Verify the formats and architectures that SOW
read from the package headers:
The output must include at least:
SOW reports canonical architecture names, so DEB amd64/arm64 appear here as x86_64/aarch64.
4. Read the generated filesystem tree
Print the actual directories:
The important structure is:
One path rule is easy to miss and essential to remember: SOW always places Dists below dists/. The logical
infra/rpm and infra/deb channels are therefore served from /infra/dists/rpm/ and /infra/dists/deb/,
while package payloads live in /infra/pool/. Always publish or mount the complete ~/repo/infra tree, never
an individual Dist.
5. Serve the repository read-only with Nginx
Use the official nginx:alpine image and mount the Repository Root read-only at /infra:
Check both metadata entry points directly:
Nginx can see only ~/repo/infra; it cannot see sow.yml or .sow/, and it cannot modify the repository.
6. Install an RPM from only infra on EL9
The Rocky Linux 9 container below is attached to the --internal network. The script removes every preconfigured
repository and enables only infra, so a successful installation cannot fall back to a public mirror.
An RPM baseurl must point to a concrete architecture view. dnf expands $basearch to x86_64 or aarch64.
7. Install a DEB from only infra on Ubuntu 24.04
APT points URIs at the Repository Root and uses the Dist name deb as Suites:
This isolated HTTP lab temporarily disables verification. A production service should sign RPM and APT metadata
and remove both gpgcheck=0 and Trusted: yes.
Docker validates the host architecture by default. To run the complete four-cell matrix, repeat each docker run
once with --platform linux/amd64 and once with --platform linux/arm64; cross-architecture execution requires
Docker binfmt/QEMU support. Repository inventory and client installation are separate acceptance gates.
8. Routine maintenance: add a new version
The normal update operation is add, not removing the old package first. Suppose the four new pg-exporter
payloads are ready:
Because the latest Dists have limit: 1, the new version wins and the old version automatically leaves that
Dist’s Desired Membership. The old bytes are not deleted immediately, and relaxing policy later does not make
the old membership reappear automatically.
Rerun the EL9 and Ubuntu clients from sections 6 and 7, refresh metadata, then install or upgrade to complete the update acceptance test.
Do not begin a normal release with sow rm. If a bad package must be withdrawn, use
sow ls -r infra -d rpm --json (or the corresponding -d deb) to find its exact SHA-256, run
sow rm sha256:... -r infra -d rpm --check, inspect the plan, then run the same command without
--check. rm removes only Dist Membership; conservative sow gc handles pool bytes separately. Avoid a bare
package name that could remove every version and architecture.
9. Two retention layers: latest and stable
limit: 1 makes rpm and deb good rolling channels but cannot express “keep every formally released version.”
Create two more Dists for that purpose:
The new Dists default to limit: 0, which means retain every version. The resulting policy is:
| Dist | Format | limit |
Role |
|---|---|---|---|
rpm |
RPM | 1 | RPM latest |
deb |
DEB | 1 | DEB latest |
rpm-stable |
RPM | 0 | Accumulate promoted RPMs |
deb-stable |
DEB | 0 | Accumulate promoted DEBs |
Stable does not automatically resurrect every historical object that happens to remain in the pool. It starts accumulating versions that you explicitly promote from this point forward.
10. Promote latest into stable
SOW does not expose a dedicated promote command. The reliable current procedure is to freeze writers,
export the exact source Dist Membership, and add those objects to the target Dist. Inputs come directly from
infra/pool; SOW verifies and reuses each existing Package Object without repackaging it or storing a second copy.
First require clean source state and save the promotion manifests:
Pause writes to rpm and deb until promotion finishes, then reuse the pool objects:
Each add should report reused. If the loop stops midway, the source Dists are unchanged; correct the failure
and rerun the same manifest. Over subsequent promotions, rpm/deb retain only the latest version while
rpm-stable/deb-stable accumulate release history.
11. Take the 2026-08 snapshot from stable
A client-visible monthly snapshot is another pair of Dists:
Pause stable writes during the snapshot window and first persist its exact Membership as manifests:
Add those manifests to the corresponding snapshot Dists:
Also retain the complete verified Repository Generation so later GC treats it as a safety root:
retain protects a whole Repository Generation for recovery and GC; the fixed client-visible URLs are still
provided by the rpm-202608 and deb-202608 Dists. SOW does not enforce Dist immutability, so never running
add or rm against snapshot Dists after creation is part of the operating contract.
12. Client address map
One Nginx service and one shared infra/pool support every channel:
| Channel | dnf baseurl |
APT URIs / Suites |
|---|---|---|
| latest | http://infra-nginx/infra/dists/rpm/$basearch/ |
http://infra-nginx/infra / deb |
| stable | http://infra-nginx/infra/dists/rpm-stable/$basearch/ |
http://infra-nginx/infra / deb-stable |
| 2026-08 | http://infra-nginx/infra/dists/rpm-202608/$basearch/ |
http://infra-nginx/infra / deb-202608 |
Run the final acceptance checks:
The result is not a disposable demo directory. It is a real Infra Repository that can continue ingesting packages,
promoting releases, and producing monthly snapshots: rpm/deb move quickly, rpm-stable/deb-stable accumulate
formal history, monthly Dists provide fixed endpoints, and every view reuses the same immutable package objects.
Stop the temporary service when the lab is complete: