PkgOps · the packaging companion
Build. Sign. Ship. No FreeBSD builder.
Write 30 lines of YAML, run one command, and get a signed FreeBSD package repository that stock pkg understands. PkgOps cross-compiles your Go project on Linux or macOS (or on a GitHub Actions runner), writes the same .pkg files and catalogs FreeBSD's own tools write, and leaves installs and upgrades to the pkg your servers already have.
Why PkgOps
No builder
Go cross-compiles for FreeBSD with
CGO_ENABLED=0. No ports tree, no build jail, no FreeBSD machine in your CI.Native and signed
Real
.pkgfiles,pkg-compatible catalogs, RSA signatures. Tampered catalogs and packages are refused by stockpkg.Reproducible
Two builds of the same inputs are byte-identical, binary included, so a package is something you can audit and re-create.
Static hosting
A repository is a directory of files: nginx, any static host, GitHub, or the small optional registry that takes uploads from CI.
Two dependencies
Pure Go with zstd and YAML, shipped as a CLI and an importable Go package. It shares no code with JailOps.
Packages that behave
rc.d scripts, service users, owned directories and
@configfiles that survive upgrades, all from the manifest.
The right tool for each job. About 95% of packaging work is a small daemon, a CLI tool, some config and an rc.d script: that is PkgOps. The heavy 5%, such as large builds from the ports tree, software that needs CGO or native libraries, custom port options and bulk builds across jails, belongs to poudriere, and PkgOps does not try to replace it. The two coexist: a PkgOps repository is one more entry in /usr/local/etc/pkg/repos.
How it fits
From source to pkg upgrade.
Walkthrough
A Go daemon with an rc.d script.
$ git clone https://github.com/jailops/pkgops && cd pkgops $ go install ./cmd/pkgops $ pkgops key generate --out repo.key --bits 4096 $ pkgops build --abi FreeBSD:15:amd64 -o dist $ pkgops build --abi FreeBSD:15:aarch64 -o dist $ pkgops repo --repo site --key repo.key dist/*/*.pkg $ pkgops verify site/FreeBSD:15:amd64/latest --pubkey repo.pub $ pkgops publish --from site --to /var/www/pkg
name: hello
version: 0.1.0
comment: A tiny HTTP daemon
www: https://example.org
maintainer: you@example.org
license: BSD2CLAUSE
build: {type: go, package: ./cmd/hello}
files:
- {source: build/${ARCH}/hello, destination: bin/hello}
services:
- {name: hello, source: packaging/rc.d-hello}$ fetch -o /usr/local/etc/pkg/keys/hello.pub https://pkg.example.org/repo.pub $ pkgops conf --name hello --url 'https://pkg.example.org/${ABI}/latest' \ --pubkey /usr/local/etc/pkg/keys/hello.pub > /usr/local/etc/pkg/repos/hello.conf $ pkg update $ pkg install hello $ sysrc hello_enable=YES $ pkg upgrade # later, after a new release
The complete manifest reference, a runnable example and the format notes are in the PkgOps repository.
Hosting
Pick what fits.
- nginx or any static server.
pkgops publishcopies packages first and the catalog last, atomically, so clients never see a catalog that points at a missing package. - The optional registry.
pkgops serveserves the repository read-only and accepts uploads with scoped tokens:pkgops push --url ... --token ... dist/*/*.pkgfrom CI. Every upload is verified, re-indexed and re-signed, with retention. It speaks plain HTTP, so put TLS in front. A web UI for the registry is coming. - GitHub, no FreeBSD machine. A composite Action builds for both architectures, signs with a key kept in a secret and publishes to Pages or Releases.
With JailOps
One infrastructure story.
- JailOps itself ships as FreeBSD packages (
jailops-client,jailops,jailops-backupd) from a signed repository, with rc.d scripts andjailops-priv init. The public repository atpkg.jailops.netis not live yet. - Your own software follows the same path: build with PkgOps, publish a signed repository, and install it in a jail with
pkg install, or through a jail's provisioningpackageslist. - Jails stay clean: the OS comes from an image generation, your application from a package you built and signed.
Verified and next
Verified on FreeBSD 15.1 arm64 with pkg 2.8.4: pkg add, info, check and delete on PkgOps packages; pkg update, install and upgrade from a static repository, including dependencies and @config files; signature checks in both directions; users, groups and directories; pkgops serve and push running on FreeBSD. Not yet verified: amd64 packages on real amd64 hardware, publishing through GitHub Pages and Releases, and rc.d service start at boot. Limits: pure Go only (no CGO), amd64 and aarch64, FreeBSD 15 and newer. PkgOps is pre-1.0 software.