MAKEPKG(5) File Formats Manual MAKEPKG(5)

MAKEPKG — package build recipe

/ports/collection/port/MAKEPKG /usr/ports/collection/port/MAKEPKG

A MAKEPKG file describes how to fetch, build and package one piece of software. It is read by mkpkg(8), which parses it itself rather than running it as a script: it looks like a shell script, but only the forms described here are understood.

The first line must be exactly

#!/bin/mkpkg

Empty lines and lines starting with ‘#’ are ignored. By convention the file starts with ‘# description:’ and ‘# url:’ comments, and ends with a () block and a # vim: filetype=sh line.

A variable is set with name=value. Quotes around the value and a trailing ‘#’ comment are removed.

name
The package name. Required.
version
The upstream version. Required.
release
The package release, raised when the recipe changes but version does not. Required.
_custom
Any variable whose name starts with ‘_’, such as _commit.

Other variables are ignored. All of the above are exported to the hooks. In source and renames, $var and ${var} are replaced by their values, as far as they are set earlier in the file.

An array is set with name=(value...), and may continue over several lines up to the closing parenthesis. Values are separated by white space and may be quoted. source and sha256sums also accept name+=(value).

source
The sources. A URL starting with http://, https:// or ftp:// is downloaded into the source directory, under the last component of the URL. Any other entry is a file in the port directory.

Archives ending in .tar, .tar.gz, .tgz, .tar.bz2, .tbz2, .tar.xz, .txz, .tar.lz, .tar.lzma, .tar.zst, .tzst or .tar.Z are unpacked into SRC; other files, including .zip archives, are copied there.

renames
Names to save the downloads under, by position: entry n applies to entry n of source. An empty entry, "" or '', keeps the original name, so only the first of three sources is renamed with
renames=($name-$version.tar.gz "" "")
The entry SKIP leaves that source out: it is neither downloaded nor unpacked.
sha256sums
The SHA256 of each source, by position. SKIP disables the check for that entry. Print a new array with mkpkg -c, or rewrite it in place with mkpkg -uc.
depends
Packages needed at run time.
makedeps
Packages needed only to build.

Both are read by pkg(8), and are what a build in the lockbox may read; see lockbox(7).

provides
Other names this package satisfies as a dependency, such as ninja for samurai. Read only by pkg(8).
variant
A one-line description that makes a port name-suffix a variant of the port name, offered by pkg add name. Read only by pkg(8).
lockbox
Build in the lockbox. yes, on or 1 select the full lockbox; no and off do nothing. Any other value is a command to run through lbexec(8) wherever a line of () starts with it. The forms may be combined.
groups
System groups and users to create before the package's files are installed. An entry is one of:
group
A system group.
group:gid
A system group with a fixed GID.
group:user:user:home:mode[:uid:gid]
A system group, and a system user in it with shell /bin/false and home directory home, which is created with mode, owned by the user. The second field is the literal word user. With both uid and gid, the IDs are fixed, so they are the same on every machine. No home directory is created for /, /dev/null, /nonexistent or /var/empty.

Groups of the base system, such as wheel or video, are never created or removed. When the package is removed, its user is deleted, and its group too, unless another installed package declares the same group.

services
runit services to enable when the package is installed: a link /service/name to /etc/sv/name is made, unless something by that name exists. The service directory itself must be part of the package. When the package is removed, the link is removed and the service stopped.
permissions
Owner, group and mode to set after installing, as path:owner:group:mode, with an octal mode. The mode of a symbolic link is not changed.
capabilities
File capabilities to set after installing, as path:capabilities, using setcap(8). Capabilities are extended attributes, which do not survive in the package archive, so they are set at install time.

Paths in permissions and capabilities are relative to the installation root.

A hook is written like a shell function:

build() {
    ...
}

The opening brace may also be on a line of its own. Each line of the body is taken with its leading white space removed, so a here-document cannot rely on indentation. The body ends at the matching closing brace; braces inside quotes and after a ‘#’ do not count.

Each hook runs as a separate sh(1) script with set -e, after the variables in mkpkg(8) ENVIRONMENT are exported. Some hooks first source /etc/mkpkg.conf.

()
A shell() before build() in the file runs before the build: after the sources are downloaded and verified, before the work directory is set up. If it fails, nothing is built. A shell() after build() runs after the package is made, checked and signed, or as soon as a step fails, the first shell() included, so it can undo the setup either way. Both run in PKGMK_SOURCE_DIR and source /etc/mkpkg.conf. Typical use is to create wrappers the build needs and to remove them again.
()
Unpacks the sources itself; without it, mkpkg(8) unpacks them as described under source. Runs in PKGMK_SOURCE_DIR, without /etc/mkpkg.conf.
()
Applies patches. Runs in SRC, without /etc/mkpkg.conf. Patch files listed in source have been copied into SRC.
build()
Builds and installs into PKG. Required. Runs in SRC and sources /etc/mkpkg.conf.
()
Adds to or cleans up the package, such as runit service files. Runs in SRC after build() and sources /etc/mkpkg.conf.

signify() holds the signify(1) public key of the key the port was signed with. It is not a hook: its body is the two lines of the public key file.

signify() {
    untrusted comment: signify public key
    RWQ...
}

The key only names the signer. It is trusted only if the same key is in a .pub file in /etc/ports/keys/; see mkpkg(8) SIGNATURE VERIFICATION. Write or replace the block with mkpkg -up, print it with mkpkg -p.

If any of groups, services, permissions or capabilities is set, mkpkg(8) adds the file var/lib/pkg/meta/name to the package, one line per entry:

group dhcpcd:user:dhcpcd:/var/lib/dhcpcd:700
service dhcpcd
permission /sbin/dhcpcd:root:dhcpcd:750
capability /usr/bin/ping:cap_net_raw+ep

It is read by addpkg(8) and delpkg(8).

A minimal recipe:

#!/bin/mkpkg
# description: Shows the full path of shell commands
# url: https://www.gnu.org/software/which/

name=which
version=2.21
release=1
source=(https://ftp.gnu.org/gnu/$name/$name-$version.tar.gz)
sha256sums=(...)

build() {
    cd $name-$version
    ./configure --prefix=/
    make
    make DESTDIR=$PKG install
}

signify() {
    untrusted comment: signify public key
    RWQ...
}

# vim: filetype=sh

A daemon with its own user and a runit service:

#!/bin/mkpkg
# description: DHCP client daemon
# url: https://github.com/NetworkConfiguration/dhcpcd

name=dhcpcd
version=10.2.2
release=1
depends=(openssl)
groups=(dhcpcd:user:dhcpcd:/var/lib/dhcpcd:700)
services=(dhcpcd)
permissions=(/sbin/dhcpcd:root:dhcpcd:750)
source=(https://github.com/NetworkConfiguration/$name/releases/download/v$version/$name-$version.tar.xz)
sha256sums=(...)

build() {
    cd $name-$version
    ./configure --prefix=/ --privsepuser=dhcpcd
    make
    make DESTDIR=$PKG install
}

post_build() {
    install -d $PKG/etc/sv/$name
    printf '#!/bin/sh\nexec 2>&1\nexec dhcpcd -B\n' > $PKG/etc/sv/$name/run
    chmod 755 $PKG/etc/sv/$name/run
}

signify() {
    untrusted comment: signify public key
    RWQ...
}

# vim: filetype=sh

signify(1), mkpkg.conf(5), lockbox(7), addpkg(8), delpkg(8), lbexec(8), mkpkg(8), pkg(8)

Entries beyond these limits are ignored without a warning: 256 sources and checksums, 64 entries each in depends and makedeps, 16 each in groups, services, permissions, capabilities and the commands of lockbox, 32 custom variables and 512 lines per hook.

September 27, 2026 mkpkg 1.6.0