How to Build and Host a Custom pkg Repository
A complete walkthrough building your own signed FreeBSD package repository — useful for internal packages, pinned versions, or a local mirror.
Beyond the official FreeBSD package repositories, pkg fully supports hosting your own repository — useful for distributing internally-built packages, pinning specific package versions, or mirroring the official set for faster local access.
Step 1: build the packages you want to distribute
poudriere bulk -j myjail -p myports -f pkglist.txt
Poudriere (a dedicated bulk ports-building tool) or a manual pkg create run against locally-built ports both produce the .pkg files a repository is ultimately made of — this walkthrough assumes you already have a directory of built packages.
Step 2: generate a signing key
openssl genrsa -out /usr/local/etc/pkg/repo-priv.key 4096
openssl rsa -in /usr/local/etc/pkg/repo-priv.key -pubout -out /usr/local/etc/pkg/repo-pub.key
Signing your repository lets client machines verify packages haven’t been tampered with in transit — the same trust model the official repositories use.
Step 3: build the repository metadata
pkg repo /usr/local/www/pkgrepo /usr/local/etc/pkg/repo-priv.key
This scans the directory of .pkg files, builds the repository catalog (package names, versions, dependencies), and signs it with the private key.
Step 4: serve the repository directory over HTTP
# using the base system's built-in webserver, or nginx/apache:
python3 -m http.server 8080 --directory /usr/local/www/pkgrepo
Any ordinary web server works — pkg just needs to be able to fetch the repository’s catalog and package files over HTTP or HTTPS.
Step 5: configure client machines to use the new repository
# /usr/local/etc/pkg/repos/mycompany.conf
mycompany: {
url: "http://pkg-server.internal:8080",
signature_type: "pubkey",
pubkey: "/usr/local/etc/pkg/repo-pub.key",
enabled: yes
}
The client needs a copy of the public key to verify signatures — never distribute the private signing key to client machines.
Step 6: optionally disable the default FreeBSD repository
# /usr/local/etc/pkg/repos/FreeBSD.conf
FreeBSD: { enabled: no }
Disabling the default repository is appropriate when you want clients to pull exclusively from your internal repository (for strict version pinning or an air-gapped environment); leave it enabled if you just want to supplement the official packages with your own additions.
Step 7: verify clients can actually reach and trust the new repository
pkg -R /usr/local/etc/pkg/repos update
pkg search <a-package-you-built>
Step 8: automate rebuilding the repository as packages change
# a simple cron entry, or a CI job:
0 3 * * * pkg repo /usr/local/www/pkgrepo /usr/local/etc/pkg/repo-priv.key
Re-running pkg repo regenerates the catalog to reflect any packages added or removed since the last build — worth automating rather than remembering to run manually every time the package set changes.
Why signing matters even for an internal-only repository
It’s tempting to skip signing for a repository that never leaves an internal network, but doing so means client machines have no way to detect a corrupted download or a compromised repository server — the same class of protection code signing provides elsewhere. Setting up signing costs one extra step at repository-creation time and pays for itself the first time it catches a problem you’d otherwise have no way to detect.