Skip to content
daniel@cosenza:~/blog
FreeBSDHow-To March 10, 2026 2 min read

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.