mkosi(1)
๐ mkosi โ Build Bespoke OS Images ๐
okay bestie, you've landed on mkosi(1) โ that's section 1, General Commands Manual, in case you were wondering. grab a snack, this one's a whole journey. โจ
๐ธ NAME ๐ธ
mkosi โ Build Bespoke OS Images. yes, with capital letters, it's that serious. ๐
๐ฆ SYNOPSIS ๐ฆ
like, here's every single way you can call her:
mkosi [options...] init
mkosi [options...] summary
mkosi [options...] cat-config
mkosi [options...] build [-- command line...]
mkosi [options...] shell [-- command line...]
mkosi [options...] boot [-- nspawn settings...]
mkosi [options...] vm [-- vmm parameters...]
mkosi [options...] ssh [-- command line...]
mkosi [options...] journalctl [-- command line...]
mkosi [options...] coredumpctl [-- command line...]
mkosi [options...] sysupdate [-- sysupdate settings...]
mkosi [options...] box [-- command line...]
mkosi [options...] dependencies [-- options...]
mkosi [options...] clean
mkosi [options...] serve
mkosi [options...] burn <device>
mkosi [options...] bump
mkosi [options...] genkey
mkosi [options...] documentation [manual]
mkosi [options...] completion [shell]
mkosi [options...] latest-snapshot
mkosi [options...] help
๐ DESCRIPTION ๐
so. mkosi is a tool for easily building customized OS images. that's the whole vibe. it's basically a fancy wrapper around dnf, apt, pacman and zypper that may generate disk images with a number of bells and whistles. like, she's got range. ๐ฅ
๐ Command Line Verbs ๐
these are the command line verbs we know and love. the main characters, if you will:
initโ Initialize mkosi. this is a one time operation that sets up various config files required for an optimal experience. currently this only initialized atmpfiles.ddropin for the mkosi package cache directory to make sure old, unused files are cleaned up automatically. lowkey housekeeping but she's doing the lord's work. ๐งนsummaryโ Show a human-readable summary of all options used for building the images. she'll parse the command line and configuration files, but only print what it is configured for and not actually build or run anything. it's giving window shopping. ๐๏ธcat-configโ Output the names and contents of all loaded configuration files. mkosi loads a bunch of files from different locations and this command makes it easier to figure out what is configured where. genuinely such a lifesaver, no cap. ๐ซถbuildโ Build the image-based on the settings passed on the command line and in the configuration files. this command is the default if no verb is specified. Arguments may be passed to the build scripts, if some are defined. To pass options to the build scripts, separate them from regular mkosi options with--. yeah the--thing is gonna come up a LOT, get comfy. ๐shellโ This builds the image if it is not built yet, and then invokessystemd-nspawnto run an interactive shell in the image. This doesn't require booting the system, it's like a betterchroot. An optional command line may be specified after the shell verb, to be invoked in place of the shell in the container. To pass extra options to nspawn, separate them from regular options with--. To use directory images with shell, theForeignUIDRange=option has to be enabled when building the image. noted. ๐bootโ Similar to shell, but instead of spawning a shell, it bootssystemdin the image usingsystemd-nspawn. Extra arguments may be specified after the boot verb, which are passed as the kernel command line to the init system in the image. To pass extra options to nspawn, separate them from regular options with--. To use directory images with boot, theForeignUIDRange=option has to be enabled when building the image. To use disk images with boot,systemd-nsresourcedandsystemd-mountfsdv260 or newer are required on the host. so like, check your versions bestie. ๐ซกvmโ Similar to boot, but uses the configured virtual machine monitor (by defaultqemu) to boot up the image, i.e. instead of container virtualization, virtual machine virtualization is used. How extra command line arguments are interpreted depends on the configured virtual machine monitor. SeeVirtualMachineMonitor=for more information. To pass extra options to the configured virtual machine monitor, separate them from regular options with--. To use directory images with vm, theForeignUIDRange=option has to be enabled when building the image. same deal, different vibe. ๐ฅ๏ธsshโ When the image is built with theSsh=alwaysoption or if systemd'ssshd-vsockservice is running in the VM (systemdv256+), this command connects to a booted virtual machine via SSH. Make sure to runmkosi sshwith the same config asmkosi buildso that it has the necessary information available to connect to the running virtual machine via SSH. Specifically, the SSH private key from theSshKey=setting is used to connect to the virtual machine. Usemkosi genkeyto automatically generate a key and certificate that will be picked up by mkosi. Any arguments passed after the ssh verb are passed as arguments to the ssh invocation. To pass extra options, separate them from regular options with--.To connect to a container, usemachinectl loginormachinectl shell. TheMachine=option can be used to give the machine a custom hostname when booting it which can later be used to ssh into the image (e.g.mkosi --machine=mymachine vmfollowed bymkosi --machine=mymachine ssh). matching outfits for the win honestly. ๐ฏโโ๏ธjournalctlโ Usesjournalctlto inspect the journal inside the image. All arguments specified after the journalctl verb and separated by--from the regular options are appended to the journalctl invocation. detective mode: activated. ๐coredumpctlโ Usescoredumpctlto look for coredumps inside the image. All arguments specified after the coredumpctl verb and separated by--from the regular options are appended to the coredumpctl invocation. finding out why things went ๐, love that for us.sysupdateโ Invokessystemd-sysupdatewith the--transfer-source=option set to the output directory and the--definitions=option set to the directory configured withSysupdateDirectory=. All arguments specified after the sysupdate verb and separated from the regular options with--are passed directly tosystemd-sysupdate.boxโ Run arbitrary commands inside of the same environment used to execute other verbs such as boot, shell, vm and more. This means/usrwill be replaced by/usrfrom the tools tree if one is used while everything else will remain in place. If no command is provided,$SHELLwill be executed orbashif$SHELLis not set. To pass extra options to the given command, separate them from regular options with--.cleanโ Remove build artifacts generated on a previous build. If combined with-f, also removes incremental build cache images and the tools tree. If-fis specified twice, also removes any package cache. the double-fis not a typo, she's just thorough. ๐๏ธserveโ This builds the image if it is not built yet, and then serves the output directory (i.e. usuallymkosi.output/, see below) via a small embedded HTTP server, listening on port8081. Combine with-fin order to rebuild the image unconditionally before serving it. This command is useful for testing network-based acquisition of OS images, for example viamachinectl pull-raw ...andmachinectl pull-tar ....burn <device>โ This builds the image if it is not built yet, and then writes it to the specified block device. The partition contents are written as-is, but the GPT partition table is corrected to match sector and disk size of the specified medium. careful with<device>okay, that's a whole block device, respect her. ๐ฅbumpโ Bumps the image version frommkosi.versionand writes the resulting version string tomkosi.version. This is useful for implementing a simple versioning scheme: each time this verb is called the version is bumped in preparation for the subsequent build. Note that--auto-bump/-Bmay be used to automatically bump the version as part of a build. The new version is only written tomkosi.versionif the build succeeds in that case. Ifmkosi.bumpexists, it is invoked to generate the new version to be used instead of using mkosi's own logic.genkeyโ Generate a pair of SecureBoot keys for usage with theSecureBootKey=/--secure-boot-key=andSecureBootCertificate=/--secure-boot-certificate=options. keys, but make them secure. ๐documentationโ Show mkosi's documentation. If no argument is given, the mkosi man page is shown, but the argumentsmkosi,mkosi-initrd,initrd,mkosi-sandbox,sandbox,mkosi.newsandnewsare supported and respectively show the man pages formkosi,mkosi-initrd,mkosi-sandboxand mkosi's NEWS file. By default this verb will try several ways to output the documentation, but a specific option can be chosen with the--doc-formatoption. Distro packagers are encouraged to add a filemkosi.1into themkosi/resourcesdirectory of the Python package, if it is missing, as well as to install it in the appropriate search path for man pages. The man page can be generated from the markdown filemkosi/resources/man/mkosi.1.mde.g viapandoc -t man -s -o mkosi.1 mkosi.1.md. packagers, this one's for you. ๐ซกcompletionโ Generate shell completion for the shell given as argument and print it to stdout. The argumentsbash,fish, andzshare understood. tab-tab-tab girlies rise. โจ๏ธdependenciesโ Output the list of packages required by mkosi to build and boot images. This list can be piped directly to a package manager to install the packages. For example, if the host system uses thednfpackage manager, the packages could be installed as follows:
mkosi dependencies | xargs -d '\n' dnf install
By default, only the dependencies required to build images with mkosi are shown. Extra tools tree profiles can be enabled to also output the packages belonging to those profiles. For example, running mkosi dependencies -- --profile runtime will also output the packages in the runtime profile on top of the regular packages. See the documentation for ToolsTreeProfiles= for a list of available profiles.
latest-snapshotโ Output the latest available snapshot in the configured mirror. This verb is useful to automatically bump snapshots every so often. Note that this verb only outputs the latest snapshot. It's up to the caller to ensure that the snapshot is written to the intended configuration file. she prints, you file. teamwork. ๐คhelpโ This verb is equivalent to the--helpswitch documented below: it shows a brief usage explanation. the "just tell me what to do" option, iconic. ๐
๐ Command-Line-Only Options
Okay so these ones? You literally cannot put them in config files. CLI or bust, bestie. ๐ซถ
--force,-fโ replaces the output file if it's already there when you're building an image. Default behaviour is that mkosi just refuses to run if an output artifact already exists (rude but safe). Say it once and all build artifacts from a previous run get yeeted before the rebuild. If incremental builds are on, saying it twice also nukes the intermediary cache files before the rebuild kicks off. If you're using a package cache (peep the FILES section further down ๐), saying it thrice removes the package cache too before rebuilding. For the clean operation this flag hits a lil differently: by default the verb only removes build artifacts from a previous run, once means the incremental cache files and the tools tree get deleted too, and twice means the package cache gets removed as well. Iconic level-up system honestly.--directory=,-Cโ takes a path to a directory, and mkosi switches into it before doing literally anything. Important: the various configuration files get searched for in this directory, so this is a genuinely effective way to build a project that lives somewhere specific. Defaults to the current working directory. And if you pass the empty string? All configuration in the current working directory gets ignored. Ghosted. ๐--debugโ turns on extra debugging output. More words, more insight, no cap.--debug-shellโ when a command inside the image fails, mkosi pops open an interactive shell in the image so you can debug further. Emotional support shell fr.--debug-workspaceโ with this, the workspace directory will not be deleted, and its location gets logged when mkosi exits. Your leftovers stay put ๐--debug-sandboxโ runsmkosi-sandboxwithstrace. For when you need to see everything.--debug-timingโ logs how long each build step takes. Timestamp era โจ--versionโ shows the package version. Quick vibe check on what you're running.--help,-hโ shows brief usage information. The classic.--genkey-common-name=โ the common name used when generating keys via mkosi'sgenkeycommand. Defaults tomkosi of %u, where%uexpands to the username of whoever invoked mkosi. Cute and personal ๐--genkey-valid-days=โ how many days the keys stay valid when generating them via mkosi'sgenkeycommand. Defaults to two years (730days). Long-term commitment, we love to see it.--auto-bump=,-Bโ if you specify it, the version gets bumped, and if the build succeeds the version is written tomkosi.versionin a fashion equivalent to thebumpverb. Super useful for simple, linear version management: each build in a series gets a version number one higher than the previous one. And ifmkosi.bumpexists, it gets invoked to generate the new version instead of using mkosi's own logic. Custom lore ๐ฅ--doc-formatโ the format to show the documentation in. Supports the valuesmarkdown,man,pandoc,systemandauto. Breaking it down:markdownshows the docs in the original Markdown format.manshows them in man page format, if it's available.pandocgenerates the man page format on the fly, ifpandocis available.systemshows the system-wide man page for mkosi, which may or may not correspond to the version you're using, depending on how you installed mkosi.auto, which is the default, tries all the methods in the orderman,pandoc,markdown,system.
--jsonโ shows the summary output asJSON-SEQ. Machine-readable slay.--wipe-build-dir,-wโ wipes the build directory if one is configured, before building the image. Fresh start energy ๐ง--rerun-build-scripts,-Rโ reruns build scripts. Requires theIncremental=option to be enabled and the image to have already been built once. IfHistory=is enabled, the history from the previous build gets reused and no new history gets written. Recycling queen ๐
๐ Supported output formats
Here's the lineup of what you can output:
- Raw GPT disk image, created using
systemd-repart(disk) - Plain directory, containing the OS tree (
directory) - Tar archive (
tar) - CPIO archive (
cpio) - Unified Kernel Image (UKI)
- ...and much more. See the
Format=documentation below ๐
You can also set the output format to none and mkosi will produce no image at all. Which honestly is useful if you only want to use the image to produce another output in the build scripts (like building an RPM).
When a GPT disk image gets created, repart partition definition files can live in mkosi.repart/ to configure the generated disk image.
Highly recommended: run mkosi on a file system that supports reflinks, like XFS and btrfs, and keep all related directories on the same file system. Why? Because that lets mkosi create images very quickly by using reflinks to do copying via copy-on-write operations. The speed is unreal, no cap. โก
๐ Configuration Settings
These settings can be set through configuration files (syntax: SomeSetting=value) and on the command line (syntax: --some-setting=value). For some command line parameters there's also a single-letter shortcut allowed. In configuration files, the setting has to be in the appropriate section, so the settings below are grouped by section.
Configuration is parsed in this exact order, bestie, pay attention:
- The command line arguments are parsed.
mkosi.local.confandmkosi.local/are parsed if they exist (in that order). This file and directory should be in.gitignore(or equivalent) and are intended for local configuration.- If an option has a corresponding default path, it's parsed if the corresponding default path exists.
mkosi.confis parsed if it exists in the directory configured with--directory=or the current working directory if--directory=isn't used. If the specified directory doesn't contain amkosi.conformkosi.tools.confand amkosi/mkosi.conformkosi/mkosi.tools.confexists, the configuration is parsed from themkosi/subdirectory of the specified directory instead.mkosi.conf.d/is parsed in the same directory asmkosi.confif it exists. Each directory and each file with the.confextension inmkosi.conf.d/gets parsed. Any directory inmkosi.conf.dis parsed as if it were a regular top level directory, except formkosi.images/,mkosi.tools.confandmkosi.initrd.conf, which are only picked up in the top level directory.- If any profiles are configured, their configuration is parsed from the
mkosi.profiles/directory. - Subimages are parsed from the
mkosi.images/directory if it exists.
Note that settings configured via the command line always override settings configured via configuration files. If the same setting is configured more than once via configuration files, later assignments override earlier ones โ except for settings that take a collection of values. Also, settings read from mkosi.local.conf or mkosi.local/ will override settings from configuration files that are parsed later, but not settings specified on the CLI. Local stuff wins, but CLI stays the final boss ๐
For settings that take a single value, the empty assignment (SomeSetting= or --some-setting=) can be used to override a previous setting and reset it back to the default.
Settings that take a collection of values get merged by appending the new values to the previously configured ones. Assigning the empty string to such a setting removes all previously assigned values, and overrides any configured default values as well. Values specified on the CLI get appended after all the values from configuration files.
๐ฆ Conditional config: the [Match] section
To conditionally include configuration files, you use the [Match] section. A [Match] section is made up of individual conditions. Conditions can use a pipe symbol (|) after the equals sign (...=|...), which causes that condition to become a triggering condition. The config file gets included if the logical AND of all non-triggering conditions and the logical OR of all triggering conditions is satisfied. To negate the result of a condition, prefix the argument with an exclamation mark. And if an argument is prefixed with the pipe symbol and an exclamation mark, the pipe symbol has to come first, then the exclamation. Order matters, periodt.
Note that [Match] conditions compare against the current values of specific settings, and don't take into account changes made to the setting in configuration files that haven't been parsed yet (settings specified on the CLI are taken into account). Also note that matching against a setting and then changing its value afterwards in a different config file may lead to unexpected results. Live dangerously, but know the risks ๐ญ
The [Match] section of a mkosi.conf file in a directory applies to the entire directory. If the conditions aren't satisfied, the entire directory gets skipped. The [Match] sections of files in mkosi.conf.d/ and mkosi.local.conf only apply to the file itself.
If there are multiple [Match] sections in the same configuration file, each of them has to be satisfied for the configuration file to be included. Specifically, triggering conditions only apply to the current [Match] section and get reset between multiple [Match] sections. As an example, the following only matches if the output format is one of disk or directory and the architecture is one of x86-64 or arm64:
[Match]
Format=|disk
Format=|directory
[Match]
Architecture=|x86-64
Architecture=|arm64
โจ [TriggerMatch]
The [TriggerMatch] section can be used to indicate triggering matches. These are identical to triggering conditions in systemd units, except they apply to the entire match section instead of just a single condition. As an example, the following matches if the distribution is debian and the release is bookworm, or if the distribution is ubuntu and the release is noble:
[TriggerMatch]
Distribution=debian
Release=bookworm
[TriggerMatch]
Distribution=ubuntu
Release=noble
The semantics of conditions in [TriggerMatch] sections are the same as in [Match], i.e. all normal conditions are joined by a logical AND and all triggering conditions are joined by a logical OR. When you mix [Match] and [TriggerMatch] sections, a match is achieved when all [Match] sections match and at least one [TriggerMatch] section matches. The absence of match sections is valued as true. Logically this means:
(โแตข Matchแตข) โง (โแตข TriggerMatchแตข)
Math girlies rise ๐งฎ๐
There's also full support for [Assert] and [TriggerAssert] sections, bestie โ these behave identically to match sections, except that parsing the config will straight-up fail if the assert sections aren't satisfied. So like: all [Assert] sections in a file, plus at least one [TriggertAssert] section, have to be satisfied or config parsing fails. Periodt, no negotiating ๐
Command line options that take no argument are shown without = in their long version. In the config files though, you gotta specify them with a boolean argument: either 1, yes, or true to enable, or 0, no, false to disable. Easy ๐
๐ธ [Distribution] Section ๐ธ
Distribution=,--distribution=,-dโ the distribution you're installing in the image. Takes one of these arguments:fedora,debian,kali,ubuntu,arch,opensuse,mageia,centos,rhel,rhel-ubi,openmandriva,rocky,alma,azureorcustom. If you don't specify it, it defaults to the distribution of the host, orcustomif the host's distribution isn't a supported one. iykyk ๐ฆRelease=,--release=,-rโ the release of the distribution to install in the image. The exact syntax of the argument depends on the distribution you're using, and it's either a numeric string (for Fedora Linux, CentOS, ..., like29) or a distribution version name (for Debian, Kali, Ubuntu, ..., likeartful). Defaults to a recent version of the chosen distribution, or the version of the distribution running on the host if it matches the configured distribution. bestie it just knows โจArchitecture=,--architecture=โ the architecture to build the image for. The archs that are actually supported depend on the distribution you're using and whether a bootable image is requested or not. When you're building for a foreign architecture, you'll also need to install and register a user mode emulator for that architecture, okay? Not optional ๐ One of these architectures can be specified per image built:alpha,arc,arm,arm64,ia64,loongarch64,mips64-le,mips-le,parisc,ppc,ppc64,ppc64-le,riscv32,riscv64,s390,s390x,tilegx,x86,x86-64. the range is giving "we contain multitudes" ๐Mirror=,--mirror=,-mโ the mirror to use for downloading the distribution packages. Expects a mirror URL as argument. If you don't provide one, the default mirror for the distribution is used. The default mirrors for each distribution are as follows (unless specified, the same mirror is used for all architectures):debianโhttp://deb.debian.orgarchโ x86-64:https://fastly.mirror.pkgbuild.com, aarch64:http://mirror.archlinuxarm.orgopensuseโhttp://download.opensuse.orgkaliโhttp://http.kali.org/kaliubuntuโ x86-64:http://archive.ubuntu.com, aarch64:http://ports.ubuntu.comcentosโhttps://mirrors.centos.orgrockyโhttps://mirrors.rockylinux.orgalmaโhttps://mirrors.almalinux.orgfedoraโhttps://mirrors.fedoraproject.orgrhel-ubiโhttps://cdn-ubi.redhat.commageiaโhttps://www.mageia.orgopenmandrivaโhttp://mirrors.openmandriva.orgazureโhttps://packages.microsoft.com/
Snapshot=โ download packages from the given snapshot instead of grabbing the latest distribution packages from the given mirror. Takes a snapshot ID (the format of the snapshot ID differs per distribution), and you use thelatest-snapshotverb to figure out the latest available snapshot. Obsessed with time travel ๐ If this setting is configured andMirror=is not explicitly configured, different default mirrors are used:debianโhttps://snapshot.debian.orgarchโ x86-64:https://archive.archlinux.org, aarch64:http://mirror.archlinuxarm.orgopensuseโhttp://download.opensuse.orgubuntuโ x86-64:http://archive.ubuntu.com, aarch64:http://ports.ubuntu.comcentosโhttps://composes.stream.centos.orgfedoraโhttps://kojipkgs.fedoraproject.org
For any distribution not listed above, snapshots are not supported, sorry bestie ๐ญ And heads up: this setting does not apply to the default tools tree โ use ToolsTreeSnapshot= for that one!
LocalMirror=,--local-mirror=โ the mirror gets used as a local, plain and direct mirror instead of being used as a prefix for the full set of repositories normally supported by distributions. Super useful for fully offline builds with a single repository ๐ซถ Supported on deb-, rpm-, and pacman-based distributions. It overrides--mirror=, but only for the local mkosi build โ it will not be configured inside the final image.--mirror=(or the default repository) will be configured inside the final image instead. sneaky sneaky ๐RepositoryKeyCheck=,--repository-key-check=โ controls signature/key checks when using repositories, enabled by default. Useful to disable checks when combined with--local-mirror=and only using a repository from a local filesystem.RepositoryKeyFetch=,--repository-key-fetch=โ controls whether mkosi fetches distribution GPG keys remotely. Enabled by default on Ubuntu when not using a tools tree, or when using Ubuntu tools trees to build Arch Linux or RPM-based distributions. Disabled by default on all other distributions. When disabled, the distribution GPG keys for the target distribution have to be installed locally on the host system alongside the package manager for that distribution. This setting is only implemented for distributions usingdnf,pacmanorzypperas their package manager. For other distributions the distribution GPG keys are always looked up locally regardless of the value of this setting. To make distribution GPG keys available without enabling this setting, the corresponding package has to be installed on the host. This is usually one ofarchlinux-keyring,debian-keyring,kali-archive-keyring,ubuntu-keyringordistribution-gpg-keys(for RPM-based distributions). the keyring lore runs deep ๐Repositories=,--repositories=โ enable package repositories that are disabled by default. You can use this to enable the EPEL repos for CentOS or different components of the Debian/Kali/Ubuntu repositories. unlocking the secret menu fr fr ๐
๐ฆ [Output] Section ๐ฆ
Format=,--format=,-tโ the image format type to generate. One ofdirectory(generating an OS image directly in a local directory),tar(similar, but a tarball of the OS image is generated),cpio(similar, but a cpio archive is generated),disk(a block device OS image with a GPT partition table),uki(a unified kernel image with the OS image in the.initrdPE section),esp(a disk image with only an ESP partition, bootloader and optionally a UKI),oci(a directory compatible with the OCI image specification),sysext,confext,portable,addonornone(the OS image is solely intended as a build image to produce another artifact). so many flavors, all iconic ๐ If thediskoutput format is used, the disk image is generated usingsystemd-repart. The repart partition definition files to use can be configured using theRepartDirectories=setting or viamkosi.repart/. When verity partitions are configured usingsystemd-repart'sVerity=setting, mkosi will automatically parse the verity hash partition's roothash fromsystemd-repart's JSON output and include it in the kernel command line of every unified kernel image built by mkosi. bestie it literally does the math for you ๐งฎโจ If thenoneoutput format is used, the outputs from a previous build are not removed, but clean scripts (seeCleanScripts=) are still executed. This allows rerunning a build script (seeBuildScripts=) without removing the results of a previous build. non-destructive queen behavior ๐ManifestFormat=,--manifest-format=โ the manifest format type or types to generate. A comma-delimited list consisting ofjson(the standard JSON output format that describes the packages installed),changelog(a human-readable text format designed for diffing). By default no manifest is generated. no manifest unless you ask, boundaries ๐Output=,--output=,-oโ name to use for the generated output image file or directory. Defaults toimageor, ifImageId=is specified, it is used as the default output name, optionally suffixed with the version set withImageVersion=or if a specific image is built frommkosi.images, the name of the image is preferred overImageId. Note that this option does not allow configuring the output directory โ useOutputDirectory=for that. ๐ฆ Note that this only specifies the output prefix โ depending on the specific output format, compression and image version used, the full output name might beimage_7.8.raw.xz. like, it's a prefix bestie, not the whole vibe ๐ญOutputExtension=,--output-extension=โ use the specified extension for the output file. Defaults to the appropriate extension based on the output format. Only includes the file extension, not any compression extension โ which will be appended to this extension if compression is enabled.OutputSize=,--output-size=โ if set, the output disk image is resized to the given size. Takes a size in bytes. The suffixesK,MandGcan be used to specify the size in kilobytes, megabytes and gigabytes. This setting only grows the disk and is limited to full disk images and no other output format. it only goes up, never down ๐ ๐CompressOutput=,--compress-output=โ bestie this one sets the compression for the resulting image or archive โจ. The argument can be either a boolean or an actual compression algorithm (xz,zstd).zstdis the default, except for CentOS and its derivatives up to version 8, which default toxz, and OCI images, which default togzip๐. Heads up though: when applied to block device image types, compression means the image literally cannot be started directly โ it needs to be decompressed first ๐ญ. That also means theshell,boot, andvmverbs are not available when this option is used. Implied fortar,cpio,uki,esp,ociandaddon๐ซถ.CompressLevel=,--compress-level=โ configures the compression level to use, takes an integer ๐ . The possible values depend on the compression being used (so like, vibes vary, check your algorithm).OutputDirectory=,--output-directory=,-Oโ path to the directory where all the generated artifacts go ๐. If you don't specify it and a directory calledmkosi.output/exists in the local directory, it automatically gets used for this purpose. Convenient fr fr.OutputMode=,--output-mode=โ the file system access mode used when creating the output image file, takes an access mode in octal notation ๐ธ. If not set, it uses the current system defaults.ImageVersion=,--image-version=โ configure the image version ๐. This accepts any string, but it's recommended to specify a series of dot-separated components. The version can also be configured by reading amkosi.versionfile (in which case it may be conveniently managed via thebumpverb or the--auto-bumpoption) or by reading stdout if it is executable (see the Scripts section below) ๐ฆ. When specified, the image version is included in the default output file name โ i.e. instead ofimage.rawthe default will beimage_0.1.rawfor version0.1of the image, and similar โจ. The version is also passed via$IMAGE_VERSIONto any build scripts invoked (which may be useful to patch it into/usr/lib/os-releaseor similar, in particular theIMAGE_VERSION=field of it) ๐.ImageId=,--image-id=โ configure the image identifier, which accepts a freeform string that shall be used to identify the image with ๐. If set, the default output file will be named after it (possibly suffixed with the version). The identifier is also passed via$IMAGE_IDto any build scripts invoked. The image ID is automatically added to/usr/lib/os-release๐ . When usingFormat=oci, the image ID is also set as theorg.opencontainers.image.ref.nameannotation on the OCI index descriptor. Iconic.OciLabels=,--oci-labels=โ sets OCI config labels on images produced withFormat=oci๐. Takes a space-separated list ofKEY=VALUEassignments. These labels are stored in the OCI image config blob and are visible viapodman inspectordocker inspect(equivalent toLABELin a Containerfile) ๐. This option may be specified more than once, in which case all listed labels will be set. If the same label is set twice, the later setting overrides the earlier one โ last one wins, periodt ๐ .OciAnnotations=,--oci-annotations=โ sets OCI manifest annotations on images produced withFormat=ociโจ. Takes a space-separated list ofKEY=VALUEassignments. These annotations are stored in the OCI image manifest. mkosi automatically setsio.systemd.mkosi.versionand, ifImageVersion=is set,org.opencontainers.image.version๐. User-specified annotations override these defaults. This option may be specified more than once, in which case all listed annotations will be set.SplitArtifacts=,--split-artifacts=โ the artifact types to split out of the final image ๐ฆ. It's a comma-delimited list consisting ofuki,kernel,initrd,os-release,pcrs,partitions,roothash,kernel-modules-initrd,repart-definitionsandtar. When building a bootable image,kernelandinitrdcorrespond to their artifact found in the image (or in the UKI), whileukicopies out the entire UKI ๐. Ifpcrsis specified, a JSON file containing the pre-calculated TPM2 digests is written out, according to the UKI specification, which is useful for offline signing โจ. When building a disk image andpartitionsis specified, pass--split=yestosystemd-repartto have it write out split partition files for each configured partition. Read the man page for more information ๐. This is useful in A/B update scenarios where an existing disk image shall be augmented with a new version of a root or/usrpartition along with its Verity partition and unified kernel. Whentaris specified, the rootfs is additionally archived as a tar archive (compressed according toCompressOutput=) ๐ฆ. Whenroothashis specified and a dm-verity disk image is built, the dm-verity roothash is written out as a separate file, which is useful for offline signing ๐ฅ.kernel-modules-initrdcorresponds to the separate kernel modules initrd which mkosi appends to the main initrd. This is primarily intended for debugging as many initrd inspection tools don't properly handle multiple initrds appended to each other ๐. Whenrepart-definitionsis specified, a directory containing the used repart definition files is written to the output directory. If multiple directories are configured viaRepartDirectories=, they are merged, with later directories taking priority over earlier ones when files with identical names exist ๐ธ. By defaultuki,kernelandinitrdare split out.RepartDirectories=,--repart-directory=โ paths to directories containingsystemd-repartpartition definition files that are used when mkosi invokessystemd-repartwhen building a disk image ๐. Ifmkosi.repart/exists in the local directory, it will be used for this purpose as well โจ. Note that mkosi invokes repart with--root=set to the root of the image root, so anyCopyFiles=source paths in partition definition files will be relative to the image root directory ๐.SectorSize=,--sector-size=โ overrides the default sector size thatsystemd-repartuses when building a disk image ๐ .Overlay=,--overlay=โ when used together withBaseTrees=, the output will consist only out of changes to the specified base trees ๐ฆ. Each base tree is attached as a lower layer in an overlayfs structure, and the output becomes the upper layer, initially empty. So files that are not modified compared to the base trees will not be present in the final output ๐. This option may be used to create systemd system extensions โจ.Seed=,--seed=โ takes a UUID as argument or the special valuerandom๐ฒ. Overrides the seed thatsystemd-repartuses when building a disk image. This is useful to achieve reproducible builds, where deterministic UUIDs and other partition metadata should be derived on each build ๐. If not specified explicitly and the filemkosi.seedexists in the local directory, the UUID to use is read from it ๐ธ. Otherwise, a random UUID is used.ElTorito=,--el-torito=โ whether to add an El Torito boot catalog to boot the ESP when building a disk image ๐ฟ. Takes a boolean value orauto. Defaults toauto. When set toauto, an El Torito boot catalog is added unless grub is installed as the BIOS bootloader (as that is incompatible ๐ญ). Requiressystemd-repartโฅ v261.ElToritoSystem=,--el-torito-system=โ set the system identifier in the ISO 9660 descriptor ๐.ElToritoVolume=,--el-torito-volume=โ set the volume identifier in the ISO 9660 descriptor ๐ธ.ElToritoPublisher=,--el-torito-publisher=โ set the publisher identifier in the ISO 9660 descriptor โจ.CleanScripts=,--clean-script=โ takes a comma-separated list of paths to executables that are used as the clean scripts for this image ๐งน. Glob patterns are supported and matching paths are sorted alphabetically. See theSCRIPTSsection for more information ๐.
๐ฆ [Content] Section ๐ฆ
Okay so now we're in the content girlie era, this is where you tell mkosi what actually goes inside the image ๐
Packages=,--package=,-pโ install the specified distribution packages (i.e.RPM,deb, ...) in the image ๐ฆ. Takes a comma-separated list of package specifications. This option may be used multiple times, in which case the specified package lists are combined โจ. UseBuildPackages=to specify packages that shall only be installed in an overlay that is mounted when the prepare scripts are executed with thebuildargument and when the build scripts are executed ๐. The types and syntax of package specifications that are allowed depend on the package installer (e.g.dnffor RPM-based distros oraptfor deb-based distros), but may include package names, package names with version and/or architecture, package name globs, package groups, and virtual provides, including file paths ๐ฆ. SeePackageDirectories=for information on how to make local packages available for installation withPackages=. Example: when using a distro that usesdnf, the following configuration would install themesonpackage (in the latest version), the 32-bit version of thelibfdisk-develpackage, all available packages that start with thegit-prefix, asystemdRPM from the local file system, one of the packages that provides/usr/bin/ld, the packages in theDevelopment Toolsgroup, and the package that contains themypypython module ๐ฅ:
Packages=meson
libfdisk-devel.i686
git-*
/usr/bin/ld
@development-tools
python3dist(mypy)
BuildPackages=,--build-package=โ similar toPackages=, but configures packages to install only in an overlay that is made available on top of the image to the prepare scripts when executed with thebuildargument and the build scripts ๐ ๏ธ. This option should be used to list packages containing header files, compilers, build systems, linkers and other build tools themkosi.buildscripts require to operate ๐ . Note that packages listed here will be absent in the final image โ build-only, bestie, they don't make the final cut ๐ญ.VolatilePackages=,--volatile-package=โ okay so this is basicallyPackages='s chaotic little sibling ๐ same energy, but these packages don't get cached whenIncremental=is on, and they get installed after any build scripts have run. Which is literally the point: use this for stuff that changes constantly, or for packages that a build script just built. ๐PackageDirectories=,--package-directory=โ tell mkosi where your extra packages live ๐ซถ it'll spin up a local repository containing everything in these directories and make it available whenever packages get installed or scripts get run. Oh and ifmkosi.packages/exists in the local directory, that gets used for this too. Free real estate โจVolatilePackageDirectories=,--volatile-package-directory=โ likePackageDirectories=, but any changes to packages in these directories won't invalidate cached images whenIncremental=is enabled. Iconic behavior tbh. ๐ฅ
Bonus round: build scripts can add more packages to the local repository by dropping the built packages into $PACKAGEDIR. And those packages in $PACKAGEDIR are shared across all image builds, so they're available for installation in all images using VolatilePackages=. bestie sharing is caring ๐
WithRecommends=,--with-recommends=โ controls whether recommended/weak dependencies get installed or not (whatever your package manager calls them). By default, recommended packages are not installed. ๐ซ This only applies to package managers that actually have the concept, which right now isapt,dnfandzypper. slay for them specifically ๐WithDocs=,--with-docs=โ include documentation in the image. Enabled by default. When you disable it, documentation won't be included if the underlying distribution package manager supports that. Also, the$WITH_DOCSenvironment variable passed to themkosi.buildscripts gets set to0or1depending on whether this option is enabled or disabled. keeping the scripts in the loop fr fr ๐งBaseTrees=,--base-tree=โ takes a comma-separated list of paths to use as base trees ๐ when you use it, each base tree gets copied into the OS tree and forms the base distribution instead of installing the distribution from scratch. Only extra packages get installed on top of the ones already chilling in the base trees.
Heads up: for this to work properly, the base image still needs to contain the package manager metadata โ setCleanPackageMetadata=no(seeCleanPackageMetadata=). No cap on that one ๐
Also, instead of a directory, you can hand it a tar file or a disk image โ in that case it gets unpacked into the OS tree. This mode of operation lets you set permissions and file ownership explicitly, which is especially clutch for projects stored in a version control system such as git that retain full file ownership and access mode metadata for committed files. chef's kiss ๐
SkeletonTrees=,--skeleton-tree=โ takes a comma-separated list of colon-separated path pairs ๐ the first path of each pair is a directory to copy into the OS tree before invoking the package manager; the second path is the target directory inside the image. If you don't give a second path, the directory gets copied on top of the root directory of the image. The second path is always interpreted as an absolute path. Use this to insert files and directories into the OS tree before the package manager installs any packages. Ifmkosi.skeleton/is found in the local directory, it's also used for this with the root directory as target (also see theFILESsection below). ๐ฆ
Important caveat bestie: skeleton trees are cached, and any changes you make to them after a cached image has been built (when usingIncremental=) only get applied when the cached image is rebuilt โ by using-ffor runningmkosi -f clean. ๐ญ
As with the base tree logic above, you can provide a tar file instead of a directory too. mkosi.skeleton.tar will be automatically used if found in the local directory. ๐ฆ
And like โ if you want to add extra package manager configuration files such as extra repositories, use SandboxTrees= instead, because mkosi invokes the package managers from outside the image and not inside, so any package manager configuration files you provide via SkeletonTrees= won't take effect when mkosi invokes a package manager to install packages. iykyk ๐
ExtraTrees=,--extra-tree=โ takes a comma-separated list of colon-separated path pairs โจ first path of each pair = a directory to copy from the host into the image; second path = the target directory inside the image. No second path? It gets copied on top of the root directory of the image. Second path is always absolute, always. Use this to override any default configuration files shipped with the distribution. Ifmkosi.extra/is found in the local directory it's also used for this with the root directory as target (also see theFILESsection below). ๐ธ
As with the base tree logic above, a tar file may be provided too. mkosi.extra.tar will be automatically used if found in the local directory. ๐
RemovePackages=,--remove-package=โ takes a comma-separated list of package specifications for removal, in the same format asPackages=. The removal happens as one of the last steps. This step gets skipped ifCleanPackageMetadata=nois used. bye bestie ๐๐ญRemoveFiles=,--remove-files=โ takes a comma-separated list of globs. Files in the image matching the globs get purged at the end. sweep sweep ๐งนโจCleanPackageMetadata=,--clean-package-metadata=โ enable/disable removal of package manager databases and repository metadata at the end of installation. Can betrue,false, orauto(which is the default). Withauto, package manager databases and repository metadata get removed if the respective package manager executable isn't present at the end of the installation. cute and smart ๐SourceDateEpoch=,--source-date-epoch=โ takes a timestamp in seconds since the UNIX epoch as its argument. File modification times of all files get clamped to this value. The variable is also propagated tosystemd-repartand to scripts executed by mkosi. If you don't set it explicitly,SOURCE_DATE_EPOCHfrom--environment=and then from the host environment are tried, in that order. This is the move for making builds reproducible ๐ซถ seeSOURCE_DATE_EPOCHfor more info. โญSyncScripts=,--sync-script=โ takes a comma-separated list of paths to executables used as the sync scripts for this image. Glob patterns are supported and matching paths are sorted alphabetically. See theSCRIPTSsection for more. ๐ฆPrepareScripts=,--prepare-script=โ takes a comma-separated list of paths to executables used as the prepare scripts for this image. Glob patterns are supported and matching paths are sorted alphabetically. See theSCRIPTSsection for more. โจBuildScripts=,--build-script=โ takes a comma-separated list of paths to executables used as the build scripts for this image. Glob patterns are supported and matching paths are sorted alphabetically. See theSCRIPTSsection for more. ๐ฅPostInstallationScripts=,--postinst-script=โ takes a comma-separated list of paths to executables used as the post-installation scripts for this image. Glob patterns are supported and matching paths are sorted alphabetically. See theSCRIPTSsection for more. ๐FinalizeScripts=,--finalize-script=โ takes a comma-separated list of paths to executables used as the finalize scripts for this image. Glob patterns are supported and matching paths are sorted alphabetically. See theSCRIPTSsection for more. ๐PostOutputScripts=,--postoutput-script=โ takes a comma-separated list of paths to executables used as the post output scripts for this image. Glob patterns are supported and matching paths are sorted alphabetically. See theSCRIPTSsection for more. ๐Bootable=,--bootable=โ takes a boolean orauto. ๐ enables or disables generation of a bootable image. If enabled, mkosi installs an EFI bootloader and adds an ESP partition when the disk image output is used. If the selected EFI bootloader (seeBootloader=) isn't installed, or no kernel images can be found, the build will fail. ๐ญ
auto behaves as if the option was enabled, but the build won't fail if either no kernel images or the selected EFI bootloader can't be found. lowkey forgiving, we love that ๐ซถ
If disabled, no bootloader gets installed even if one is found inside the image, no unified kernel images get generated, and no ESP partition gets added to the image if the disk output format is used. ๐ซ
And if the esp output format is used, an ESP partition and bootloader are always added โ the Bootable= option then only controls whether a UKI gets added to the ESP partition or not. periodt. ๐
Bootloader=,--bootloader=โ picks your EFI bootloader, bestie. Takes one ofnone,systemd-boot,uki,grub,systemd-boot-signed,uki-signedorgrub-signed. Defaults tosystemd-boot. Here's the tea on each:noneโ no EFI bootloader gets installed into the image at all, we're going bare ๐systemd-bootโ systemd-boot gets installed, and for each installed kernel a UKI gets generated and stored inEFI/Linuxin the ESP โจukiโ a single UKI gets generated for the latest installed kernel (the one with the highest version), installed toEFI/BOOT/BOOTX64.EFIin the ESPgrubโ for each installed kernel, a UKI gets generated and stored inEFI/Linuxin the ESP. For each generated UKI, a menu entry gets appended to the grub configuration ingrub/grub.cfgin the ESP which chainloads into the UKI. A shimgrub.cfgis also written toEFI/<distribution>/grub.cfgin the ESP which loadsgrub/grub.cfgin the ESP for compatibility with signed versions of grub which load the grub configuration from this location ๐
The signed variants (systemd-boot-signed, uki-signed, grub-signed) will only install pre-signed EFI binaries shipped by the distribution, no cap.
Kernels need to be placed into the root filesystem (for example using ExtraTrees=) under /usr/lib/modules/$version, named vmlinux or vmlinuz. The $version is as produced by Kbuild's kernelversion make target. Don't sleep on that โ๏ธ
Note: when you're usingsystemd-bootorsystemd-boot-signed, mkosi expects the systemd-boot EFI binaries to be present in the image. Depending on your distribution, these may be packaged separately. For example, Debian-based images will needsystemd-boot-efi๐ซถ
BiosBootloader=,--bios-bootloader=โ takes one ofnoneorgrub, defaults tonone. If it'snone, no BIOS bootloader gets installed, periodt. If it'sgrub, grub gets installed as the BIOS boot loader if a bootable image is requested with theBootable=option. And if no repart partition definition files are configured, mkosi will add a grub BIOS boot partition and an EFI system partition to the default partition definition files ๐
Important vibe check: this option is not mutually exclusive with Bootloader=. You can absolutely have an image that's bootable on both UEFI and BIOS by configuring both Bootloader= and BiosBootloader=. Dual-boot girlies rise ๐
The grub BIOS boot partition should have UUID 21686148-6449-6e6f-744e-656564454649 and should be at least 1MB. Even if no EFI bootloader is installed, we still need an ESP for BIOS boot, because that's where we store the kernel, initrd and grub modules ๐
ShimBootloader=,--shim-bootloader=โ takes one ofnone,unsigned, orsigned. Defaults tonone.noneโ shim and MokManager will not be installed to the ESPunsignedโ mkosi will search for unsigned shim and MokManager EFI binaries and install them. IfSecureBoot=is enabled, mkosi will sign the unsigned EFI binaries before installing them ๐signedโ mkosi will search for signed EFI binaries and install those. Even ifSecureBoot=is enabled, mkosi won't sign these binaries again, they're already iconic
Note that this option only takes effect when an image that's bootable on UEFI firmware is requested using other options (Bootable=,Bootloader=). Also note: when this option is enabled, mkosi will only install already signed bootloader binaries, kernel image files and unified kernel images โ because self-signed binaries would not be accepted by the signed version of shim ๐ญ
UnifiedKernelImages=,--unified-kernel-images=โ specifies whether to use unified kernel images or not whenBootloader=is set tosystemd-bootorgrub. Takes one ofnone,unsigned,signedorauto, and defaults toauto.unsignedorsignedโ unified kernel images are always used, and the build will fail if any components required to build unified kernel images are missing โ ๏ธautoโ unified kernel images will be used if all necessary components are available; otherwise Type 1 entries as defined by the Boot Loader Specification will be used instead- disabled โ Type 1 entries will always be used
If Bootloader= is set to one of the signed variants, a pre-built UKI will be searched for and the build will fail if it can't be found โ unless UnifiedKernelImages= is set to unsigned, in which case the UKI will be built locally ๐ ๏ธ This is super useful when combined with the runtime Firmware= option set to custom so that the local signing key is enrolled in UEFI db ๐
UnifiedKernelImageFormat=,--unified-kernel-image-format=โ takes a filename without any path components to specify the format that unified kernel images should be installed as. This may include both the regular specifiers (see Specifiers) and special delayed specifiers, which get expanded during the installation of the files โ described right below, bestie ๐
The default format for this parameter is &e-&k with -&h being appended if roothash= or usrhash= is found on the kernel command line, and +&c if /etc/kernel/tries is found in the image.
The following specifiers may be used:
&&โ&character&eโ Entry Token&kโ Kernel version&hโroothash=orusrhash=value of kernel argument
UnifiedKernelImageProfiles=,--uki-profile=โ build additional UKI profiles, queen behavior ๐ Takes a comma-separated list of paths to UKI profile config files. This option may be used multiple times, in which case each config gets built into a corresponding UKI profile. Config files in themkosi.uki-profiles/directory are automatically picked up, no manual work needed โจ All configured UKI profiles are added as additional UKI profiles to each UKI built by mkosi.
See the documentation for the UKIProfile section for info on which settings can be configured in UKI profile config files ๐
Initrds=,--initrd=โ use user-provided initrd(s). Takes a comma-separated list of paths to initrd files. This option may be used multiple times, in which case the initrd lists are combined ๐ง
If this setting isn't specified and a bootable image is requested, mkosi will automatically build a default initrd if the image looks like it might be a bootable image based on a few factors (output format, is a kernel package installed, ...). To disable building the default initrd, assign the empty string to this setting. To build the default initrd even when specifying this setting, the special value default can be assigned alongside the other values ๐
When the default initrd is used, it can be further configured using the settings below and with mkosi.initrd.conf, which can either be a file or directory containing extra configuration for the default initrd. mkosi will also look for initrds in a subdirectory io.mkosi.initrd of the artifact directory (see $ARTIFACTDIR in the section ENVIRONMENT VARIABLES). Any initrds found there are appended to the user-provided initrd(s) and any default initrd built by mkosi ๐
InitrdProfiles=,--initrd-profile=โ set the profiles to enable for the default initrd. Takes a comma-delimited list of profiles. By default, all profiles are disabled, so you gotta opt in ๐lvmโ enables support for LVMnetworkโ enables support for network via systemd-networkdnfsโ enables support for NFS. It requires networking in the initrd, using thenetworkprofile, or some other custom methodpkcs11โ enables support for PKCS#11plymouthโ provides a graphical interface at boot (animation and password prompt) ๐ธraidโ enables support for RAID arrays
InitrdPackages=,--initrd-package=โ extra packages to install into the default initrd. Takes a comma separated list of package specifications. This option may be used multiple times, in which case the specified package lists are combined ๐ฆInitrdVolatilePackages=,--initrd-volatile-package=โ similar toVolatilePackages=, except it applies to the default initrd. That's it, that's the whole thing ๐ซถDevicetrees=,--devicetrees=โ comma-separated list of devicetree patterns for automatic hardware-based selection. Patterns are glob expressions. mkosi searches for devicetree files in standard locations relative to:/usr/lib/modules/<kver>/dtb//usr/lib/firmware/<kver>/device-tree//usr/lib/linux-image-<kver>/
For UKI builds, multiple matches enable automatic hardware-based selection using the .dtbauto sections โจ Type 1 boot entries require exactly one match, so don't get cute with it.
Example: Devicetrees=rockchip/*,imx.* would include all Rockchip devicetrees and any IMX devicetrees. Slay ๐
Splash=,--splash=โ when set, the boot splash for any unified kernel image built by mkosi will be picked up from the given path inside the image ๐จ aesthetic boot experience, we love to see itMicrocodeHost=,--microcode-host=โ when set totrue, only include microcode for the host's CPU in the image. No bloat, only vibes ๐KernelCommandLine=,--kernel-command-line=โ use the specified kernel command line when building images. This one's got some rules, so listen up bestie:
If the root or usr partition are created with verity enabled, roothash= or usrhash= respectively are automatically added to the kernel command line, and root= or mount.usr= should not be added ๐
โโ๏ธ
Otherwise, if the value of this setting contains the literals root=PARTUUID or mount.usr=PARTUUID, these are replaced with the partition UUID of the root or usr partition respectively. For example, root=PARTUUID would be replaced with root=PARTUUID=58c7d0b2-d224-4834-a16f-e036322e88f7 where 58c7d0b2-d224-4834-a16f-e036322e88f7 is the partition UUID of the root partition. Magic โจ
KernelModules=,--kernel-modules=โ bestie, this is THE list. It takes a list of glob patterns that specify which kernel modules to include in the image ๐ Each argument may be prefixed with a dash (-), to exclude matching modules. The arguments are evaluated in order, and the last positive or negative matching pattern determines the result. The modules that were last matched by a positive pattern are included in the image, as well as their module and firmware dependencies ๐ซถ The.kosuffix and compression suffix are ignored during matching. Globs are matched against module paths relative to/usr/lib/module/<kver>, e.g. the module at/usr/lib/module/<kver>/kernel/foo/bar.ko.xzbecomeskernel/foo/barfor matching purposes โจ Globs beginning with โ/โ are treated specially โ lowkey the icon behavior: the glob is first matched against a path relative to/usr/lib/module/<kver>/kernel, and only then against/usr/lib/module/<kver>/. This is a convenience, since usually only the in-tree modules underkernel/are of interest. For example, the module at/usr/lib/module/<kver>/kernel/foo/bar.ko.xzcan be matched by either/foo/baror/kernel/foo/bar. The glob patterns may include just the basename (e.g.loop), which must match the basename of the module, the relative path (e.g.block/loop), which must match the final components of the module path up to the basename, or an absolute path (e.g./drivers/block/loop), which must match the full path to the module ๐ When suffixed with/, the pattern will match all modules underneath that directory. The patterns may include shell-style globs (*,?,[...-...]) โญ If the special valuedefaultis used, the default kernel modules defined in themkosi-initrdconfiguration are included as well. If the special valuehostis used, the currently loaded modules on the host system are included as well ๐ธKernelModulesInitrd=,--kernel-modules-initrd=โ Boolean value, enabled (true) by default ๐ If enabled, when building a bootable image, mkosi will generate an extra initrd for each unified kernel image it assembles. This initrd contains only modules and possibly firmware, and is then appended to the base initrd to form the final initrd file. This keeps the base initrd kernel independent, and only augments it with the necessary kernel-version specific modules when the UKI is assembled. If disabled, no extra initrd is generated. Note that the kernel modules will still not be included in the base initrd, which remains kernel independent. Instead, it is assumed the user provides the necessary modules, if any, via an additional custom initrd ๐KernelInitrdModules=,--kernel-modules-initrd-include=โ LikeKernelModules=, but specifies the kernel modules to include in the initrd ๐FirmwareFiles=,--firmware-files=โ Takes a list of glob patterns that specify which firmware files to include in the image ๐ฆ The patterns are interpreted in the same way as in theKernelModules=settings, except that the paths are relative to/usr/lib/firmware/<subdir>. The compression suffix is ignored and must not be included in the pattern. Firmware dependencies of kernel modules installed in the image are automatically included. Example:FirmwareFiles=cxgb4/bcm8483.binorFirmwareFiles=bcm8483.*would both cause/usr/lib/firmware/cxgb4/bcm8483.bin.xzto be included, even if not listed by a module ๐ฅLocale=,--locale=,LocaleMessages=,--locale-messages=,Keymap=,--keymap=,Timezone=,--timezone=,Hostname=,--hostname=,RootShell=,--root-shell=โ The settingsLocale=,--locale=,LocaleMessages=,--locale-messages=,Keymap=,--keymap=,Timezone=,--timezone=,Hostname=,--hostname=,RootShell=,--root-shell=correspond to the identically namedsystemd-firstbootoptions ๐ Seesystemd-firstboot(1)for more information. Additionally, where applicable, the corresponding systemd credentials for these settings are written to/usr/lib/credstore, so that they apply even if only/usris shipped in the image โจRootPassword=,--root-password=โ Set the system root password ๐ If this option is not used, but amkosi.rootpwfile is found in the local directory, the password is automatically read from it, or if the file is executable it is run as a script and stdout is read instead (see the SCRIPTS section below). If the password starts withhashed:, it is treated as an already hashed root password. The root password is also stored in/usr/lib/credstoreunder the appropriate systemd credential so that it applies even if only/usris shipped in the image ๐ To create an unlocked account without any password usehashed:without a hash ๐Autologin=,--autologin=,-aโ Enable autologin for the root user on/dev/pts/0(nspawn),/dev/tty1and/dev/hvc0๐ชโจMakeInitrd=,--make-initrd=โ Add/etc/initrd-releaseand/initto the image so that it can be used as an initramfs ๐Ssh=,--ssh=โ Specifies whether to install ansshdsocket unit and matching service in the final image. Takes one ofalways,never,autoorruntime. Defaults toauto. If set toautoandsshdis present in the image and the generator binarysystemd-ssh-generatoris not present, or if set toalways, mkosi will installsshdunits in the final image that expose SSH over VSock ๐ If set tonever, mkosi will not install these units. If theruntimevalue is used, mkosi will also not install any units but abort startingmkosi vmif no SSH credentials are configured. When building with this option and running the image usingmkosi vm, themkosi sshcommand can be used to connect to the container/VM via SSH ๐ Note that you still have to make sureopensshis installed in the image to makemkosi sshbehave correctly. Runmkosi genkeyto automatically generate an X.509 certificate and private key to be used by mkosi to enable SSH access to any virtual machines viamkosi ssh๐ To access images booted usingmkosi boot, usemachinectl. Starting with systemd v256,systemd-ssh-generator(8)will automatically providesshdover VSock when running inside a VM. So if youโre using a recent version of systemd inside a VM, this option is generally not used. You still needopensshinstalled in the image, and the default setting of--vsock=autois enough to ensure a VSock is available inside the VM โ
Note: if the image distro uses SELinux, mkosiโssshdservice will be denied access to the VSock, resulting in failure to connect to it from the host ๐ญ You will need to either disable SELinux enforcement, or create a custom policy module (e.g. withaudit2allow).
SELinuxRelabel=,--selinux-relabel=โ Specifies whether to relabel files to match the imageโs SELinux policy ๐ก๏ธ Takes a boolean value orauto. Defaults toauto. If disabled, files will not relabeled. If enabled, an SELinux policy has to be installed in the image andsetfileshas to be available to relabel files. If any errors occur duringsetfiles, the build will fail ๐ If set toauto, files will be relabeled if mkosi is not building a directory image, an SELinux policy is installed in the image and ifsetfilesis available. Any errors occurred duringsetfileswill be ignored ๐
Note that when running unprivileged,setfileswill fail to set any labels that are not in the hostโs SELinux policy. To ensuresetfilessucceeds without errors, make sure to run mkosi as root or build from a host system with the same SELinux policy as the image youโre building โ ๏ธ
MachineId=,--machine-id=โ Takes a UUID or the special valuerandom๐ฒ Sets the machine ID of the image to the specified UUID. If set torandom, a random UUID will be written to/etc/machine-id. If not specified explicitly and the filemkosi.machine-idexists in the local directory, the UUID to use is read from it. Otherwise,uninitializedwill be written to/etc/machine-id๐ซถ
๐ก๏ธ [Validation] Section
SecureBoot=,--secure-boot=โ Signsystemd-boot(if it is not signed yet) and any generated unified kernel images for UEFI SecureBoot ๐๐SecureBootAutoEnroll=,--secure-boot-auto-enroll=โ Set up automatic enrollment of the secure boot keys in virtual machines as documented insystemd-boot(7)ifSecureBoot=is used โจ Note thatsystemd-bootwill only do automatic secure boot key enrollment in virtual machines starting from systemd v253. To do auto enrollment on systemd v252 or on bare metal machines, write asystemd-bootconfiguration file to/efi/loader/loader.confusing an extra tree withsecure-boot-enroll forceorsecure-boot-enroll manualin it ๐ Auto enrollment is not supported on systemd versions older than v252 ๐ Defaults toyes.SecureBootKey=,--secure-boot-key=โ Path to the PEM file containing the secret key for signing the UEFI kernel image ifSecureBoot=is used and PCR signatures whenSignExpectedPcr=is also used ๐ WhenSecureBootKeySource=is specified, the input type depends on the source ๐SecureBootCertificate=,--secure-boot-certificate=โ Path to the X.509 file containing the certificate for the signed UEFI kernel image, ifSecureBoot=is used ๐SecureBootSignTool=,--secure-boot-sign-tool=โ Tool to use to sign secure boot PE binaries ๐ ๏ธ Takes one ofsystemd-sbsign,sbsignorauto. Defaults toauto. If set toauto, eithersystemd-sbsignorsbsignare used if available, withsystemd-sbsignbeing preferred ๐
These next ones are the verity + secure-boot signing girlies, and they are lowkey doing the most โ but itโs fine, weโre built different ๐
Verity=,--verity=โ This is the switch that says whether verity for extension images should be enforced or turned off. It takes one ofsigned,hash,defer,auto, or a boolean value. If itโs set tosigned, a verity key and certificate must be present, and the build will fail if mkosi doesnโt detect any verity partitions in the disk image produced bysystemd-repart. If disabled, verity partitions get excluded from the extension images produced bysystemd-repart. If itโs set tohash, mkosi configuressystemd-repartto create a verity hash partition, but no signature partition. If itโs set todefer, space for the verity sig partition is allocated but it will not be populated yet. If itโs set toautoand a verity key and certificate are present, mkosi passes them tosystemd-repartand expects the generated disk image to contain verity partitions, but the build wonโt fail if no verity partitions are found in the disk image produced bysystemd-repart.
Heads up bestie: explicitly disabling verity signature and/or hash is not yet implemented for the disk output, and only works for extension images at the moment.
VerityKey=,--verity-key=โ Path to the PEM file containing the secret key for signing the verity signature, if a verity signature partition is added withsystemd-repart. WhenVerityKeySource=is specified, the input type depends on the source.VerityCertificate=,--verity-certificate=โ Path to the X.509 file containing the certificate for signing the verity signature, if a verity signature partition is added withsystemd-repart.SignExpectedPcr=,--sign-expected-pcr=โ Measure the components of the unified kernel image (UKI) usingsystemd-measureand embed the PCR signature into the unified kernel image. This option takes a boolean value or the special valueauto, which is the default, and is equal to a true value if thesystemd-measurebinary is inPATH. Depends onSecureBoot=being enabled and key fromSecureBootKey=.SignExpectedPcrKey=,--sign-expected-pcr-key=โ Path to the PEM file containing the secret key for signing the expected PCR signatures. WhenSignExpectedPcrKeySource=is specified, the input type depends on the source.SignExpectedPcrCertificate=,--sign-expected-pcr-certificate=โ Path to the X.509 file containing the certificate for signing the expected PCR signatures.SignInitrdPCRs=,--sign-initrd-pcrs=โ Whether to generate PCR policies that can only be satisfied from the initrd. This is required for initialization of NvPCRs. It takes a boolean value or the special valueauto, which is the default and is equivalent to a true value if the version ofukifyis at leastv262and either PCR signing is enabled (seeSignExpectedPcr=) orpcrsis selected inSplitArtifacts=. If enabled explicitly, either PCR signing orSplitArtifacts=pcrsmust be enabled as well. When PCR signing is enabled, the policies are signed with the key supplied toSignExpectedPcrKey=and embedded in the UKI. IfSplitArtifacts=pcrsis enabled and PCR signing is disabled, the policy digests are written unsigned to the split PCR JSON artifact for offline signing.SecureBootKeySource=,--secure-boot-key-source=,VerityKeySource=,--verity-key-source=,SignExpectedPcrKeySource=,--sign-expected-key-source=โ The source of the corresponding private key, to support OpenSSL engines and providers, e.g.--secure-boot-key-source=engine:pkcs11or--secure-boot-key-source=provider:pkcs11.SecureBootCertificateSource=,--secure-boot-certificate-source=,VerityCertificateSource=,--verity-certificate-source=,SignExpectedPcrCertificateSource=,--sign-expected-certificate-source=โ The source of the corresponding certificate, to support OpenSSL providers, e.g.--secure-boot-certificate-source=provider:pkcs11. Note that engines are not supported.Passphrase=,--passphrase=โ Specify the path to a file containing the passphrase to use for LUKS encryption. It should contain the passphrase literally, and not end in a newline character (i.e. in the same format ascryptsetupand/etc/crypttabexpect the passphrase files). The file must have an access mode of0600or less. Note that this setting on its own does not enable any encryption โ you also have to add one or more partition definition files tomkosi.repart/withEncrypt=key-fileto add encrypted partitions to the image.Checksum=,--checksum=โ Generate a<output>.SHA256SUMSfile of all generated artifacts after the build is complete.Sign=,--sign=โ Sign the generatedSHA256SUMSusinggpgafter completion.OpenPGPTool=,--openpgp-tool=โ OpenPGP implementation to use for signing.gpgis the default. Selecting a value different than the default will use the given Stateless OpenPGP (SOP) tool for signing theSHA256SUMSfile. Exemplary choices aresqopandrsop, but any implementation fromhttps://www.openpgp.org/about/sop/that can be installed locally will work.Key=,--key=โ Select thegpgkey to use for signingSHA256SUMS. This key must be already present in thegpgkeyring.
๐ ๏ธ [Build] Section ๐ ๏ธ
Okay, this section is giving โletโs make the build reproducible eraโ and honestly? Obsessed โจ
ToolsTree=,--tools-tree=โ If specified, programs executed by mkosi to build and boot an image are looked up inside the given tree instead of in the host system. Use this option to make image builds more reproducible by always using the same versions of programs to build the final image instead of whatever version is installed on the host system. If this option is not used, but themkosi.tools/directory is found in the local directory, it is automatically used for this purpose with the root directory as target. The tools tree directory is kept between repeated image builds unless cleaned by callingmkosi clean -f. Note that binaries found in any of the paths configured withExtraSearchPaths=will be executed with/usr/from the tools tree instead of from the host. If the host distribution or release does not match the tools tree distribution or release respectively, this might result in failures when trying to execute binaries from any of the extra search paths. If set toyes, mkosi will automatically add an extra tools tree image and use it as the tools tree. This image can be further configured using the settings below and withmkosi.tools.conf, which can either be a file or directory containing extra configuration for the default tools tree. See the TOOLS TREE section for further details.ToolsTreeDistribution=,--tools-tree-distribution=โ Set the distribution to use for the default tools tree. Defaults to the host distribution except if the host (not target) distribution is Ubuntu, which defaults to Debian, and RHEL, CentOS, Alma and Rocky, which default to Fedora, orcustomif the distribution of the host is not a supported distribution.ToolsTreeRelease=,--tools-tree-release=โ Set the distribution release to use for the default tools tree. By default, the hardcoded default release in mkosi for the distribution is used.ToolsTreeProfiles=,--tools-tree-profile=โ Set the profiles to enable for the default tools tree. Takes a comma-delimited list consisting ofdevel,misc,package-managerandruntime. By default, all profiles exceptdevelandguiare enabled. Thedevelprofile contains tools required to build (C/C++) projects. Themiscprofile contains various useful tools that are handy to have available in scripts. The package manager profile contains package managers and related tools other than those native to the tools tree distribution. Theruntimeprofile contains the tools required to boot images in asystemd-nspawncontainer or in a virtual machine. Theguiprofile contains tools required to boot images in a virtual machine with audio and graphics support.ToolsTreeMirror=,--tools-tree-mirror=โ Set the mirror to use for the default tools tree. By default, the default mirror for the tools tree distribution is used.ToolsTreeSnapshot=,--tools-tree-snapshot=โ Same asSnapshot=but for the default tools tree. By default, if the tools tree distribution matches the imageโs distribution, the imageโs snapshot is used.ToolsTreeRepositories=,--tools-tree-repository=โ Same asRepositories=but for the default tools tree.ToolsTreeSandboxTrees=,--tools-tree-sandbox-tree=โ Same asSandboxTrees=but for the default tools tree.ToolsTreePackages=,--tools-tree-package=โ Extra packages to install into the default tools tree. Takes a comma separated list of package specifications. This option may be used multiple times, in which case the specified package lists are combined.ToolsTreePackageDirectories=,--tools-tree-package-directory=โ Same asPackageDirectories=but for the default tools tree.ToolsTreeCertificates=,--tools-tree-certificates=โ Specify whether to use certificates and keys from the tools tree. Enabled by default. If enabled,/etc/pki/ca-trust,/etc/pki/tls,/etc/ssl,/etc/ca-certificates, and/var/lib/ca-certificatesfrom the tools tree are used. Otherwise, these directories are picked up from the host.ExtraSearchPaths=,--extra-search-path=โ List of colon-separated paths to look for tools in, before using the regular$PATHsearch path.Incremental=,--incremental=,-iโ okay bestie, this one takes eitherstrictor a boolean value as its argument and enables incremental build mode. In this mode, a copy of the OS image gets created immediately after all OS packages are installed and the prepare scripts have executed, but before themkosi.buildscripts are invoked (or anything that happens after them). On subsequentmkosiinvocations with the-iswitch, that cached image may be used to skip OS package installation, which drastically speeds up repetitive build times. Note that while there is some rudimentary cache invalidation, it is definitely not perfect. To force a rebuild of the cached image, combine-iwith-ffso the cached image is first removed and then re-created. If set tostrict, the build fails if a previously built cached image does not exist. ๐CacheOnly=,--cache-only=โ takes one ofauto,metadata,always, ornever; defaults toauto. Ifalways, the package manager is instructed not to contact the network. That gives a minimal level of reproducibility, as long as the package cache is already fully populated. If set tometadata, the package manager can still download packages, but we wonโt sync the repository metadata. If set toauto, the repository metadata is synced when no image has ever been built before, or โ and all cached images are invalidated in this case โ when every image was previously cached (seeIncremental=) and at least one of those caches is now out of date. Packages can be downloaded during the build. If set tonever, repository metadata is always synced and packages can be downloaded during the build. โจSandboxTrees=,--sandbox-tree=โ a comma-separated list of colon-separated path pairs. The first path of each pair is a directory to copy into the mkosi sandbox before executing a tool. The second path of each pair is the target directory inside the sandbox. If the second path is not provided, the directory is copied on top of the root directory of the sandbox. The second path is always interpreted as an absolute path. If themkosi.sandbox/directory is found in the local directory, it is used for this purpose with the root directory as target (also see the FILES section below). mkosi will look for the package manager configuration and related files in the configured sandbox trees. Unless specified otherwise, it will use the configuration files from their canonical locations in/usror/etcin the sandbox trees. For example, it will look for/etc/dnf/dnf.confin the sandbox trees ifdnfis used to install packages. See PACKAGE MANAGER-SPECIFIC BEHAVIOUR for further details. ๐ฆWorkspaceDirectory=,--workspace-directory=โ path to a directory where to store data required temporarily while building the image. This directory should have enough space to store the full OS image, though in most modes the actually used disk space is smaller. If not specified, a subdirectory of$XDG_CACHE_HOME(if set),$CACHE_DIRECTORY(if set),$HOME/.cache(if set), or/var/tmpis used. The data in this directory is removed automatically after each build. Itโs safe to manually remove the contents of this directory if an mkosi invocation is aborted abnormally โ for example, due to a reboot or power failure. ๐ซถCacheDirectory=,--cache-directory=โ takes a path to a directory to use as the incremental cache directory for the incremental images produced when theIncremental=option is enabled. If this option is not used, but amkosi.cache/directory is found in the local directory, it is automatically used for this purpose. ๐CacheKey=,--cache-key=โ specifies the subdirectory within the cache directory where to store the cached image. This may include both the regular specifiers (see Specifiers) and special delayed specifiers that are expanded after config parsing has finished, instead of during config parsing, which are described below. The default format for this parameter is&d~&r~&a~&I. The specifier cheat sheet is:&&โ&character&dโDistribution=&rโRelease=&aโArchitecture=&iโImageId=&vโImageVersion=&Iโ subimage name withinmkosi.images/ormainNote that all images within a build must have a unique cache key. ๐
PackageCacheDirectory=,--package-cache-dir=โ takes a path to a directory to use as the package cache directory for the distribution package manager used. If unset, but amkosi.pkgcache/directory is found in the local directory, it is automatically used for this purpose; otherwise a suitable directory in the userโs home directory or system is used. ๐BuildDirectory=,--build-directory=โ takes a path to a directory to use as the build directory for build systems that support out-of-tree builds (such as Meson). The directory used this way is shared between repeated builds, and allows the build system to reuse artifacts (such as object files, executable, ...) generated on previous invocations. The build scripts can find the path to this directory in the$BUILDDIRenvironment variable. This directory is mounted into the imageโs root directory whenmkosi-chrootis invoked during execution of the build scripts. If this option is not specified, but a directorymkosi.builddir/exists in the local directory, it is automatically used for this purpose (also see the FILES section below). ๐งBuildKey=,--build-key=โ specifies the subdirectory within the build directory where to store incremental build artifacts. This may include both the regular specifiers (see Specifiers) and special delayed specifiers that are expanded after config parsing has finished, instead of during config parsing โ the same delayed specifiers that are supported byCacheKey=. The default format for this parameter is&d~&r~&a. To disable usage of a build subdirectory completely, assign a literal-to this setting. ๐UseSubvolumes=,--use-subvolumes=โ takes a boolean orauto. Enables or disables use of btrfs subvolumes for directory tree outputs. If enabled, mkosi will create the root directory as a btrfs subvolume and use btrfs subvolume snapshots where possible to copy base or cached trees, which is much faster than doing a recursive copy. If explicitly enabled and btrfs is not installed or subvolumes cannot be created, an error is raised. Ifauto, missing btrfs or failures to create subvolumes are ignored. ๐ธRepartOffline=,--repart-offline=โ specifies whether to build disk images using loopback devices. Enabled by default. When enabled,systemd-repartwill not use loopback devices to build disk images. When disabled,systemd-repartwill always use loopback devices to build disk images. Note that when usingRepartOffline=no, mkosi cannot run unprivileged and the image build has to be done as the root user outside of any containers and with loopback devices available on the host system. There are currently two known scenarios whereRepartOffline=nohas to be used. The first is when usingSubvolumes=in a repart partition definition file, as subvolumes cannot be created without using loopback devices. The second is when creating a system with SELinux and an XFS root partition. Becausemkfs.xfsdoes not support populating an XFS filesystem with extended attributes, loopback devices have to be used to ensure the SELinux extended attributes end up in the generated XFS filesystem. ๐History=,--history=โ takes a boolean. If enabled, mkosi will write the configuration provided via the CLI for the latest build to the.mkosi-privatesubdirectory in the directory from which it was invoked. These arguments are then reused as long as the image is not rebuilt, to avoid having to specify them over and over again. To give an example of why this is useful: if you runmkosi -O my-custom-output-dir -ffollowed bymkosi vm, mkosi will fail saying the image hasnโt been built yet. If you runmkosi -O my-custom-output-dir --history=yes -ffollowed bymkosi vm, it will boot the image built in the previous step as expected. ๐ญBuildSources=,--build-sources=โ takes a comma-separated list of colon-separated path pairs. The first path of each pair refers to a directory to mount from the host. The second path of each pair refers to the directory where the source directory should be mounted when running scripts. Every target path is prefixed with/work/src, and all build sources are sorted lexicographically by their target before mounting, so that top level paths are mounted first. If not configured explicitly, the current working directory is mounted to/work/src. ๐BuildSourcesEphemeral=,--build-sources-ephemeral=โ takes a boolean or the special valuebuildcache; disabled by default. Configures whether changes to source directories, the working directory, and stuff configured usingBuildSources=are persisted. If enabled, all source directories will be reset to their original state every time after running all scripts of a specific type (except sync scripts). ๐ฅ๐ฃ๐ฅ If set tobuildcache, the overlay is not discarded when running build scripts, but saved to the build directory configured viaBuildDirectory=, and will be reused on subsequent runs. The overlay is still discarded for all other scripts. This option can be used to implement more advanced caching of builds, but can lead to unexpected states of the source directory. When using this option, a build directory must be configured. ๐ฅ๐ฃ๐ฅ ๐ฅEnvironment=,--environment=โ this is how you sneak env vars into the environment that package managers and theprepare/build/postinstall/finalizescripts get run with ๐ซถ. Takes a space-separated list of variable assignments or just plain variable names โ and if you go the plain-name route, the values get passed through from the environment mkosi was invoked in (iykyk, it just inherits them). You can specify this option more than once and every listed variable gets set. Set the same variable twice? The later one wins, no cap. ๐EnvironmentFiles=,--env-file=โ takes a comma-separated list of paths to files full of environment variable definitions that get added to the scripting environment โจ. It'll usemkosi.envif it finds one in the local directory. Order matters bestie: variables are read first frommkosi.envif it exists, then from the list of files you gave, then from theEnvironment=settings. SoEnvironment=is the final boss ๐WithTests=,--with-tests=,-Tโ if you set this to false (or when the command-line option is used), the$WITH_TESTSenvironment variable gets set to0when themkosi.buildscripts are invoked ๐งช. Build scripts are supposed to use this to bypass any unit or integration tests that normally run during the source build process. Big caveat though: this option has no effect unless themkosi.buildbuild scripts actually honor it ๐ญ it's giving "I sent the memo but nobody read it".WithNetwork=,--with-network=โ when true, enables network connectivity while the build scriptsmkosi.buildare invoked ๐. By default, build scripts run with networking turned off (yeah, off, like airplane mode but make it build). The$WITH_NETWORKenvironment variable gets passed to themkosi.buildbuild scripts, indicating whether the build is done with or without network ๐ฆProxyUrl=,--proxy-url=โ configure a proxy to be used for all outgoing network connections ๐. All the various tools mkosi invokes that can be configured with a proxy get configured to use this one. mkosi also sets a bunch of well-known environment variables specifying the proxy, for any programs it invokes that might need internet access ๐ProxyExclude=,--proxy-exclude=โ configure hostnames that should NOT go through the proxy, like a VIP list but the opposite ๐ . Takes a comma-separated list of hostnames.ProxyPeerCertificate=,--proxy-peer-certificate=โ point this at a file containing certificates used to verify the proxy ๐. Defaults to the system-wide certificate store. Currently, setting a proxy peer certificate is only supported whendnfordnf5is used to build the image โ so don't come for it if you're on something else.ProxyClientCertificate=,--proxy-client-certificate=โ a file containing the certificate used to authenticate the client with the proxy ๐. Currently only supported whendnfordnf5is used to build the image.ProxyClientKey=,--proxy-client-key=โ a file containing the private key used to authenticate the client with the proxy ๐๏ธ. Defaults to the proxy client certificate if one is provided. Currently only supported whendnfordnf5is used to build the image.MakeScriptsExecutable=,--make-scripts-executable=โ if one of the hook scripts (see the SCRIPTS section) isn't marked as executable, mkosi will attempt tochmodit instead of just failing outright ๐. Defaults tono. Lowkey a lifesaver when you forget the chmod, we've all been there.ForeignUIDRange=,--foreign-uid-range=โ if enabled, directory images will be owned by the foreign UID range on disk instead of the user's UID ๐. Heads up though: enabling this option is required to use directory images withmkosi shell,mkosi bootandmkosi vm. And on the host side,systemd-nsresourcedandsystemd-mountfsdv260 or newer are required to make use of this option ๐ซถDelegateRanges=,--delegate-ranges=โ sets the number of delegated ranges in the foreign UID range ๐. Defaults to3for regular users and0for root.
๐ฆ [Runtime] Section ๐ฆ (previously known as the [Host] section)
NSpawnSettings=,--settings=โ specifies a.nspawnsettings file forsystemd-nspawnto use in thebootandshellverbs, and to place next to the generated image file ๐. Super useful for configuring thesystemd-nspawnenvironment when the image is run. And if you don't use this setting but there's anmkosi.nspawnfile chilling in the local directory, it gets automatically used for this purpose โจVirtualMachineMonitor=,--vmm=โ configures which virtual machine monitor to use ๐ฅ๏ธ. Takes one ofqemuorvmspawn. Defaults toqemu. When set toqemu, the image is booted withqemu. Most output formats can be booted in qemu ๐ . Any arguments you specify after the verb get appended to the qemu invocation and are interpreted as extra qemu command line arguments. When set tovmspawn,systemd-vmspawnis used to boot up the image โ but vmspawn only supportsdiskanddirectorytype images, so know that going in ๐. Any arguments specified after the verb get appended to thesystemd-vmspawninvocation and are interpreted as extra vmspawn options and extra kernel command line arguments.Console=,--console=โ configures how to set up the console of the VM ๐. Takes one ofinteractive,read-only,native,gui, orheadless. Defaults tointeractive.interactivegives you an interactive terminal interface to the VM ๐read-onlyis similar but strictly read-only, i.e. it does not accept any input from the usernativealso provides a TTY-based interface, but uses qemu's native implementation (which means the qemu monitor is available ๐)guishows the qemu graphical UI ๐ธheadlessruns the VM without any console attached โ perfect for fully automated or scripted VM usage. Note thatheadlessis only supported by theqemuverb!
CPUs=,--cpus=โ configures the number of CPU cores to assign to the guest when booting a virtual machine ๐ง . Defaults to2. Set it to0and the number of CPUs available to the mkosi process will be used instead โ main character energy, take it all ๐RAM=,--ram=โ configures the amount of RAM assigned to the guest when booting a virtual machine ๐. Defaults to2G.MaxMem=,--maxmem=โ configures the maximum amount of memory the guest may deploy in total (that's RAM plus hotplug memory devices) ๐ฅ. Defaults to the amount of RAM configured.KVM=,--kvm=โ configures whether KVM acceleration should be used when booting a virtual machine โก. Takes a boolean value orauto. Defaults toauto.CXL=,--cxl=โ configures whether CXL devices are enabled for a given machine ๐. Only valid if the architecture supports cxl! Takes a boolean value. Defaults tofalse.VSock=,--vsock=โ configures whether to provision a vsock when booting a virtual machine ๐ก. Takes a boolean value orauto. Defaults toauto.VSockCID=,--vsock-cid=โ configures the vsock connection ID to use when booting a virtual machine ๐ข. Takes a number in the interval[3, 0xFFFFFFFF)orhashorauto. Defaults toauto.- Set it to
hashand the connection ID gets derived from the full path to the image ๐ฆ - Set it to
autoand mkosi will try to find a free connection ID automatically โจ - Otherwise, the number you provide is used as is, no notes ๐
- Set it to
TPM=,--tpm=โ configure whether to use a virtual TPM when booting a virtual machine ๐. Takes a boolean value orauto. Defaults toauto.Removable=,--removable=โ configures whether to attach the image as a removable device when booting a virtual machine ๐. Takes a boolean value. Defaults tono. When enabled, the root disk is always attached as a SCSI device regardless of theDiskType=setting ๐DiskType=,--disk-type=โ configures the disk type to use for the root disk when booting a virtual machine ๐พ. Takes one ofvirtio-blk,virtio-scsi,nvme, orscsi-cd. Defaults tovirtio-blk. Usescsi-cdto attach the image as a CD-ROM drive โ that's the move for booting El Torito ISO images ๐Firmware=,--firmware=โ configures the virtual machine firmware to use ๐ง. Takes one ofuefi,uefi-secure-boot,linux,linux-noinitrdorauto. Defaults toauto.uefiuses the OVMF firmware without secure boot supportuefi-secure-bootuses the OVMF firmware with secure boot support ๐linuxuses direct kernel boot โ peep theLinux=option for more details on which kernel image is used with direct kernel bootlinux-noinitrdis identical tolinuxexcept that no initrd is used ๐autousesuefi-secure-bootif possible andlinuxotherwise โจ
FirmwareVariables=,--firmware-variables=โ configures the path to the virtual machine firmware variables file to use ๐. Currently, this option is only taken into account when theuefioruefi-secure-bootfirmware is used. If you don't specify it, mkosi will search for the default variables file and use that instead ๐microsoftโ a firmware variables file with the Microsoft secure boot certificates already enrolled will be used ๐microsoft-mokโ that same Microsoft-certificates file gets extended with aMokListvariable containing the secure boot certificate fromSecureBootCertificate=๐. This is intended to be used together with shim binaries signed by the distribution and locally signed EFI binaries.customโ the secure boot certificate fromSecureBootCertificate=will be enrolled into the default firmware variables file ๐- Also,
virt-fw-varsfrom the virt-firmware project can be used to customize OVMF variable files, if you're feeling fancy โจ
Linux=,--linux=โ sets the kernel image used for qemu direct kernel boot ๐ง. If you don't set it, mkosi falls back to the kernel given on the command line (the-kerneloption), or the latest kernel that got installed into the image โ and if no kernel was installed into the image at all, it just fails ๐. Heads up: when thecpiooutput format is in play, direct kernel boot is used no matter what firmware you configured. Depending on that firmware, qemu might boot the kernel itself or go through the configured firmware instead. This setting can include both the regular specifiers (see Specifiers) and the special delayed specifiers, which are expanded after config parsing has finished rather than during it โ those are described below ๐. The specifiers you get:&&โ expands to the&character&bโ expands to the final build directory (including subdirectory)
Drives=,--drive=โ add a drive, bestie ๐๐จ. Takes a colon-delimited string shaped like<id>:<size>[:<directory>[:<options>[:<file-id>[:<flags>]]]]. Breaking it down:idis the ID assigned to the drive โ this can be used as thedrive=property in various qemu devices.sizeis the size of the drive, in bytes, and the suffixesK,MandGalso work for kilobytes, megabytes and gigabytes respectively.directoryoptionally says which directory to create the file backing the drive in; if unset, the file gets created under/var/tmp.optionsoptionally specifies extra comma-delimited properties passed verbatim to qemu's-blockdevoption.file-idis the ID of the file backing the drive โ if unset it defaults to the drive ID, and drives with the same file ID will share the backing file (the directory and size of the file are determined from the first drive with a given file ID).flagstakes a comma-separated list of drive flags, which currently only supportspersistโ andpersistdetermines whether the drive is persisted across qemu invocations. The files backing the drives get created with the schema/<directory>/mkosi-drive-<machine-or-image-name>-<file-id>. You can skip values by setting them to the empty string โ so specifying e.g.myfs:1G::::persistwill create a persistent drive under/var/tmp/mkosi-drive-main-myfsโจ. Example usage:
[Runtime]
Drives=btrfs:10G
ext4:20G
QemuArgs=-device nvme,serial=btrfs,drive=btrfs
-device nvme,serial=ext4,drive=ext4
QemuArgs=โ space-delimited list of extra arguments to pass when invoking qemu ๐ฅ.Ephemeral=,--ephemeral=โ when you use this with theshell,boot, orvmverbs, it runs that verb on a temporary snapshot of the output image, and that snapshot is removed immediately when the container terminates ๐ซง. Ngl, taking that temporary snapshot is way more efficient on filesystems that support reflinks natively (btrfs or xfs) than on the more traditional ones that don't (ext4).Credentials=,--credential=โ set credentials to be passed tosystemd-nspawnor the virtual machine respectively whenmkosi shell/bootormkosi vmare used ๐. Takes a space separated list of values, which can be eitherkey=valuepairs or paths. If a path is provided: if it's a file, the credential name will be the name of the file; if the file is executable, the credential value will be the output of executing the file; otherwise the credential value is the contents of the file. If the path is a directory, the exact same logic applies to each file in that directory. Note that values are only treated as paths if they do not contain the delimiter (=).KernelCommandLineExtra=,--kernel-command-line-extra=โ set extra kernel command line entries that get appended to the kernel command line at runtime when booting the image ๐ฅพ. In a container, these are passed as extra arguments tosystemd. In a VM, they're appended to the kernel command line via the SMBIOSio.systemd.stub.kernel-cmdline-extraOEM string. This will only be picked up bysystemd-bootandsystemd-stubversions newer than or equal to v254, so likeโฆ check your version fr.RuntimeTrees=,--runtime-tree=โ takes a colon-separated pair of paths ๐ณ. The first path is a directory to mount into any machine (container or VM) started by mkosi. The second path is the target directory inside the machine โ and if you don't provide it, the directory is mounted at/root/srcin the machine. If the second path is relative, it's interpreted relative to/root/srcin the machine. For each mounted directory, the uid and gid of the user running mkosi are mapped to the root user in the machine โ meaning all the files and directories appear as if they're owned by root in the machine, and all new files and directories created by root in the machine in those directories will be owned by the user running mkosi on the host ๐. Note that when usingmkosi vmwith this feature, systemd v254 or newer has to be installed in the image.RuntimeSize=,--runtime-size=โ if specified, disk images are grown to the specified size when they're booted withmkosi bootormkosi vm๐. Takes a size in bytes, and the suffixesK,MandGalso work for kilobytes, megabytes and gigabytes respectively.RuntimeNetwork=,--runtime-network=โ takes one ofuser,interfaceornone, and defaults touser๐. Specifies the networking to set up when booting the image:usersets up usermode networking;interfacesets up a virtual network connection between the host and the image, which translates to a veth interface formkosi shellandmkosi bootand a tap interface formkosi vmandmkosi vmspawn. Note that withinterface, mkosi does not automatically configure the host interface โ it's expected that a recent version ofsystemd-networkdis running on the host, which will automatically configure the host interface of the link.RuntimeBuildSources=,--runtime-build-sources=โ mount the build sources configured withBuildSources=and the build directory (if one is configured) to the same locations in/workthat they were mounted to when running the build script, when usingmkosi bootormkosi vm๐ ๏ธ.BindUser=,--bind-user=โ bind the home directory of the current user into the container/vm ๐ . Takes a boolean. Disabled by default.SshKey=,--ssh-key=โ path to the X.509 private key in PEM format used to connect, via themkosi sshcommand, to a virtual machine started withmkosi vmand built with theSsh=option enabled (or withsystemd-ssh-generatorinstalled) ๐. If it's not configured andmkosi.keyexists in the working directory, it'll automatically be used for this purpose. Runmkosi genkeyto automatically generate a key inmkosi.key.SshCertificate=,--ssh-certificate=โ path to the X.509 certificate in PEM format to provision as the SSH public key in virtual machines started withmkosi vm๐. If it's not configured andmkosi.crtexists in the working directory, it'll automatically be used for this purpose. Runmkosi genkeyto automatically generate a certificate inmkosi.crt.Machine=,--machine=โ specify the machine name to use when booting the image ๐ท๏ธ. Can also be used to refer to a specific image when SSH-ing into an image (e.g.mkosi --image=myimage ssh). Note thatEphemeral=has to be enabled to start multiple instances of the same image. Defaults to the current working directory name.Register=,--register=โ takes a boolean value orauto๐. Specifies whether to register the vm/container withsystemd-machined. If enabled, mkosi fails if it can't register the vm/container withsystemd-machined. If disabled, mkosi will not register it. Ifauto, mkosi registers the vm/container withsystemd-machinedif it's available. Defaults toauto. Note that mkosi will try to register the machine withsystemd-machinedin the user session โ and systemd v259 or newer is required to havesystemd-machinedavailable in user sessions ๐.ForwardJournal=,--forward-journal=โ specify the path to which journal logs from containers and virtual machines should be forwarded ๐. If the path has the.journalextension, it's interpreted as a file to which the journal should be written; otherwise the path is interpreted as a directory to which the journal should be written. Note that systemd v256 or newer is required in the virtual machine for log forwarding to work. Also note: if a path with the.journalextension is given, the journal size is limited to 4G โ so configure an output directory instead of a file if your workload produces more than 4G worth of journal data ๐ญ.StorageTargetMode=,--storage-target-mode=โ specifies whether theserveverb should startsystemd-storagetmto serve disk images over NVME-TCP ๐พ. Takes a boolean value orauto. If enabled,systemd-storagetmis always started and mkosi will fail if it cannot start it. If disabled,systemd-storagetmis never started. Ifauto,systemd-storagetmis started if a disk image is being built, thesystemd-storagetmbinary is found, andmkosi serveis being invoked as the root user ๐.SysupdateDirectory=,--sysupdate-directory=โ points at the directory holding thesystemd-sysupdatetransfer definition files thatmkosi sysupdateuses. And bestie, ifmkosi.sysupdate/exists in your local directory, that gets used for this too ๐
Heads up though: mkosi sysupdate runs systemd-sysupdate with --transfer-source= set to the mkosi output directory. So to actually use that in a transfer definition file, set PathRelativeTo=explicit so the Path= setting for the transfer source gets interpreted relative to the mkosi output directory. Fr fr, configuring PathRelativeTo=explicit and Path=/ for the transfer source is generally all you need for the match pattern to be interpreted relative to the mkosi output directory โจ
๐ฆ [Match] Section ๐ฆ
Profiles=โ matches against the profiles you've configured.Distribution=โ matches against the configured distribution.Release=โ matches against the configured distribution release. If you use this condition and no distribution has been explicitly configured yet, the host distribution and release get used instead ๐Architecture=โ matches against the configured architecture. If this condition is used and no architecture has been explicitly configured yet, the host architecture is used.
Also Architecture=uefi is a whole vibe: use it to match against any architecture that supports UEFI โจ
Repositories=โ matches against repositories enabled with theRepositories=setting. Takes a single repository name.OutputFormat=โ matches against the configured value forOutputFormat=.PathExists=โ this condition is satisfied if the given path exists. Relative paths are interpreted relative to the parent directory of the config file that the condition is read from.ImageId=โ matches against the configured image ID, and yes it supports globs ๐ซถ If this condition is used and no image ID has been explicitly configured yet, this condition fails. (sad but true ๐ญ)ImageVersion=โ matches against the configured image version. Image versions can be prepended by the operators==,!=,>=,<=,<,>for rich version comparisons according to the UAPI group version format specification. If no operator is prepended, the equality operator is assumed by default. If this condition is used and no image version has been explicitly configured yet, this condition fails.Bootable=โ matches against the configured value for theBootable=feature. Takes a boolean value orauto.Bootloader=โ matches against the configured value forBootloader=.Format=โ matches against the configured value for theFormat=option. Takes an output format (see theFormat=option).SystemdVersion=โ matches against the systemd version on the host (as reported bysystemctl --version). Values can be prepended by the operators==,!=,>=,<=,<,>for rich version comparisons according to the UAPI group version format specification. If no operator is prepended, the equality operator is assumed by default.BuildSources=โ takes a build source target path (seeBuildSources=). This match is satisfied if any of the configured build sources uses this target path. So like, if we have amkosi.conffile containing:
[Build]
BuildSources=../abc/qed:kernel
and a drop-in containing:
[Match]
BuildSources=kernel
the drop-in will be included ๐ Any absolute paths passed to this setting are interpreted relative to the current working directory.
HostArchitecture=โ matches against the host's native architecture. See theArchitecture=setting for a list of possible values.ToolsTreeDistribution=โ matches against the configured tools tree distribution.ToolsTreeRelease=โ matches against the configured tools tree release.Environment=โ matches against a specific key/value pair configured withEnvironment=. If no value is provided, it just checks whether the given key is in the environment regardless of which value it has.Image=โ match against the current (sub)image name. The name of a subimage is its name inmkosi.images/(without any.confsuffix). The name of the top level image ismain. The main use case is letting you have a shared config that can be included by both the top level image and subimages by gating the universal settings behind aImage=mainmatch. Iconic behavior honestly ๐
Okay so here's the little cheat sheet showing which matchers support globs, which support rich comparisons, and the default value that gets matched against if no value has been configured at the time the config file is read. Read it like a mood board ๐
Profiles=โ globs: no ยท rich comparisons: no ยท default: match failsDistribution=โ globs: no ยท rich comparisons: no ยท default: match host distributionRelease=โ globs: no ยท rich comparisons: no ยท default: match host releaseArchitecture=โ globs: no ยท rich comparisons: no ยท default: match host architecturePathExists=โ globs: no ยท rich comparisons: no ยท default: n/aImageId=โ globs: yes ยท rich comparisons: no ยท default: match failsImageVersion=โ globs: no ยท rich comparisons: yes ยท default: match failsBootable=โ globs: no ยท rich comparisons: no ยท default: match auto featureFormat=โ globs: no ยท rich comparisons: no ยท default: match default formatSystemdVersion=โ globs: no ยท rich comparisons: yes ยท default: n/aBuildSources=โ globs: no ยท rich comparisons: no ยท default: match failsHostArchitecture=โ globs: no ยท rich comparisons: no ยท default: n/aToolsTreeDistribution=โ globs: no ยท rich comparisons: no ยท default: match the fallback tools tree distribution (seeToolsTreeDistribution=in[Build])ToolsTreeRelease=โ globs: no ยท rich comparisons: no ยท default: match default tools tree releaseEnvironment=โ globs: no ยท rich comparisons: no ยท default: n/aImage=โ globs: no ยท rich comparisons: no ยท default: n/a
๐ง [Include] ๐ง
Include=,--include=,-Iโ include extra configuration from the given file or directory. The extra configuration gets included immediately after parsing the setting, except when it's used on the command line โ in that case the extra configuration is included after parsing all command line arguments โญ
And ngl, each path containing extra configuration is only parsed once, even if you include it more than once with Include=. The builtin configs for the mkosi default initrd, default tools tree, default virtual machine image and default UKI addon can be included by including the literal value mkosi-initrd, mkosi-tools, mkosi-vm or mkosi-addon respectively ๐
Note: include names starting with either of the literalsmkosi-orcontrib-are reserved for use by mkosi itself. Like, don't touch, that's their lane ๐
๐ [Config] Section ๐
Profiles=,--profile=โ select the given profiles. A profile is a configuration file or directory in themkosi.profiles/directory. The configuration files and directories of each profile are included after parsing themkosi.conf.d/*.confdrop in configuration. Order matters bestie ๐Dependencies=,--dependency=โ the images that this image depends on, specified as a comma-separated list. All images configured in this option will be built before this image.
And when this setting is specified for the "main" image, it specifies which subimages should be built. See the BUILDING MULTIPLE IMAGES section for more information ๐ฅ
MinimumVersion=,--minimum-version=โ the minimum mkosi version required to build this configuration. If specified multiple times, the highest specified version is used.
The minimum version can also be specified as a git commit hash when prefixed with commit:, in which case mkosi must be executed from a git checkout and the specified git commit hash must be an ancestor of the currently checked out git commit in the repository that mkosi is being executed from. Very main character energy for a version requirement, honestly ๐ซถ
ConfigureScripts=,--configure-script=โ takes a comma-separated list of paths to executables that are used as the configure scripts for this image. Glob patterns are supported and matching paths are sorted alphabetically. See the SCRIPTS section for more information โจPassEnvironment=,--pass-environment=โ takes a list of environment variable names separated by spaces. When building multiple images, pass the listed environment variables to each individual subimage as if they were "universal" settings. See the BUILDING MULTIPLE IMAGES section for more information ๐
๐ธ [UKIProfile] Section ๐ธ
The UKIProfile section can be used in UKI profile config files which are passed to the UnifiedKernelImageProfiles= setting. The following settings can be specified in the UKIProfile section:
Profile=โ the contents of the.profilesection of the UKI profile. Takes a list of key/value pairs separated by=. TheID=key must be specified (no skipping, bestie ๐ค). See the UKI specification for a full list of possible keys.Cmdline=โ extra kernel command line options for the UKI profile. Takes a space delimited list of extra kernel command line arguments. Note that the final.cmdlinesection will the combination of the base.cmdlinesection and the extra kernel command line arguments specified with this setting ๐SignExpectedPcr=โ sign expected PCR measurements for this UKI profile. Takes a boolean. Enabled by default โ
โญ Specifiers โญ
The current value of various settings can be accessed when parsing configuration files by using specifiers. To write a literal % character in a configuration file without treating it as a specifier, use %% (escape it, bestie, or it'll have opinions ๐).
The following specifiers are understood:
Distribution=โ%dRelease=โ%rArchitecture=โ%aFormat=โ%tOutput=โ%oOutputDirectory=โ%OImageId=โ%iImageVersion=โ%v
There are also specifiers that are independent of settings:
...and here's the rest of the specifier table, bestie โ these ones are about where stuff lives ๐
%Cโ parent directory of the current config file ๐%Pโ the current working directory%Dโ the directory that mkosi was invoked in%Iโ name of the current subimage inmkosi.images๐ผ๏ธ
Finally, there are specifiers that are derived from a setting:
%Fโ the default filesystem of the configured distribution ๐ฝ
Heads up ๐ซถ โ the current working directory changes as mkosi parses its configuration. Specifically, each time mkosi parses a directory containing a mkosi.conf file, mkosi changes its working directory to that directory. It's giving "we live here now", and honestly? slay.
Also note that the directory mkosi was invoked in is influenced by the --directory= command line argument. so if you're confused about %D, check what you passed to that flag โจ
And here's the receipts for the directory specifiers โ the following table shows example values for %C, %P and %D:
For $D/mkosi.conf:
%Cโ$D%Pโ$D%Dโ$D
For $D/mkosi.conf.d/abc/abc.conf:
%Cโ$D/mkosi.conf.d%Pโ$D%Dโ$D
For $D/mkosi.conf.d/abc/mkosi.conf:
%Cโ$D/mkosi.conf.d/abc%Pโ$D/mkosi.conf.d/abc%Dโ$D
See how %P keeps sliding around? That's the working-directory thing. it's not a bug, it's a lifestyle ๐
๐ Supported distributions ๐
okay so you can make images that contain installations of these distributions, no cap:
- Fedora Linux ๐ฉ
- Debian
- Kali Linux ๐
- Ubuntu
- Arch Linux ๐น
- openSUSE ๐ฆ
- Mageia
- CentOS
- RHEL
- RHEL UBI
- OpenMandriva
- Rocky Linux ๐ชจ
- Alma Linux
- Azure Linux โ๏ธ
- postmarketOS ๐ฑ
- None (requires the user to provide a pre-built rootfs) ๐ซ
In theory, any distribution may be used on the host for building images containing any other distribution, as long as the necessary tools are available. Like, cross-distro building is real. Specifically, fr fr:
- Any distribution that packages
aptmay be used to build Debian, Kali or Ubuntu images. - Any distribution that packages
dnfmay be used to build images for any of the RPM-based distributions. - Any distro that packages
pacmanmay be used to build Arch Linux images. - Any distribution that packages
zyppermay be used to build openSUSE images. - Other distributions and build automation tools for embedded Linux systems such as Buildroot, OpenEmbedded and Yocto Project may be used by selecting the custom distribution, and populating the rootfs via a combination of base trees, skeleton trees, and prepare scripts. ๐งฐ
Currently, Fedora Linux packages all relevant tools as of Fedora 28. noted. ๐
โ ๏ธ Caveat time, bestie: when not using a custom mirror, RHEL images can only be built from a host system with a RHEL subscription (established using e.g. subscription-manager). No sub, no RHEL image. that's just how it is ๐ญ
๐ฅ EXECUTION FLOW ๐ฅ
okay this is the whole glow-up sequence โ the execution flow for mkosi build. Default values/calls are shown in parentheses. And real talk: when building with --incremental=yes, mkosi creates a cache of the distribution installation if not already existing, and replaces the distribution installation in consecutive runs with data from the cached one. Basically it remembers its own homework. iconique ๐ง
- Parse CLI options
- Parse configuration files
- Run configure scripts (
mkosi.configure) - If we're not running as root, unshare the user namespace and map the the current user to root in it. If available this uses
systemd-nsresourcedto acquire a delegated range in the foreign UID range, otherwise an unprivileged user namespace is set up by mkosi. (yes, "the the" โ we keep the original's chaos, it's canon ๐) - Unshare the mount namespace
- Remount the following directories read-only if they exist:
/usr/etc/opt/srv/boot/efi/media/mnt
Then, for each image, we execute the following steps. buckle up, there's 31 of them ๐ข:
- Copy sandbox trees into the workspace
- Sync the package manager repository metadata
- Run sync scripts (
mkosi.sync) - Copy base trees (
--base-tree=) into the image - Reuse a cached image if one is available
- Copy a snapshot of the package manager repository metadata into the image
- Copy skeleton trees (
mkosi.skeleton) into image - Install distribution and packages into image
- Run prepare scripts on image with the final argument (
mkosi.prepare) - Install build packages in overlay if any build scripts are configured
- Run prepare scripts on overlay with the build argument if any build scripts are configured (
mkosi.prepare) - Cache the image if configured (
--incremental=yes) - Run build scripts on image + overlay if any build scripts are configured (
mkosi.build) - Finalize the build if the output format
noneis configured - Copy the build scripts outputs into the image
- Copy the extra trees into the image (
mkosi.extra) - Run post-install scripts (
mkosi.postinst) - Write config files required for
Ssh=,Autologin=andMakeInitrd= - Install
systemd-bootand configure secure boot if configured (--secure-boot=yes) - Run
systemd-sysusers - Run
systemd-tmpfiles - Run
systemctl preset-all - Run
depmod - Run
systemd-firstboot - Run
systemd-hwdb - Remove packages and files (
RemovePackages=,RemoveFiles=) - Run SELinux relabel if a SELinux policy is installed
- Run finalize scripts (
mkosi.finalize) - Generate unified kernel image if configured to do so
- Generate final output format
- Run post-output scripts (
mkosi.postoutput)
like... the pipeline ate and left no crumbs ๐โจ
๐ SCRIPTS ๐
so to allow for image customization that cannot be implemented using mkosi's builtin features, mkosi supports running scripts at various points during the image build process that can customize the image as needed. your image, your rules ๐ฆ
Scripts are executed on the host system as root (either real root or root within the user namespace that mkosi created when running unprivileged) with a customized environment to simplify modifying the image. For each script, the configured build sources (BuildSources=) are mounted into the current working directory before running the script in the current working directory. $SRCDIR is set to point to the current working directory. The following scripts are supported:
- If
mkosi.configure(ConfigureScripts=) exists, it is executed before building the image. This script may be used to dynamically modify the configuration. It receives the configuration serialized as JSON on stdin and should output the modified configuration serialized as JSON on stdout. Note that this script only runs when building or booting the image (build,vm,bootandshellverbs). If a default tools tree is configured, it will be built before running the configure scripts and the configure scripts will run with the tools tree available. This also means that the modifications made by configure scripts will not be visible in the summary output. ๐ (yes, the changes are shy, they don't show up in the summary, that's the whole point) - If
mkosi.sync(SyncScripts=) exists, it is executed before the image is built. This script may be used to update various sources that are used to build the image. One use case is to rungit pullon various source repositories before building the image. Specifically, theBuildSourcesEphemeral=setting does not apply to sync scripts, which means sync scripts can be used to update build sources even ifBuildSourcesEphemeral=is enabled. ๐ lowkey the loophole we all needed - If
mkosi.prepare(PrepareScripts=) exists, it is first called with thefinalargument, right after the software packages are installed. It is called a second time with thebuildcommand line parameter, right after the build packages are installed and the build overlay mounted on top of the image's root directory . (that stray space before the period? iconic, we keep it ๐ ) This script has network access and may be used to install packages from other sources than the distro's package manager (e.g.pip,npm, ...), after all software packages are installed but before the image is cached (if incremental mode is enabled). In contrast to a general purpose installation, it is safe to install packages to the system (pip install,npm install -g) instead of in$SRCDIRitself because the build image is only used for a single project and can easily be thrown away and rebuilt so there's no risk of conflicting dependencies and no risk of polluting the host system. ๐ go off, install globally, it's fine here - If
mkosi.build(BuildScripts=) exists, it is executed with the build overlay mounted on top of the image's root directory. When running the build script,$DESTDIRpoints to a directory where the script should place any files generated it would like to end up in the image. Note thatmake-,automake-, andmeson-based build systems generally honor$DESTDIR, thus making it very natural to build source trees from the build script. After running the build script, the contents of$DESTDIRare copied into the image. ๐๏ธ - If
mkosi.postinst(PostInstallationScripts=) exists, it is executed after the (optional) build tree and extra trees have been installed. This script may be used to alter the images without any restrictions, after all software packages and built sources have been installed. ๐ช no restrictions, bestie, do what you want - If
mkosi.finalize(FinalizeScripts=) exists, it is executed as the last step of preparing an image. ๐ - If
mkosi.postoutput(PostOutputScripts=) exists, it is executed right after all the output files have been generated, before they are finally moved into the output directory. This can be used to generate additional or alternative outputs, e.g.SHA256FILESor SBOM manifests. ๐ฆ - If
mkosi.clean(CleanScripts=) exists, it is executed right after the outputs of a previous build have been cleaned up. A clean script can clean up any outputs that mkosi does not know about (e.g. artifacts fromSplitArtifacts=partitionsor RPMs built in a build script). Note that this script does not use the tools tree even if one is configured. ๐งน - If
mkosi.versionexists and is executable, it is run during configuration parsing and populatesImageVersion=with the output on stdout. This can be used for external version tracking, e.g. withgit describeordate '+%Y-%m-%d'. Note that this script is executed on the host system without any sandboxing. โ ๏ธ no sandbox, so like, be normal about it
all of these little guys run in the current working directory with $SRCDIR pointing at it, remember that and you're golden ๐ซถโจ
- If
mkosi.rootpwexists and is executable, it gets run during configuration parsing and it populatesRootPassword=with whatever it spits out on stdout โ yes bestie, the script literally feeds your root password in. This is how you randomly generate a password, and you can remember it by outputting it to stderr or by reading$MKOSI_CONFIGin another script (likemkosi.postoutput, for example) ๐ Heads up though: this script is executed on the host system with no sandboxing whatsoever, so keep it cute and keep it safe.
If a script uses the .chroot extension, mkosi will chroot into the image using mkosi-chroot (see below, it's coming) before executing the script. So like, if mkosi.postinst.chroot exists, mkosi will chroot into the image and run it as the post-installation script. Iconic behavior honestly โจ
And instead of a single file script, mkosi will also read all files in lexicographical order from appropriately named .d directories โ e.g. all files in a mkosi.build.d would be used as build scripts. This is supported by:
mkosi.sync.d,mkosi.prepare.d,mkosi.build.d,mkosi.postinst.d,mkosi.finalize.d,mkosi.postoutput.d, andmkosi.clean.d.
You can combine this with the .chroot extension too ๐ซถ So like, mkosi.build.d/01-foo.sh would be run without chrooting into the image, and mkosi.build.d/02-bar.sh.chroot would be run after chrooting into the image first. Mix and match, she contains multitudes.
๐ธ the environment variable lore (still part of SCRIPTS btw) ๐ธ
Scripts executed by mkosi receive the following environment variables, and ngl this list is a lot but every single one matters:
$ARCHITECTUREโ contains the architecture from theArchitecture=setting. IfArchitecture=is not set, it'll contain the native architecture of the host machine. See the documentation ofArchitecture=for possible values for this variable.$QEMU_ARCHITECTUREโ contains the architecture from$ARCHITECTUREin the format used by qemu. Super useful for finding the qemu binary (qemu-system-$QEMU_ARCHITECTURE).$EFI_ARCHITECTUREโ contains the architecture from$ARCHITECTUREin the format used by UEFI. It is unset on architectures that do not support UEFI (not ghosting you, it's just not there).$DISTRIBUTIONโ contains the distribution from theDistribution=setting.$RELEASEโ contains the release from theRelease=setting.$DISTRIBUTION_ARCHITECTUREโ contains the architecture from$ARCHITECTUREin the format used by the configured distribution.$PROFILESโ contains the profiles from theProfiles=setting as a comma-delimited string.$CACHEDโ set to1if a cached image is available,0otherwise.$CHROOT_SCRIPTโ contains the path to the running script relative to the image root directory. The primary usecase for this variable is in combination with themkosi-chrootscript. See the description ofmkosi-chrootbelow for more information.$SRCDIRโ contains the path to the directory mkosi was invoked from, with any configured build sources mounted on top. And$CHROOT_SRCDIRcontains the value that$SRCDIRwill have after invokingmkosi-chroot.$BUILDDIRโ only defined ifmkosi.builddirexists, and points to the build directory to use. This is useful for all build systems that support out-of-tree builds to reuse already built artifacts from previous runs.$CHROOT_BUILDDIRcontains the value that$BUILDDIRwill have after invokingmkosi-chroot.$DESTDIRโ a directory into which any installed software generated by a build script may be placed. This variable is only set when executing a build script.$CHROOT_DESTDIRcontains the value that$DESTDIRwill have after invokingmkosi-chroot.$OUTPUTDIRโ points to the staging directory used to store build artifacts generated during the build.$CHROOT_OUTPUTDIRcontains the value that$OUTPUTDIRwill have after invokingmkosi-chroot.$PACKAGEDIRโ points to the directory containing the local package repository. Build scripts can add more packages to the local repository by writing the packages to$PACKAGEDIR.$ARTIFACTDIRโ points to the directory that is used to pass around build artifacts generated during the build and make them available for use by mkosi. This is similar toPACKAGEDIR, but it's meant for artifacts that may not be packages understood by the package manager, e.g. initrds created by other initrd generators than mkosi. Build scripts can add more artifacts to the directory by placing them in$ARTIFACTDIR. Files in this directory are only available for the current build and are not copied out like the contents of$OUTPUTDIR.
mkosi will also use certain subdirectories of an artifacts directory to automatically use their contents at certain steps. Currently the following two subdirectories in the artifact directory are used by mkosi:
io.mkosi.microcodeโ all files in this directory are used as microcode files, i.e. they are prepended to the initrds in lexicographical order.io.mkosi.initrdโ all files in this directory are used as initrds and joined in lexicographical order.
It is recommended that users of $ARTIFACTDIR put things for their own use in a similar namespaced directory, e.g. local.my.namespace. Stay in your lane, but like, in a cute namespaced way ๐
$BUILDROOTโ the root directory of the image being built, optionally with the build overlay mounted on top depending on the script that's being executed.$WITH_DOCSโ either0or1depending on whether a build without or with installed documentation was requested (WithDocs=yes). A build script should suppress installation of any package documentation to$DESTDIRin case$WITH_DOCSis set to0.$WITH_TESTSโ either0or1depending on whether a build without or with running the test suite was requested (WithTests=no). A build script should avoid running any unit or integration tests in case$WITH_TESTSis0.$WITH_NETWORKโ either0or1depending on whether a build without or with networking is being executed (WithNetwork=no). A build script should avoid any network communication in case$WITH_NETWORKis0.$SOURCE_DATE_EPOCHโ defined if requested (SourceDateEpoch=TIMESTAMP,Environment=SOURCE_DATE_EPOCH=TIMESTAMPor the host environment variable$SOURCE_DATE_EPOCH). This is useful to make builds reproducible. See SOURCE_DATE_EPOCH for more information.$MKOSI_UIDand$MKOSI_GIDโ respectively are the uid, gid of the user that invoked mkosi.$MKOSI_CONFIGโ a file containing a json summary of the settings of the current image. This file can be parsed inside scripts to gain access to all settings for the current image. Obsessed with this one tbh, it's giving full transparency ๐$IMAGE_IDโ contains the identifier from theImageId=or--image-id=setting.$IMAGE_VERSIONโ contains the version from theImageVersion=or--image-version=setting.$MKOSI_DEBUGโ either0or1depending on whether debugging output is enabled.
Consult this (formerly a table, now a very extra list, but every checkmark is preserved fr fr) to know which script receives which environment variables. The eight script slots are, in order: configure, sync, prepare, build, postinst, finalize, postoutput, clean.
$ARCHITECTUREโconfigure,sync,prepare,build,postinst,finalize,postoutput,clean(all of them, she's everywhere ๐)$ARTIFACTDIRโprepare,build,postinst,finalize$BUILDDIRโbuild,postinst,finalize$BUILDROOTโprepare,build,postinst,finalize$CACHEDโsync$CHROOT_BUILDDIRโbuild$CHROOT_DESTDIRโbuild$CHROOT_OUTPUTDIRโpostinst,finalize$CHROOT_SCRIPTโprepare,build,postinst,finalize$CHROOT_SRCDIRโprepare,build,postinst,finalize$MKOSI_DEBUGโconfigure,sync,prepare,build,postinst,finalize,postoutput,clean$DESTDIRโbuild$DISTRIBUTIONโconfigure,sync,prepare,build,postinst,finalize,postoutput,clean$DISTRIBUTION_ARCHITECTUREโconfigure,sync,prepare,build,postinst,finalize,postoutput,clean$EFI_ARCHITECTUREโconfigure,sync,prepare,build,postinst,finalize,postoutput,clean$IMAGE_IDโconfigure,sync,prepare,build,postinst,finalize,postoutput,clean$IMAGE_VERSIONโconfigure,sync,prepare,build,postinst,finalize,postoutput,clean$MKOSI_CONFIGโsync,prepare,build,postinst,finalize,postoutput,clean$MKOSI_GIDโconfigure,sync,prepare,build,postinst,finalize,postoutput,clean$MKOSI_UIDโconfigure,sync,prepare,build,postinst,finalize,postoutput,clean$OUTPUTDIRโpostinst,finalize,postoutput,clean$PACKAGEDIRโprepare,build,postinst,finalize$PROFILESโconfigure,sync,prepare,build,postinst,finalize,clean$QEMU_ARCHITECTUREโconfigure$RELEASEโconfigure,sync,prepare,build,postinst,finalize,postoutput,clean$SOURCE_DATE_EPOCHโprepare,build,postinst,finalize,clean$SRCDIRโconfigure,sync,prepare,build,postinst,finalize,postoutput,clean$WITH_DOCSโprepare,build$WITH_NETWORKโprepare,build,postinst,finalize$WITH_TESTSโprepare,build
Additionally, when a script is executed, a few scripts are made available via $PATH to simplify common usecases. (Yes, scripts inside your scripts. It's scripts all the way down ๐โจ)
mkosi-chrootโ this one's the โจ main character โจ of the script squad. It chroots into the image and runs whatever command you hand it. But bestie, it does so much more than just chroot: it mounts a bunch of files and directories ($SRCDIR,$DESTDIR,$BUILDDIR,$OUTPUTDIR,$CHROOT_SCRIPT) into the image, and rewrites the matching environment variables so they point at the locations inside the image instead. It also mounts the APIVFS filesystems (/proc,/dev, ...) so that scripts and tools executed inside the chroot actually work properly, no cap. And it propagates/etc/resolv.conffrom the host into the chroot if you asked for it, so DNS resolution works inside the chroot too ๐ฆ. Once themkosi-chrootcommand exits, all those mount points get cleaned up. Slay.
So like, to invoke ls inside of the image, you'd do:
mkosi-chroot ls ...
Wanna run the entire script inside the image instead? Just add a .chroot suffix to the name (mkosi.build.chroot instead of mkosi.build, etc.) and you're iconic ๐
.
- For all of the supported package managers (
dnf,rpm,apt,dpkg,pacman,zypper), scripts of the same name get dropped into$PATHthat make absolutely sure these commands operate on the image's root directory with the configuration supplied by the user โ literally not on the host system. Which means from a script you can just do e.g.dnf install vimand it installsviminto the image. Obsessed ๐.
On top of that, mkosi-install, mkosi-reinstall, mkosi-upgrade and mkosi-remove will invoke the corresponding operation of the package manager being used to built the image.
gitis automatically invoked withsafe.directory=*to avoid permissions errors when running as the root user in a user namespace. No permission drama, periodt.useraddandgroupaddare automatically invoked with--root=$BUILDROOTwhen executed outside of the image.
When scripts are executed, any directories that are still writable are also made read-only (/home, /var, /root, ...) and only the minimal set of directories that need to be writable stay writable. Why? To make sure scripts can't mess with the host system when mkosi is running as root. Lowkey genius, highkey necessary ๐.
Heads up bestie: when executing scripts, all source directories are made ephemeral, which means all changes made to source directories while running scripts are thrown away after the scripts finish executing. Use the output, build or cache directories if you need to persist data between builds. Don't come crying to me when your edits vanish ๐.
๐ธ FILES ๐ธ
To make it easy to build images for development versions of your projects, mkosi can read configuration data from the local directory โ under the assumption that it's invoked from a source tree. Specifically, the following files are used if they exist in the local directory:
- The
mkosi.skeleton/directory ormkosi.skeleton.tararchive may be used to insert files into the image. The files are copied before the distribution packages are installed into the image. This allows creation of files that need to be provided early โ for example to configure the package manager or set systemd presets.
When using the directory, file ownership is not preserved: all files copied will be owned by root. To preserve ownership, use a tar archive.
- The
mkosi.extra/directory ormkosi.extra.tararchive may be used to insert additional files into the image, on top of what the distribution includes in its packages. They are similar tomkosi.skeleton/andmkosi.skeleton.tar, but the files are copied into the directory tree of the image after the OS was installed.
When using the directory, file ownership is not preserved: all files copied will be owned by root. To preserve ownership, use a tar archive.
- The
mkosi.sandbox/directory ormkosi.sandbox.tararchive may be used to configure the package manager without the files being inserted into the image. If the files should be included in the image, usemkosi.skeleton/andmkosi.skeleton.tarinstead.
When using the directory, file ownership is not preserved: all files copied will be owned by root. To preserve ownership, use a tar archive.
- The
mkosi.nspawnnspawn settings file will be copied into the same place as the output image file, if it exists. This is useful since nspawn looks for settings files next to image files it boots, for additional container runtime settings. Cute little sidecar situation โจ. - The
mkosi.cache/directory, if it exists, is automatically used as package download cache, in order to speed repeated runs of the tool. - The
mkosi.builddir/directory, if it exists, is automatically used as out-of-tree build directory, if the build commands in themkosi.buildscripts support it. Specifically, this directory will be mounted into the build container, and the$BUILDDIRenvironment variable will be set to it when the build scripts are invoked. A build script may then use this directory as build directory, for automake-style or ninja-style out-of-tree builds. This speeds up builds considerably, in particular when mkosi is used in incremental mode (-i): not only the image and build overlay, but also the build tree is reused between subsequent invocations. Note that if this directory does not exist the$BUILDDIRenvironment variable is not set, and it is up to the build scripts to decide whether to do an in-tree or an out-of-tree build, and which build directory to use. - The
mkosi.rootpwfile can be used to provide the password for the root user of the image. If the password is prefixed withhashed:it is treated as an already hashed root password. The password may optionally be followed by a newline character which is implicitly removed. The file must have an access mode of0600or less. If this file does not exist, the distribution's default root password is set (which usually means access to the root user is blocked). ๐ - The
mkosi.passphrasefile provides the passphrase to use when LUKS encryption is selected. It should contain the passphrase literally, and not end in a newline character (i.e. in the same format ascryptsetupand/etc/crypttabexpect the passphrase files). The file must have an access mode of0600or less. - The
mkosi.crtandmkosi.keyfiles contain an X.509 certificate and PEM private key to use when signing is required (UEFI SecureBoot, verity, ...). - The
mkosi.output/directory is used to store all build artifacts. Everything lands here ๐. - The
mkosi.credentials/directory is used as a source of extra credentials similar to theCredentials=option. For each file in the directory, the filename will be used as the credential name and the file contents become the credential value โ or, if the file is executable, mkosi will execute the file and the command's output to stdout will be used as the credential value. Output to stderr will be ignored. Credentials configured withCredentials=take precedence over files inmkosi.credentials. - The
mkosi.repart/directory is used as the source forsystemd-repartpartition definition files which are passed tosystemd-repartwhen building a disk image. If it does not exist and theRepartDirectories=setting is not configured, mkosi will default to the following partition definition files:
00-esp.conf (if we're building a bootable image):
[Partition]
Type=esp
Format=vfat
CopyFiles=/boot:/
CopyFiles=/efi:/
SizeMinBytes=512M
SizeMaxBytes=512M
05-bios.conf (if we're building a BIOS bootable image):
[Partition]
# UUID of the grub BIOS boot partition which grubs needs on GPT to
# embed itself into.
Type=21686148-6449-6e6f-744e-656564454649
SizeMinBytes=1M
SizeMaxBytes=1M
10-root.conf
[Partition]
Type=root
Format=<distribution-default-filesystem>
CopyFiles=/
Minimize=guess
Note that if either mkosi.repart/ is found or RepartDirectories= is used, we will not use any of the default partition definitions. It's one or the other, bestie ๐ซถ.
All these files are optional. Yes, all of them. Breathe ๐.
Note that the location of all these files may also be configured during invocation via command line switches, and as settings in mkosi.conf, in case the default settings are not acceptable for a project.
๐ง CACHING ๐ง
mkosi supports three different caches for speeding up repetitive re-building of images. Specifically:
- The package cache of the distribution package manager is cached between builds. The location can beconfigured with the
PackageCacheDirectory=option or themkosi.pkgcache/directory. This form of caching relies on the distribution's package manager, and caches distribution packages (RPM, deb, ...) after they are downloaded, but before they are unpacked. - The repository metadata of the distribution is cached if a cache directory is configured with
CacheDirectory=or themkosi.cache/directory. When the repository metadata is resynced is controlled with theCacheOnly=option, which by default will always resync the repository metadata unless every image is cached (see theIncremental=option). Whenever the repository metadata is resynced, all cached images are invalidated. Rude, but fair ๐. - If the incremental build mode is enabled with
Incremental=yes, cached copies of the final image and build overlay are made immediately before the build sources are copied in (for the build overlay) or the artifacts generated bymkosi.buildare copied in (in case of the final image). This form of caching allows bypassing the time-consuming package unpacking step of the distribution package managers, but is only effective if the list of packages to use remains stable, but the build sources and its scripts change regularly. - Finally, between multiple builds the build artifact directory may be shared, using the
BuildDirectory=option or themkosi.builddir/directory. This directory allows build systems such as Meson to reuse already compiled sources from a previous build, thus speeding up the build process of amkosi.buildbuild script. โก
โจ TOOLS TREES โจ
okay so picture this: a secondary image that mkosi itself uses to build the actual images. That's a tools tree, bestie ๐ It's giving "build the thing that builds the thing," and honestly? iconic.
Why do you care? Two huge reasons:
- it makes image builds way more reproducible ๐
- it lets you use newer tooling that isn't in the host distro running mkosi yet (iykyk, no waiting for your distro to catch up)
How do you get one? Three ways, pick your fighter:
- hand it over with the
ToolsTree=option - drop it in the
mkosi.toolsdirectory - let mkosi build it automatically by setting
ToolsTree=yes
For most use cases, setting it is enough to just use the default tools trees โ and honestly, using a tools tree is recommended. Do it. Slay. ๐ฆ
Fully custom tools trees can be built like any other mkosi image, but mkosi ships a builtin include with the default tools tree packages so you don't have to hand-roll it:
mkosi --include=mkosi-tools --format=directory
Tools trees โ default ones included โ can be further customized via the different ToolsTree*= variables, plus the mkosi.tools.conf configuration file (or directory). The one thing you can't currently do is change the output format for tools trees via configuration files. Not yet. F in the chat ๐ญ
๐ง which packages go in the default tools trees?
So here's the vibe check: the following table (rip table formatting, we're doing lists now) shows for which distributions default tools tree packages are defined, and exactly which packages land in those default tools trees.
The eight distros in question โ say hi ๐ โ are Fedora, CentOS, Debian, Kali, Ubuntu, Arch, openSUSE, and postmarketOS.
So below, "all eight" literally means all eight of those, and every other entry lists its exact set. No cap, nothing dropped ๐
Included for all eight (Fedora, CentOS, Debian, Kali, Ubuntu, Arch, openSUSE, postmarketOS):
aclattrbashbtrfs-progsca-certificatescoreutilscpiocreaterepo_ccurldiffutilsdnfdosfstoolse2fsprogsedk2-ovmferofs-utilsfindutilsgitgrepjqkmodlessmtoolsnanoopenscopensshopensslpkcs11-providersedp11-kitqemusbsigntoolssocatsquashfs-toolsstraceswtpmsystemdukifytarutil-linuxvirtiofsdvirt-firmwarexfsprogsxzzstd
The rest, with exact distro lists (order as in the original columns):
aptโ Fedora, CentOS, Debian, Kali, Ubuntu, Archarchlinux-keyringโ Fedora, CentOS, Debian, Kali, Ubuntu, Archdebian-keyringโ Fedora, CentOS, Debian, Kali, Ubuntu, Arch, postmarketOSdistribution-gpg-keysโ Fedora, CentOS, Debian, Kali, Arch, openSUSE, postmarketOSgrub-toolsโ Fedora, CentOS, Debian, Kali, Ubuntu, Archkali-archive-keyringโ Kali only ๐ (one distro to rule them all)perfโ Fedora, CentOS, Debian, Kali, Arch, openSUSE, postmarketOSpacmanโ Fedora, CentOS, Debian, Kali, Ubuntu, Arch, postmarketOSpolicycoreutilsโ Fedora, CentOS, Debian, Kali, Ubuntu, openSUSE, postmarketOSubuntu-keyringโ Fedora, CentOS, Debian, Kali, Ubuntu, Archzypperโ Fedora, Debian, Kali, Ubuntu, Arch, openSUSE, postmarketOS
so yes, some of the "keyring" packages show up on distros that are not that distro. That's genuinely what the table says, don't come for me ๐
๐ PACKAGE MANAGER-SPECIFC BEHAVIOUR ๐
different package managers need different little treats from mkosi, so here's the per-family lore ๐ซถ
๐ Arch and other pacman-based distributions
mkosi generates a pacman.conf in the sandbox tree if one doesn't exist. If one does exist, a DownloadUser setting is always removed. And the generated config will always include all files with the .conf extension from /etc/pacman.d in the sandbox tree. Periodt.
๐ง Debian and other deb-based distributions
mkosi generates /etc/apt/sources.list.d/mkosi.sources in the sandbox tree if it does not exist. The configured Mirror= is used for everything except the security and debug repositories โ those always use http://deb.debian.org. No negotiating. ๐
๐ฅ Fedora and other distributions using DNF
mkosi generates /etc/yum.repos.d/mkosi.repo in the sandbox tree if it does not exist. Simple as that, bestie โจ
๐ฆ Opensuse and other distributions using Zypper
mkosi generates /etc/zypp/repos.d/mkosi.repo in the sandbox tree if it does not exist. Matching energy, different path, we love consistency ๐
๐ง BUILDING MULTIPLE IMAGES ๐ง
okay so bestie, if the mkosi.images/ directory exists, mkosi will load individual subimage configurations from it and build each of them. Image configurations can be either directories containing mkosi configuration files or regular files with the .conf extension. slay.
When image configurations are found in mkosi.images/, mkosi will build the images specified in the Dependencies= setting of the main image and all of their dependencies (or all of them if no images were explicitly configured using Dependencies= in the main image configuration). To add dependencies between subimages, the Dependencies= setting can be used as well. Subimages are always built before the main image. periodt.
When images are defined, mkosi will first read the main image configuration (configuration outside of the mkosi.images/ directory), followed by the image specific configuration.
Several โmultiversalโ settings apply to the default tools tree and to the main image and cannot be configured separately outside of the main image:
RepositoryKeyCheck=RepositoryKeyFetch=SourceDateEpoch=WorkspaceDirectory=PackageCacheDirectory=BuildSources=BuildSourcesEphemeral=ProxyClientCertificate=ProxyClientKey=ProxyExclude=ProxyPeerCertificate=ProxyUrl=MakeScriptsExecutable=
Several โuniversalโ settings apply to the main image and all its subimages and cannot be configured separately in subimages. The following settings are universal and cannot be configured in subimages:
Architecture=BuildDirectory=CacheDirectory=Distribution=ExtraSearchPaths=LocalMirror=Mirror=OutputDirectory=OutputMode=PackageDirectories=Release=RepartOffline=Repositories=SandboxTrees=ToolsTree=ToolsTreeCertificates=UseSubvolumes=SecureBootCertificate=SecureBootCertificateSource=SecureBootKey=SecureBootKeySource=VerityCertificate=VerityCertificateSource=VerityKey=VerityKeySource=VolatilePackageDirectories=WithNetwork=WithTests
There are also settings which are passed down to subimages but can be overridden. For these settings, values configured explicitly in the subimage will take priority over values configured on the CLI or in the main image config. Currently the following settings are passed down to subimages but can be overridden:
Incremental=CacheOnly=Profiles=ImageId=ImageVersion=SectorSize=CacheKey=BuildKey=CompressLevel=SignExpectedPcrKey=SignExpectedPcrKeySource=SignExpectedPcrCertificate=SignExpectedPcrCertificateSource=
Additionally, there are various settings that can only be configured in the main image but which are not passed down to subimages:
MinimumVersion=PassEnvironment=ToolsTreeDistribution=ToolsTreeRelease=ToolsTreeProfiles=ToolsTreeMirror=ToolsTreeSnapshot=ToolsTreeRepositories=ToolsTreeSandboxTrees=ToolsTreePackages=ToolsTreePackageDirectories=History=- Every setting in the
[Runtime]section
Images can refer to outputs of images they depend on. Specifically, for the following options, mkosi will only check whether the inputs exist just before building the image:
BaseTrees=ExtraTrees=Initrds=
To refer to outputs of an imageโs dependencies, simply configure any of these options with a relative path to the output to use in the output directory of the dependency. Or use the %O specifier to refer to the output directory.
A good example on how to build multiple images can be found in the systemd repository.
๐ธ ENVIRONMENT VARIABLES ๐ธ
$MKOSI_LESSโ overrides options forlesswhen it is invoked by mkosi to page output. iconic.$MKOSI_DNFโ can be used to override the executable used asdnf. This is particularly useful to select betweendnfanddnf5. Note that this variable is only honoured when no tools tree is in use; when a tools tree is active, thednfexecutable is autodetected from within the tools tree. iykyk.$MKOSI_DNF_DISABLE_PLUGINSโ can be used to disable dnf plugins. The value is forwarded to dnfโs--disableplugin=option.$MKOSI_DNF_ENABLE_PLUGINSโ can be used to enable dnf plugins. The value is forwarded to dnfโs--enableplugin=option.$EPEL_MIRRORโ can be used to override the default mirror location used for the epel repositories whenMirror=is used. By default mkosi looks for the epel repositories in thefedorasubdirectory of the parent directory of the mirror specified inMirror=. For example if the mirror is set tohttps://mirror.net/centos-streammkosi will look for the epel repositories inhttps://mirror.net/fedora/epel. no cap.SYSEXT_SCOPEandCONFEXT_SCOPEโ can be used to override the default value of the respectiveextension-releasefile when building a sysext or confext. By default the value is set toinitrd system portable.
โจ EXAMPLES โจ
Create and run a raw GPT image with ext4, as image.raw:
# mkosi -p systemd -i boot
Create and run a bootable GPT image, as foobar.raw:
$ mkosi -d fedora -p kernel-core -p systemd -p systemd-boot -p udev -o foobar.raw
# mkosi --output foobar.raw boot
$ mkosi --output foobar.raw vm
Create and run a Fedora Linux image in a plain directory:
# mkosi --distribution fedora --format directory boot
Create a compressed image.raw.xz with SSH installed and add a checksum file:
$ mkosi --distribution fedora --format disk --checksum=yes --compress-output=yes --package=openssh-clients
Inside the source directory of an automake-based project, configure mkosi so that simply invoking mkosi without any parameters builds an OS image containing a built version of the project in its current state:
$ cat >mkosi.conf <<EOF
[Distribution]
Distribution=fedora
[Output]
Format=disk
[Content]
Packages=kernel,systemd,systemd-udev,openssh-clients,httpd
BuildPackages=make,gcc,libcurl-devel
EOF
$ cat >mkosi.build <<EOF
#!/bin/sh
if [ "$container" != "mkosi" ]; then
exec mkosi-chroot "$CHROOT_SCRIPT" "$@"
fi
cd $SRCDIR
./autogen.sh
./configure --prefix=/usr
make -j `nproc`
make install
EOF
$ chmod +x mkosi.build
# mkosi -i boot
# systemd-nspawn -bi image.raw
๐ฆ Different ways to boot with vm ๐ฆ
The easiest way to boot a virtual machine is to build an image with the required components and let mkosi call qemu with all the right options:
$ mkosi -d fedora -p systemd-udev,systemd-boot,kernel-core build
$ mkosi -d fedora vm
...
fedora login: root (automatic login)
[root@fedora ~]#
The default is to boot with a text console only. In this mode, messages from the boot loader, the kernel, and systemd, and later the getty login prompt and shell all use the same terminal. It is possible to switch between the qemu console and monitor by pressing Ctrl+a then c. The qemu monitor may for example be used to inject special keys or shut down the machine quickly. Alternatively the machine can be shut down using Ctrl+a then x.
To boot with a graphical window, add --console=gui:
$ mkosi -d fedora --console=gui qemu
A kernel may be booted directly with mkosi vm -kernel ... -initrd ... -append '...'. This is a bit faster because no boot loader is used, and it is also easier to experiment with different kernels and kernel command lines. Note that despite the name, qemuโs -append option replaces the default kernel command line embedded in the kernel and any previous -append specifications.
The UKI is also copied into the output directory and may be booted directly:
$ mkosi vm -- -kernel mkosi.output/fedora~38/image.efi
When booting using an external kernel, we donโt need the kernel in the image, but we would still want the kernel modules to be installed.
It is also possible to do a direct kernel boot into a boot loader, taking advantage of the fact that systemd-boot(7) is a valid UEFI binary:
$ mkosi vm -- -kernel /usr/lib/systemd/boot/efi/systemd-bootx64.efi
In this scenario, the kernel is loaded from the ESP in the image by systemd-boot.
๐ REQUIREMENTS ๐
- mkosi requires a Linux kernel that provides
mount_setattr()which was introduced in 5.12. - mkosi currently requires systemd 254 or newer to build bootable disk images.
- mkosi requires various dependencies to be installed. If a tools tree is used, (see the
ToolsTree=documentation for more information), then mkosi will download all of its dependencies (except python and a package manager) itself. Otherwise, youโll need to install the required dependencies on your system. You can runmkosi dependenciesto get a list of the basic required dependencies. For regular mkosi use, we strongly recommend using a tools tree. bestie, seriously. - On Debian/Kali/Ubuntu it might be necessary to install the
ubuntu-keyring,ubuntu-archive-keyring,kali-archive-keyringand/ordebian-archive-keyringpackages explicitly, in addition toapt, depending on what kind of distribution images you want to build. - The minimum required Python version is 3.9.
๐ง Unprivileged User Namespaces ๐ง
mkosi needs unrestricted abilities to create and act within Linux namespaces. Some distributions restrict the creation of, or capabilities within, user namespaces, which breaks mkosi. not me finding out the hard way ๐ญ.
For information about Ubuntu, which implements such restrictions using AppArmor, see https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces. To enable unprivileged user namespaces on Ubuntu, run the following commands:
sudo sysctl -w kernel.apparmor_restrict_unprivileged_unconfined=0
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
To persist these sysctl settings across reboots, create /etc/sysctl.d/unprivileged-userns.conf with the following contents:
kernel.apparmor_restrict_unprivileged_unconfined=0
kernel.apparmor_restrict_unprivileged_userns=0
For other systems, try researching the kernel.unprivileged_userns_clone or user.max.user_namespace sysctls. you got this ๐
.
๐ FREQUENTLY ASKED QUESTIONS (FAQ) ๐
okay bestie, this is the part where everyone slides into the DMs asking the same stuff, so let's just get it all out in the open ๐
๐ซถ Why does mkosi vm with KVM not work on Debian/Kali/Ubuntu?
so here's the tea โ โ other distros are totally fine with letting you access /dev/kvm, but on Debian/Kali/Ubuntu that's only allowed for users in the kvm group. and because mkosi unshares a user namespace when it's running unprivileged, even if the calling user was in the kvm group, when mkosi unshares the user namespace to run unprivileged, it loses access to the kvm group โ and by the time we start qemu we don't have access to /dev/kvm anymore. ๐ญ it's giving tragic.
as a workaround, you can change the permissions of the device nodes to 0666, which is sufficient to make KVM work unprivileged. to persist these settings across reboots, copy /usr/lib/tmpfiles.d/static-nodes-permissions.conf to /etc/tmpfiles.d/static-nodes-permissions.conf and change the mode of /dev/kvm from 0660 to 0666. slay, problem solved โจ
๐ How do I add a regular user to an image?
you can use the following snippet in a post-installation script, bestie:
useradd --create-home --user-group $USER --password "$(openssl passwd -stdin -6 <$USER_PASSWORD_FILE)"
note that from systemd v256 onwards, if enabled, systemd-homed-firstboot.service will prompt to create a regular user on first boot if there are no regular users. so it might just do it for you, iconic behavior honestly ๐
๐ฆ Why do I see failures to chown files when building images?
when not running as root, your user is not able to change ownership of files to arbitrary owners. and various distributions still ship files in their packages that are not owned by the root user. ๐ when not running as root, mkosi maps the current user to root when invoking package managers, which means that changing ownership to root will work โ but changing ownership to any other user or group will fail.
note that chown calls are only suppressed when running package managers, but not when running scripts. if this is required, e.g. for a build script, you can set the MKOSI_CHROOT_SUPPRESS_CHOWN variable to a true value (1, yes, true) to suppress chown calls in mkosi-chroot and .chroot scripts.
if this behavior causes applications running in your image to misbehave, you can consider running mkosi as root, which avoids this problem. alternatively, if running mkosi as root is not desired, you can use unshare --map-auto --map-current-user --setuid 0 --setgid 0 to become root in a user namespace with more than one user โ assuming the UID/GID mappings in /etc/subuid and /etc/subgid are configured correctly. note that running mkosi as root or with unshare means that all output files produced by mkosi will not be owned by your current user anymore. ๐ lowkey a big deal.
note that for systemd services that need directories in /var owned by the service user and group, an alternative to shipping these directories in packages or creating them via systemd-tmpfiles is to use StateDirectory=, CacheDirectory= or LogsDirectory= in the service file โ which instructs systemd to create the directory when it first starts the service. ๐ซถ
alternatively, the z or Z directives for systemd-tmpfiles can be used to chown various directories and files to their owning user when the system first boots up.
๐ธ Why does portablectl inspect <image> / systemd-dissect <image> say my portable service isn't one?
systemd-dissect and portablectl inspect check for PORTABLE_PREFIXES= in os-release, and if the key is missing, they'll fail to recognise a portable service as one โ showing โ under Use as for in the case of systemd-dissect, or n/a under Portable Service for portablectl. ๐ญ rude.
since there is no good default to set for this key, and the generated portable service images will still attach properly even when the key is not set, mkosi doesn't set one. no cap.
you can set PORTABLE_PREFIXES= by setting Environment=PORTABLE_PREFIXES=XXX in your mkosi config. easy fix, bestie ๐
๐ง BaseTrees=/Overlay= do not work with nsresourced?
this is expected, and it needs upstream kernel changes expected to land in Linux 7.3. ๐ฐ๏ธ you can skip the usage of nsresourced by setting the environment variable MKOSI_FORCE_USERNS_FALLBACK to a true value.
โญ REFERENCES โญ
- Primary mkosi git repository on GitHub
mkosiโ A Tool for Generating OS Images introductory blog post by Lennart Poettering- The mkosi OS generation tool story on LWN
๐ SEE ALSO ๐
systemd-nspawn(1), systemd-repart(8), dnf(8)
and that's the whole page, bestie!! ๐ you made it to the end, I'm so proud of you fr fr ๐ this was mkosi(1) โ the source/version and date are down there in the footer where they always live, doing their little main-character thing. now go build something iconic ๐ฆโจ
๐ฆ where this page comes from
- arch rolling: mkosi 27.1-1 ยท LGPL-2.1-or-later
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms