<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://www.jwillikers.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.jwillikers.com/" rel="alternate" type="text/html" /><updated>2026-04-22T12:55:41+00:00</updated><id>https://www.jwillikers.com/feed.xml</id><title type="html">JWillikers</title><subtitle>Handy admin and dev guides from my myriad of tinkering</subtitle><author><name>Jordan Williams</name></author><entry><title type="html">Bazel: { Fast, Correct } — Choose One</title><link href="https://www.jwillikers.com/bazel-fast-correct-choose-one" rel="alternate" type="text/html" title="Bazel: { Fast, Correct } — Choose One" /><published>2025-07-14T00:00:00+00:00</published><updated>2025-07-14T00:00:00+00:00</updated><id>https://www.jwillikers.com/Bazel:%20%7B%20Fast,%20Correct%20%7D%20%E2%80%94%20Choose%20One</id><content type="html" xml:base="https://www.jwillikers.com/bazel-fast-correct-choose-one"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>When it comes to build systems and package management, there really is no silver bullet.
Everything would be so much easier if there were.
<a href="https://bazel.build/">Bazel</a> has been gaining popularity for a while now, and for good reason.
It&#8217;s very fast and offers both distributed caching and remote building, and supports multiple programming languages.
It&#8217;s also more correct than most of the competition.
Another sign of its popularity is the number of clones its spawned which includes <a href="https://buck2.build/">Buck2</a>, <a href="https://www.pantsbuild.org/">Pants</a>, and <a href="https://please.build/">Please</a>.<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>
Of those, only Bazel and Buck2 treat C&#43;&#43; as a first-class citizen, and from Buck2&#8217;s documentation you can find it says, "There are not yet mechanisms to build in release mode."<sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup>
<em>Wait.</em>
<em>Really?</em>
Enough of the clones.
I&#8217;ll just focus on Bazel here, since my primary target is C&#43;&#43; and I&#8217;d like to be able to make release builds.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="where_bazel_shines">Where Bazel Shines</h2>
<div class="sectionbody">
<div class="paragraph">
<p>I&#8217;ve briefly mentioned several benefits that Bazel offers, and there&#8217;s no need to rehash those here.
Rather, I&#8217;m going to dive into what makes Bazel <em>way better</em> for developers compared to traditional build systems.
That&#8217;s Bazel&#8217;s improved determinism.
Developers need to be able to build code reliably, and deterministic builds are reliable.
When it comes to building software, things should <em>just work</em>.<sup class="footnote">[<a id="_footnoteref_3" class="footnote" href="#_footnotedef_3" title="View footnote.">3</a>]</sup>
When they don&#8217;t, developers lose time debugging their build system, forcing clean rebuilds, and monkeying around with their environment.
It&#8217;s distracting and frustrating, taking energy away from what could be used for cool new features, critical bug fixes, or a brief moment of peace and tranquility<sup class="footnote">[<a id="_footnoteref_4" class="footnote" href="#_footnotedef_4" title="View footnote.">4</a>]</sup>.
Bazel makes builds more deterministic by using file hashes instead of timestamps to detect file changes, not having a separate configure step, and by requiring correct dependencies.</p>
</div>
<div class="paragraph">
<p>Both Make and Ninja rely on file modification timestamps to determine if a file has changed and subsequently rebuild dependents.
Unfortunately for Make and Ninja, modification timestamps aren&#8217;t a correct measure of whether or not a file has actually changed.
File checksums do just that, which is why Bazel relies on checksums instead of modification times.
This may seem like a small thing, but it&#8217;s a big step forward, ensuring that things are actually rebuilt when they need to be rebuilt.
It also avoids unnecessary rebuilds.</p>
</div>
<div class="paragraph">
<p>When you build a project with Bazel, there&#8217;s something you&#8217;ll notice immediately when you&#8217;re used to build systems like CMake, Meson, and Autotools.
There&#8217;s no configure step.
Those build systems all require running a configure step to initialize the build system.
This populates the build system&#8217;s cache and generates all of the necessary bits used to build the software using the underlying build tool, usually Make or Ninja.
With Bazel, there is no configure step.
You just build the software with the build command.
There is no underlying build tool as Bazel handles everything, and Bazel will automatically cache the things it needs and reload anything that&#8217;s changed.</p>
</div>
<div class="paragraph">
<p>Those familiar with CMake, Meson, Autotools, and Make, know all too well the inclination to immediately wipe the entire build directory and start from scratch whenever hitting an unexpected build issue.
Where this does fix the issue, the root of the problem is typically a change to a default value that already exists in the build system&#8217;s cache and is therefore not updated, or a change to the external environment which, unbeknown to the build system, renders its cache invalid.
Bazel is setup to handle the former problem, but is still susceptible to changes in its environment.</p>
</div>
<div class="paragraph">
<p>Sometimes, the build system doesn&#8217;t understand that it needs to rebuild a specific component when a file managed by the build system has been changed.
This is caused by incorrect or incomplete dependency specifications in the build system.
This is what Bazel means when it says that it is <em>correct</em>.
Everything built by Bazel must have correctly specified all of its dependencies or it won&#8217;t build.
Bazel accomplishes this with what it dubs a <em>sandbox</em>.
Each individual component is built in its own individual sandbox where it only has access to the components upon which it depends.
If there is any inconsistency between what the component actually depends upon and what is specified in the build system, the component either won&#8217;t build because a dependency it can&#8217;t find a missing dependency or it <em>will build</em> because the dependency is extraneous.
When dealing with C&#43;&#43;, this is about the best you can hope for.
Handling superfluous dependencies requires tooling that spans the code itself, the build system, and the package manager.
This is why Rust is really good at handling this, since its integrated tooling flags both unused imports and unused dependencies for removal.</p>
</div>
<div class="paragraph">
<p>These aspects of Bazel lead to builds which are <em>more correct</em>, simpler to perform, and less disruptive for developers.
Developers don&#8217;t need to frequently rebuild from scratch due to cache inconsistencies or deal with missing or incorrect dependencies.
Rebuilds occur when files actually change.
Of course, developers love quick builds, too.
Everything is awesome, correct?</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="correct">Correct?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Bazel&#8217;s tagline is that it is both fast and correct.
Despite that claim, there appear to be a couple of constraints on the word <em>correct</em> as Bazel uses it here.
Correct only applies so far as Bazel is doing the building, and, as discussed in the previous section, overspecifying dependencies is <em>not incorrect</em>.
This first point is the most important, and the one I&#8217;m going to prioritize in this section.</p>
</div>
<div class="sect2">
<h3 id="hermetic">Hermetic</h3>
<div class="paragraph">
<p>Bazel builds aren&#8217;t actually hermetic, terminology which Bazel uses frequently in its documentation.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>By hermetic, Bazel means free of external influence, which is closely tied to the concept of reproducible builds.
While Bazel builds can be hermetic, in the wild, it may be difficult to find any that are.
Technically, I&#8217;m not sure completely hermetic builds are achievable.
Just consider what happens when you try to build a decently sized application on a computer with hardly any RAM.
The build will fail.
That&#8217;s a physical limitation influencing the build.
On the other hand, reproducible builds have a more attainable goal, that builds of the software always result in the exact same binary.
That said, reproducible and hermetic are often synonymous forms of measurement, since the more hermetic a build is, the more reproducible it is and vice versa.
Deterministic builds on the other hand, are focused on consistency.
A deterministic build can incorporate a build timestamp in the final binary, even though this exposes the build to external influence and means that it is no longer "reproducible."</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>You need a compiler, glibc, or a bunch of random libraries?
No sweat, Bazel will just use whatever it finds on the host.
Wait, what about the sandbox?
Well, just like with a real sandbox, with Bazel, you can just stand up, step outside the sandbox, grab anything you may need, and step right back in to your sandbox.
Not a problem.
Bazel has full read access to the host because its <em>sandbox</em> doesn&#8217;t isolate builds from the host.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you ever want to know if a build system or package manager is hermetic or perhaps the degree to which it is, just ask the question, "How does it manage the libc implementation?"
If you can&#8217;t find that it manages anything to do with the libc implementation, then you&#8217;ve got your answer.
It&#8217;s not hermetic.
It&#8217;s using that libc from somewhere. Or it&#8217;s all in assembly. In which case, you have <em>much, much</em> bigger problems. Or it&#8217;s in Fortran, maybe? If so, I&#8217;m sorry. You probably don&#8217;t need to keep reading.<sup class="footnote">[<a id="_footnoteref_5" class="footnote" href="#_footnotedef_5" title="View footnote.">5</a>]</sup></p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Recall that one of the major benefits of Bazel comes from its sandbox.
If you forget to add a dependency on another part of your project, the build will fail since the sandbox won&#8217;t contain the missing component.
Bazel therefore <em>creates</em> the correct environment for the build.
This forces the build system to be correct, or at least, more correct in this case.
The approach of traditional build systems is error-prone exactly because it requires you to write a correct build system.
Well, Bazel takes the same approach to the host environment, putting the onus on developers.
Expecting a developer to write a correct build system is like trying to transport a 5-gallon bucket of nitroglycerin half-way around the world <em>inside</em> a jet engine, saying, "It&#8217;ll be fine as long as there isn&#8217;t too much turbulence," and then immediately detonating half a ton of dynamite around the engine for good measure.<sup class="footnote">[<a id="_footnoteref_6" class="footnote" href="#_footnotedef_6" title="View footnote.">6</a>]</sup><sup class="footnote">[<a id="_footnoteref_7" class="footnote" href="#_footnotedef_7" title="View footnote.">7</a>]</sup>
Wait, maybe that&#8217;s a better metaphor for what you end up with…<sup class="footnote">[<a id="_footnoteref_8" class="footnote" href="#_footnotedef_8" title="View footnote.">8</a>]</sup>
The recent research article <a href="https://www.computer.org/csdl/magazine/so/5555/01/10703127/20JUqvPnreo">On Build Hermeticity in Bazel-based Build Systems</a> by Shenyu Zheng, Bram Adams, and Ahmed E. Hassan backs up this metaphor.
The researchers analyzed seventy open source projects using the Bazel build system and found zero of them to be hermetic.
Defaults played a big role in determining the hermeticity of the projects, and Bazel is definitely not hermetic out-of-the-box.</p>
</div>
<div class="sect3">
<h4 id="bazelify_all_the_things">Bazelify All the Things</h4>
<div class="paragraph">
<p>So, how does one make a Bazel project hermetic?
This is a fundamental part of Bazel&#8217;s design, and solving this problem retroactively is very difficult, if not impossible.
One approach, taken by the <a href="https://registry.bazel.build/">Bazel Central Repository</a>, abbreviated as <em>BCR</em>, is to use Bazel for everything.
This effectively uses Bazel as a package manager.
The BCR offers some compiler toolchains which usually provide artifacts from prebuilt binary releases.
In contrast, most libraries on the BCR appear to be built from source by adding a Bazel build system for each version of the library.
The BCR has a fairly limited amount of software available at this time, which is unsurprising given adding an entire build system for any project of reasonable complexity is a substantial investment.
This maintenance overhead is a massive impediment for the BCR to overcome in order to scale appropriately.
The upstream projects are not responsible for maintaining their packages in the BCR.
These Bazel build systems are effectively an island, and may lead to all sorts of inconsistencies between the built artifacts.
These differences may be harmless, but they&#8217;re likely to contain bugs which are not seen outside the BCR.
The upstream may not be able to support developers in this situation, and developers may have difficulty finding similar issues, making it more difficult for them to diagnose and solve the problem.
To make matters worse, this also has important security implications.
Bugs introduced by the Bazel build system may be vulnerabilities or the BCR could be used to carry out a supply chain attack.
Because the BCR package will most likely have less visibility in the wild, vulnerabilities are also less likely to be discovered.</p>
</div>
<div class="paragraph">
<p>Most package managers don&#8217;t have these kinds of difficulties, at least, not to the same extent, because they use the build system provided by the upstream project.
This approach fosters a direct collaboration with the upstream projects, minimizing the kinds of downsides associated with adding a bespoke build system just described.
The BCR does indicate that it "is a central host for upstream projects that don&#8217;t have upstream support," in <a href="https://github.com/bazelbuild/bazel-central-registry/blob/main/README.md">its README</a>, but convincing upstream maintainers to accept the burden of maintaining a Bazel build system sounds like a pipe dream at this time.
Many freedesktop projects are in the process of migrating or have migrated to Meson from Autotools and/or CMake.<sup class="footnote">[<a id="_footnoteref_9" class="footnote" href="#_footnotedef_9" title="View footnote.">9</a>]</sup>
I&#8217;ve helped with some of these migrations myself, even helping get the Meson build system in the <code>util-linux</code> project production-ready.<sup class="footnote">[<a id="_footnoteref_10" class="footnote" href="#_footnotedef_10" title="View footnote.">10</a>]</sup>
Migrating the build system again right away, just doesn&#8217;t seem like something you&#8217;d want to ask yet.
Bazel is capable of wrapping existing build systems, which would make it function just like a traditional package manager.
However, without something akin to the BCR crowd sourcing this work, it&#8217;s too large an undertaking to ask individual projects to maintain this themselves.</p>
</div>
<div class="paragraph">
<p>The lack of dependencies in the BCR is also a massive problem for projects with many dependencies, particularly large graphical frameworks like Qt or Gtk.
Bazel requires writing everything for one of these from scratch, using custom rules, and/or trying to shim it in via third party package managers.
Trust me.
It&#8217;s horrifying.</p>
</div>
<div class="paragraph">
<p>Even if Bazel is used for everything, it&#8217;s still possible that your build relies on something, somewhere on the host system.
Luckily, Bazel does have a flag, albeit an experimental one, that enables a sandbox that is properly isolated from the host&#8217;s filesystem.
The flag is <a href="https://bazel.build/reference/command-line-reference#build-flag&#8212;&#8203;experimental_use_hermetic_linux_sandbox">--experimental_use_hermetic_linux_sandbox</a>.
Additional flags can be used to permit access to parts of the host&#8217;s filesystem.
If you do permit access to the host filesystem, then you&#8217;re going to need to control the environment, which brings us to the other way to solve this problem.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Cross-<em>complication</em><sup class="footnote">[<a id="_footnoteref_11" class="footnote" href="#_footnotedef_11" title="View footnote.">11</a>]</sup> is one area where Bazel probably is more hermetic.
There are packages available for the Arm GNU Toolchain as well as FreeRTOS.
Projects using Yocto SDKs are probably better than those just using whatever compiler and system libraries are on the host.
Although you still have to be sure to manage the version of the Yocto SDK being used to build the software.
Maybe you can even manage your Yocto SDK with Bazel, assuming those hard-coded absolute paths don&#8217;t get in the way?</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="environment_management">Environment Management</h4>
<div class="paragraph">
<p>If Bazel uses anything on the host system, then something besides Bazel should manage those files on the host.
This brings us to the second approach for making Bazel builds correct, managing the environment.
This approach is fraught with dangers, especially when the versions of dependencies aren&#8217;t explicitly managed within the project.
Virtual machines, Docker, or any number of package managers could be used to solve this problem.
All of them add more configuration and maintenance overhead.
Synchronizing changes to the environment across all build machines can also be difficult and lead to all sorts of inconsistencies.
Take Docker, for instance.
Managing the environment with containers complicates integration with IDE&#8217;s and building an updated image locally doesn&#8217;t ensure that every developer or build machine starts using that new image from that commit onwards or that it rewinds with the Git history.
Unless you have a manageable number of external dependencies you can handle exclusively with Bazel, this approach is likely to be your best choice.</p>
</div>
<div class="sect4">
<h5 id="nix_slow_correct_choose_two">Nix: { Slow, Correct } — Choose Two</h5>
<div class="paragraph">
<p><a href="https://nixos.org/">Nix</a> is one solution for managing dependencies outside of Bazel.
It has an incredible number of packages, is great for managing dependencies within a project, and reproducibility is its top priority.
Remember what I said at the beginning, there&#8217;s no silver bullet when it comes to build systems and package managers?
Well, Nix is no exception.
Forget about Windows.
It&#8217;s slow as molasses and you&#8217;d better be good with Haskell, because that&#8217;s pretty much what the Nix language is.
Nix still doesn&#8217;t solve the problem of overspecifying dependencies, however, it accomplishes exactly what Bazel doesn&#8217;t.
Instead of relying on the environment to be correct, Nix <em>creates the correct environment</em>.
This environment is probably as isolated from the host system as you can get.
For Linux, the entire build toolchain and glibc implementation are provided by Nix as are all of the other dependencies.</p>
</div>
<div class="sect5">
<h6 id="nix_bazel">Nix + Bazel</h6>
<div class="paragraph">
<p>Nix and Bazel don&#8217;t get along very well.
<a href="https://nix-bazel.build/">Nix + Bazel</a> is a project that tries to get these feuding children to play nice by providing a set of rules, <code>rules_nixpkgs</code>, for Bazel to use packages managed by Nix.
The talk <a href="https://youtu.be/FoSCSQO5xhI?si=2Y0iUiAK4t6yv8P9">The Best of Both Worlds With Nix + Bazel</a> by Andreas Herrmann goes into this in detail and I highly recommend watching it.
Sadly, the project has yet to make <code>rules_nixpkgs</code> work with remote execution, one of Bazel&#8217;s hallmark features.
See <a href="https://github.com/tweag/rules_nixpkgs/issues/180">tweag/rules_nixpkgs issue #180</a> for further details.</p>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="bliss">Bliss</h4>
<div class="paragraph">
<p>There is a third approach, and I don&#8217;t want to treat it the same way it treats all of the other non-hermetic dependencies of your project.
That is, to ignore them.
You could just not think about each little thing that might influence the build.
This is actually a viable solution for projects that you only ever need to build once, and never need to build again.
Whether or not this is something you can actually predict, you can probably do a little bit to address the issues.
Most projects out there would at least benefit from considering their dependencies and how they impact build reproducibility and reliability.
Taking small steps to, at the very least, document those dependencies can be a huge benefit for everyone.
There are lots of great questions you can ask yourself around these kinds of things.
One of the best questions to ask is, "How do I make sure that I can build this commit in six months?"
It&#8217;s not difficult to go a little bit farther and to actually try building a commit from six months ago in your project.
How far back do you have to go before you can no longer build things?
What caused those old builds to break?
What changes do you have to make to fix them?
What changes can you make now to prevent that from happening in the future?
What problems are developers facing during onboarding?
Answering questions like these may be a better starting point than trying to solve hermeticity for your entire project all at once, and it certainly shouldn&#8217;t take as long.
So, why not do that, too?
From the previous sections, improving hermeticity for your projects' builds may seem like an insurmountable obstacle.
It may not be something ever completely solved, but something consistently evaluated over time.
Like many things in this realm, you&#8217;ll often be best suited by an incremental approach that solves one individual problem at a time.
As you fix problems, it&#8217;s important to be wary of regressions, testing things again after some significant amount of time has passed.
And finally, always keep in mind the cost of the solutions.
Frequently, solving these kinds of issues requires some form of additional overhead like maintenance tasks and knowledge of different tools and configuration files or languages.
Document everything.
Automate everything.
And keep the <em>real</em> goal in mind.
The most important thing probably isn&#8217;t reproducible or correct builds, but rather that developers are able to just build the darn code without having the build system or tooling constantly getting in the way.
So be sure to gauge if developers are able to maintain these additions, or whether the learning curve is too high, or if the solution adds more complexity and problems than it actually solves.
Um… when did I get on the soapbox?
My apologies.
Let&#8217;s wrap this up.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Where does that leave us?
Bazel is still a major upgrade to most major C and C&#43;&#43; build systems, if only for it&#8217;s speed and greater determinism.
Both of these are huge wins for developers that just need to build some code.
There are pitfalls, though.
There&#8217;s still a lot of ground to cover when it comes to reproducible, hermetic, and "correct" builds.
Bazel doesn&#8217;t have a enough available for those trying to solve this within the Bazel ecosystem, nor is there a simple, ones-size-fits-all solution to manage environments outside of Bazel.
Solving this complex problem is largely left to developers.
Luckily, there are developers out there actively working on solutions, and things are definitely trending in the right direction.
There&#8217;s also practical steps developers can take to better understand the hermeticity effecting their builds, without having to commit to expensive or finicky solutions.<sup class="footnote">[<a id="_footnoteref_12" class="footnote" href="#_footnotedef_12" title="View footnote.">12</a>]</sup></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="references">References</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p><a href="https://blogsystem5.substack.com/p/bazel-next-generation">The next generation of Bazel builds</a> by Julio Merino</p>
</li>
<li>
<p><a href="https://youtu.be/FoSCSQO5xhI?si=2Y0iUiAK4t6yv8P9">The Best of Both Worlds With Nix + Bazel</a> by Andreas Herrmann</p>
</li>
<li>
<p><a href="https://www.computer.org/csdl/magazine/so/5555/01/10703127/20JUqvPnreo">On Build Hermeticity in Bazel-based Build Systems</a> by Shenyu Zheng, Bram Adams, and Ahmed E. Hassan</p>
</li>
<li>
<p><a href="https://nix-bazel.build/">Nix + Bazel</a></p>
</li>
<li>
<p><a href="https://bazel.build/basics/hermeticity">Bazel Documentation: Hermeticity</a></p>
</li>
</ul>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. Apparently Buck1 was open-sourced a couple of years before Bazel.
</div>
<div class="footnote" id="_footnotedef_2">
<a href="#_footnoteref_2">2</a>. <a href="https://buck2.build/docs/about/why/">Why Buck2?</a>
</div>
<div class="footnote" id="_footnotedef_3">
<a href="#_footnoteref_3">3</a>. Assuming you&#8217;re not looking at, like, the dependency graph or something. At which point you start wondering, "How is this even working in the first place?"
</div>
<div class="footnote" id="_footnotedef_4">
<a href="#_footnoteref_4">4</a>. Knowing with absolute certainty that your change just fixed everything. It&#8217;s important to cherish those fleeting moments, you know?
</div>
<div class="footnote" id="_footnotedef_5">
<a href="#_footnoteref_5">5</a>. I&#8217;m assuming that the state of this <a href="https://github.com/edbaunton/rules_fortran">rules_fortran project</a> accurately reflects Bazel&#8217;s support. And yes, that link is one of the top search results from Google.
</div>
<div class="footnote" id="_footnotedef_6">
<a href="#_footnoteref_6">6</a>. Yes, just a single, lone jet engine.
</div>
<div class="footnote" id="_footnotedef_7">
<a href="#_footnoteref_7">7</a>. Obviously, the jet engine in question was in a test facility. No humans, animals, plants, environmental ecosystems, or biological organisms were harmed or adversely effected in the making of this metaphor, apart from the author, possibly.
</div>
<div class="footnote" id="_footnotedef_8">
<a href="#_footnoteref_8">8</a>. For those who have been hand-writing makefiles for years without a single issue, I kindly ask that you cross-compile your project <em>before</em> sending me feedback on the accuracy of this metaphor.
</div>
<div class="footnote" id="_footnotedef_9">
<a href="#_footnoteref_9">9</a>. I have to say "and" here because DBus has like three build systems. At that point, what&#8217;s one more build system?
</div>
<div class="footnote" id="_footnotedef_10">
<a href="#_footnoteref_10">10</a>. Autotools failed to cross-compile too many times.
</div>
<div class="footnote" id="_footnotedef_11">
<a href="#_footnoteref_11">11</a>. Pardon the pun. It&#8217;s <em>cross-compilation</em>.
</div>
<div class="footnote" id="_footnotedef_12">
<a href="#_footnoteref_12">12</a>. I lied. There is a silver bullet. Slow builds got you down? Do compilers constantly berate you for being wrong? Do you ever read the assembly code generated by your compiler and think to yourself, <em>I can do better</em>. Do tools format your code for you with out asking and tell you how to do your job? Sick of the contrived limitations being placed upon you by tyrannical operating systems that think they know best? Ready to throw off the yoke of abstractions forced on you by conceited, so-called <em>high-level</em> programming languages? Give them all the boot! <em>No compiler, no problem!</em> Make compilers, build systems, and package managers a thing of the past! Stop being forced to purchase copies of new C&#43;&#43; standards you never asked for, remove the threat of supply chain attacks, stop reverse engineering in its tracks, thwart others from understanding what you&#8217;re code is <em>really doing</em>, always build for release, optimize for <em>every CPU</em>, understand what a <em>full-stack</em> developer is, comply with the GPL without exposing your IP, seamlessly accelerate your workloads with GPUs, NPUs, and MPUs, simplify your CI pipelines, play by your own rules, use <code>goto</code>, freely access registers, own all the resources, expand your mind, up you&#8217;re GDB skills, and kiss code reviews goodbye! Make the switch to assembly today! <em>This message brought to you by hardware vendors everywhere. Code responsibly.</em>
</div>
</div>]]></content><author><name>Jordan Williams</name></author><category term="Build System" /><category term="Bazel" /><category term="Build System" /><category term="CMake" /><category term="Hermetic" /><category term="Hermeticity" /><category term="Linux" /><category term="Nix" /><category term="Package Manager" /><category term="Package Management" /><category term="Reproducible" /><category term="Reproducibility" /><category term="Sandbox" /><summary type="html"><![CDATA[When it comes to build systems and package management, there really is no silver bullet. Everything would be so much easier if there were. Bazel has been gaining popularity for a while now, and for good reason. It&#8217;s very fast and offers both distributed caching and remote building, and supports multiple programming languages. It&#8217;s also more correct than most of the competition. Another sign of its popularity is the number of clones its spawned which includes Buck2, Pants, and Please.[1] Of those, only Bazel and Buck2 treat C&#43;&#43; as a first-class citizen, and from Buck2&#8217;s documentation you can find it says, "There are not yet mechanisms to build in release mode."[2] Wait. Really? Enough of the clones. I&#8217;ll just focus on Bazel here, since my primary target is C&#43;&#43; and I&#8217;d like to be able to make release builds. 1. Apparently Buck1 was open-sourced a couple of years before Bazel. 2. Why Buck2?]]></summary></entry><entry><title type="html">A Podman Pod as a systemd Service</title><link href="https://www.jwillikers.com/a-podman-pod-as-a-systemd-service" rel="alternate" type="text/html" title="A Podman Pod as a systemd Service" /><published>2021-03-18T00:00:00+00:00</published><updated>2021-03-18T00:00:00+00:00</updated><id>https://www.jwillikers.com/A%20Podman%20Pod%20as%20a%20systemd%20Service</id><content type="html" xml:base="https://www.jwillikers.com/a-podman-pod-as-a-systemd-service"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Ever wanted to run a container, or pod, as a <a href="https://systemd.io/">systemd</a> service on Linux?
This allows the container to be started automatically and even restarted on failure.
I&#8217;m got a container running like this right now thanks to <a href="https://podman.io/">Podman</a> which makes this incredibly easy and a bit more secure.
If managing your containers as services is something you&#8217;re interested in, then this tutorial is for you.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="tutorial">Tutorial</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This tutorial lays out the steps to manage a Podman container as a <a href="https://systemd.io/">systemd</a> service.
A UniFi Controller container, derived from a <a href="https://kubernetes.io/">Kubernetes</a> YAML file, will be used as an example.
Steps are provided for both rootless and root configurations.
This tutorial continues the series on Podman.
Previous tutorials include <a href="podman-compose.html">Podman Compose</a>, <a href="translate-docker-compose-to-kubernetes-with-podman.html">Translate Docker Compose to Kubernetes With Podman</a>, and <a href="automatically-update-podman-containers.html">Automatically Update Podman Containers</a>.
The target system is elementary OS 5.1, based on <a href="https://ubuntu.com/">Ubuntu</a> 18.04.
You&#8217;ll need to have Podman installed, of course.
To install Podman on an Ubuntu system, follow the instructions in <a href="install-podman-on-ubuntu.html">Install Podman on Ubuntu</a>.
You are expected to be familiar with Linux containers, Podman, the command-line, the Kubernetes configuration format, {Git}, systemd, and anything else I forgot to mention&#8230;&#8203;</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Clone the repository with the Kubernetes YAML file for the UniFi Controller.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">git clone git@github.com:jwillikers/unifi-controller.git ~/Projects/unifi-controller</code></pre>
</div>
</div>
</li>
<li>
<p>Provide the generated Kubernetes YAML to <a href="https://docs.podman.io/en/latest/markdown/podman-kube-play.1.html">podman-kube-play(1)</a> to create and launch the pod.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">Rootless</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">podman kube play ~/Projects/unifi-controller/unifi-controller.yml</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">Root</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>podman kube play ~/Projects/unifi-controller/unifi-controller.yml</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
<li>
<p>Change into the directory where you want the systemd unit files to be placed.
Below are common locations for these files.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">Rootless</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">cd</span> ~/.config/systemd/user</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">Root</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">cd</span> /etc/systemd/system</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
<li>
<p>Generate the systemd service unit files using <a href="https://docs.podman.io/en/latest/markdown/podman-generate-systemd.1.html">podman-generate-systemd(1)</a>.
The following commands use a couple of extra options.
By default, podman-generate-systemd will output the content of the units to the console.
<code>--files</code> places the output in the appropriate files.
In this particular situation, it will create a service unit file for the pod and a service unit file for the single container.
The <code>--name</code> option will use the names of the pod and containers instead of their hash id&#8217;s.
The <code>--new</code> option causes the pods and containers to be created each time the service starts or restarts.
When running containers as systemd services, this option is required for Podman&#8217;s auto-update functionality to work.
For details on auto-update, checkout <a href="automatically-update-podman-containers.html">Automatically Update Podman Containers</a>.
The last argument to the command is the pod&#8217;s identifier.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">Rootless</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">podman generate systemd <span class="nt">--files</span> <span class="nt">--name</span> <span class="nt">--new</span> unifi-controller</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">Root</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>podman generate systemd <span class="nt">--files</span> <span class="nt">--name</span> <span class="nt">--new</span> unifi-controller</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
<li>
<p>Enable the systemd service.
For the rootless configuration, the service will start upon the user logging in.
For the root configuration, the service will be activated on boot.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">Rootless</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">systemctl <span class="nt">--user</span> <span class="nb">enable</span> <span class="nt">--now</span> pod-unifi-controller.service
Created symlink /home/jordan/.config/systemd/user/multi-user.target.wants/pod-unifi-controller.service → /home/jordan/.config/systemd/user/pod-unifi-controller.service.
Created symlink /home/jordan/.config/systemd/user/default.target.wants/pod-unifi-controller.service → /home/jordan/.config/systemd/user/pod-unifi-controller.service.</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">Root</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>systemctl <span class="nb">enable</span> <span class="nt">--now</span> pod-unifi-controller.service
Created symlink /etc/systemd/system/multi-user.target.wants/pod-unifi-controller.service → /etc/systemd/system/pod-unifi-controller.service.
Created symlink /etc/systemd/system/default.target.wants/pod-unifi-controller.service → /etc/systemd/system/pod-unifi-controller.service.</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
<li>
<p>Access the controller&#8217;s web console at <a href="https://127.0.0.1:8443/" class="bare">https://127.0.0.1:8443/</a>.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">fish</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">open http://127.0.0.1:8443</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">Other shells</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">xdg-open http://127.0.0.1:8443</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
</ol>
</div>
</div>
</div>
<div class="sect1">
<h2 id="see_also">See Also</h2>
<div class="sectionbody">
<div class="paragraph">
<p>On Red Hat&#8217;s Enable Sysadmin publication, the article <a href="https://www.redhat.com/sysadmin/improved-systemd-podman">Improved systemd integration with Podman 2.0</a> delves into Podman&#8217;s systemd and auto-update functionality.</p>
</div>
<div class="paragraph">
<p>An article on Red Hat&#8217;s Developer Blog, <a href="https://developers.redhat.com/blog/2019/04/24/how-to-run-systemd-in-a-container/">How to run systemd in a container</a>, describes how to run systemd from within containers.</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/containers/toolbox/">Toolbox</a> is a simplified wrapper for using Podman containers for development.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Given the simplicity of managing Podman containers as systemd services, why not use them yourself if they fit your use case?</p>
</div>
</div>
</div>]]></content><author><name>Jordan Williams</name></author><category term="Virtualization" /><category term="containers" /><category term="elementary" /><category term="Docker" /><category term="Kubernetes" /><category term="Linux" /><category term="Podman" /><category term="systemd" /><category term="Ubuntu" /><summary type="html"><![CDATA[Ever wanted to run a container, or pod, as a systemd service on Linux? This allows the container to be started automatically and even restarted on failure. I&#8217;m got a container running like this right now thanks to Podman which makes this incredibly easy and a bit more secure. If managing your containers as services is something you&#8217;re interested in, then this tutorial is for you.]]></summary></entry><entry><title type="html">Automatically Update Podman Containers</title><link href="https://www.jwillikers.com/automatically-update-podman-containers" rel="alternate" type="text/html" title="Automatically Update Podman Containers" /><published>2021-03-17T00:00:00+00:00</published><updated>2021-03-17T00:00:00+00:00</updated><id>https://www.jwillikers.com/Automatically%20Update%20Podman%20Containers</id><content type="html" xml:base="https://www.jwillikers.com/automatically-update-podman-containers"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://podman.io/">Podman</a> can automatically update your containers and hopefully make you&#8217;re life easier at the same time.
Setting this up for Podman is actually pretty straightforward.
Read on to learn how to set this up.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="tutorial">Tutorial</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This tutorial will guide you through the steps to configure automatic updates for a Podman container.
Specifically, the tutorial will walk through automating updates for a UniFi Controller container using a <a href="https://kubernetes.io/">Kubernetes</a> YAML file.
It&#8217;s a continuation of the <a href="podman-compose.html">Podman Compose</a> and <a href="translate-docker-compose-to-kubernetes-with-podman.html">Translate Docker Compose to Kubernetes With Podman</a> posts.
The target system is <a href="https://ubuntu.com/">Ubuntu</a> 18.04.
You&#8217;ll need to have Podman installed, of course.
You should also be familiar with Linux containers, Podman, the command-line, the Kubernetes configuration format, <a href="https://git-scm.com/">Git</a>, and <a href="https://systemd.io/">systemd</a>.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Clone the GitHub repository with the Kubernetes configuration file for the UniFi controller.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">git clone git@github.com:jwillikers/unifi-controller.git ~/Projects/unifi-controller</code></pre>
</div>
</div>
</li>
<li>
<p>Inspect the YAML file.</p>
<div class="openblock">
<div class="content">
<div class="listingblock">
<div class="title">~/Projects/unifi-controller/unifi-controller.yml</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2021-03-13T17:21:54Z"</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">unifi-controller</span>
    <span class="na">io.containers.autoupdate</span><span class="pi">:</span> <span class="s">image</span> <i class="conum" data-value="1"></i><b>(1)</b>
  <span class="na">name</span><span class="pi">:</span> <span class="s">unifi-controller</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Add the label <code>io.containers.autoupdate</code> and set it to <code>image</code> to enable automatic updates for the containers herein.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>When using the <code>podman create</code> command, the <code>--label</code> or <code>-l</code> flag can be followed by the label, <code>"io.containers.autoupdate=image"</code> to enable auto-updates for the container.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The image name must be fully qualified for auto-update to update the image.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</li>
<li>
<p>Provide the generated Kubernetes YAML to <a href="https://docs.podman.io/en/latest/markdown/podman-kube-play.1.html">podman-kube-play(1)</a> to create and launch the pod.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">podman kube play ~/Projects/unifi-controller/unifi-controller.yml</code></pre>
</div>
</div>
</li>
<li>
<p>Check the labels attached to the UniFi Controller container with <code>podman ps</code>.</p>
<div class="openblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">podman ps <span class="nt">-a</span> <span class="nt">--filter</span> <span class="nv">name</span><span class="o">=</span>unifi-controller <span class="nt">--format</span> <span class="s2">"{{.Names}}  {{.Labels}}"</span>
unifi-controller_unifi-controller_1  map[PODMAN_SYSTEMD_UNIT:container-unifi-controller_unifi-controller_1.service build_version:Linuxserver.io version:- 6.0.45-ls100 Build-date:- 2021-03-02T04:05:16+00:00 com.docker.compose.container-number:1 com.docker.compose.service:unifi-controller io.containers.autoupdate:image io.podman.compose.config-hash:123 io.podman.compose.project:unifi-controller io.podman.compose.version:0.0.1 maintainer:aptalca]</code></pre>
</div>
</div>
<div class="paragraph">
<p>There are quite a few labels present, but one of them is the correct label, <code>io.containers.autoupdate:image</code>.
This confirms that the container is labelled correctly.</p>
</div>
</div>
</div>
</li>
<li>
<p>Enable the Podman&#8217;s auto-update systemd timer.
This tutorial uses the rootless runtime, but the necessary command is provided for enabling the auto-update timer for containers run as root.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">Rootless</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">systemctl <span class="nt">--user</span> <span class="nb">enable</span> <span class="nt">--now</span> podman-auto-update.timer</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">Root</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>systemctl <span class="nb">enable</span> <span class="nt">--now</span> podman-auto-update.timer</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
<li>
<p>When using <a href="https://docs.podman.io/en/latest/markdown/podman-generate-systemd.1.html">podman-generate-systemd(1)</a> to create systemd units for a pod, make sure to use the <code>--new</code> flag.
This will create, start, and remove containers as part of the systemd units, which is necessary for applying automatic updates to running containers.
To learn more about running a pod or container as a systemd service, refer to <a href="a-podman-pod-as-a-systemd-service.html">A Podman Pod as a systemd Service</a>.</p>
</li>
<li>
<p>It&#8217;s also possible to trigger auto-updates manually with <a href="https://docs.podman.io/en/latest/markdown/podman-auto-update.1.html">podman-auto-update(1)</a>.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">podman auto-update</code></pre>
</div>
</div>
</li>
<li>
<p>In case you&#8217;re interested in accessing the UniFi controller container, the controller&#8217;s web console is at <a href="https://127.0.0.1:8443/" class="bare">https://127.0.0.1:8443/</a>.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">fish</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">open http://127.0.0.1:8443</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">Other shells</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">xdg-open http://127.0.0.1:8443</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
</ol>
</div>
</div>
</div>
<div class="sect1">
<h2 id="see_also">See Also</h2>
<div class="sectionbody">
<div class="paragraph">
<p>On Red Hat&#8217;s Enable Sysadmin publication, the article <a href="https://www.redhat.com/sysadmin/improved-systemd-podman">Improved systemd integration with Podman 2.0</a> delves into Podman&#8217;s auto-update functionality.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You have learned how to enable automatic updates for Podman containers.</p>
</div>
</div>
</div>]]></content><author><name>Jordan Williams</name></author><category term="Virtualization" /><category term="auto-update" /><category term="containers" /><category term="elementary" /><category term="Docker" /><category term="Kubernetes" /><category term="Linux" /><category term="Podman" /><category term="Ubuntu" /><summary type="html"><![CDATA[Podman can automatically update your containers and hopefully make you&#8217;re life easier at the same time. Setting this up for Podman is actually pretty straightforward. Read on to learn how to set this up.]]></summary></entry><entry><title type="html">Automate Flatpak Updates With systemd</title><link href="https://www.jwillikers.com/automate-flatpak-updates-with-systemd" rel="alternate" type="text/html" title="Automate Flatpak Updates With systemd" /><published>2021-03-16T00:00:00+00:00</published><updated>2021-03-16T00:00:00+00:00</updated><id>https://www.jwillikers.com/Automate%20Flatpak%20Updates%20With%20systemd</id><content type="html" xml:base="https://www.jwillikers.com/automate-flatpak-updates-with-systemd"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://elementary.io/">elementary OS</a> 5.1 doesn&#8217;t automatically update <a href="https://flatpak.org/">Flatpak</a> applications.
Given the arbitrary appearance of updates, it&#8217;s a bit bothersome to be nagged about updates all day.
Flatpak doesn&#8217;t provide an auto-update mechanism but instead leaves this up to software apps.
GNOME Software has had this functionality baked-in since GNOME 3.30, for instance, according to the <a href="https://www.phoronix.com/scan.php?page=home">Phoronix</a> article <a href="https://www.phoronix.com/scan.php?page=news_item&amp;px=GNOME-3.30-Auto-Updates-Flatpak">GNOME Software 3.30 Will Automatically Update Flatpaks By Default</a>.
Since I don&#8217;t want to have multiple app stores on my machine, I opted for using <a href="https://systemd.io/">systemd</a> to update Flatpaks.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="tutorial">Tutorial</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The instructions here describe how to create systemd service and timers to automate updating both user and system Flatpak installations.
The system systemd units will only update the system Flatpaks, whereas the user systemd units will update both the user&#8217;s Flatpaks and the system&#8217;s.
In most cases, having both user and system services to update Flatpaks is unnecessary.
The system systemd units are handy for the default Flatpak behavior, which installs Flatpaks system-wide.
The user systemd units are great for users who opt to install Flatpaks in their user-specific installation, such as Flatpak developers.</p>
</div>
<div class="paragraph">
<p>The tutorial uses elementary OS 5.1 as a reference operating system but are more generally applicable to any Linux system with systemd and Flatpak.
I assume you are familiar with these concepts and will keep things brief.
Separate instructions are provided for the user and system Flatpak installations.
The systemd units here were derived from those provided by <a href="https://github.com/flatpak/flatpak/issues/1399#issuecomment-403065567"><em>marcelpaulo</em>'s GitHub comment</a>.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The systemd user unit files are placed in the directory <code>/etc/systemd/user/</code> where they are applied to all users on the system.
An individual user can place the unit files in the directory <code>~/.config/systemd/user/</code> to only effect her account.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Create the systemd service unit to update Flatpaks.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">User</dt>
<dd>
<div class="listingblock">
<div class="title">/etc/systemd/user/update-user-flatpaks.service</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="systemd"><span class="k">[Unit]</span>
<span class="nt">Description</span><span class="p">=</span>Update user Flatpaks

<span class="k">[Service]</span>
<span class="nt">Type</span><span class="p">=</span>oneshot
<span class="nt">ExecStart</span><span class="p">=</span>/usr/bin/flatpak update --assumeyes --noninteractive

<span class="k">[Install]</span>
<span class="nt">WantedBy</span><span class="p">=</span>default.target</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">System</dt>
<dd>
<div class="listingblock">
<div class="title">/etc/systemd/system/update-system-flatpaks.service</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="systemd"><span class="k">[Unit]</span>
<span class="nt">Description</span><span class="p">=</span>Update system Flatpaks
<span class="nt">After</span><span class="p">=</span>network-online.target
<span class="nt">Wants</span><span class="p">=</span>network-online.target

<span class="k">[Service]</span>
<span class="nt">Type</span><span class="p">=</span>oneshot
<span class="nt">ExecStart</span><span class="p">=</span>/usr/bin/flatpak update --assumeyes --noninteractive --system

<span class="k">[Install]</span>
<span class="nt">WantedBy</span><span class="p">=</span>multi-user.target</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
<li>
<p>Create the systemd timer unit to automate the updates.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">User</dt>
<dd>
<div class="listingblock">
<div class="title">/etc/systemd/user/update-user-flatpaks.timer</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="systemd"><span class="k">[Unit]</span>
<span class="nt">Description</span><span class="p">=</span>Update user Flatpaks daily

<span class="k">[Timer]</span>
<span class="nt">OnCalendar</span><span class="p">=</span>daily
<span class="nt">Persistent</span><span class="p">=</span>true

<span class="k">[Install]</span>
<span class="nt">WantedBy</span><span class="p">=</span>timers.target</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">System</dt>
<dd>
<div class="listingblock">
<div class="title">/etc/systemd/system/update-system-flatpaks.timer</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="systemd"><span class="k">[Unit]</span>
<span class="nt">Description</span><span class="p">=</span>Update system Flatpaks daily

<span class="k">[Timer]</span>
<span class="nt">OnCalendar</span><span class="p">=</span>daily
<span class="nt">Persistent</span><span class="p">=</span>true

<span class="k">[Install]</span>
<span class="nt">WantedBy</span><span class="p">=</span>timers.target</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
<li>
<p>Start the systemd timer.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">User</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">systemctl <span class="nt">--user</span> <span class="nb">enable</span> <span class="nt">--now</span> update-user-flatpaks.timer
Created symlink /home/jordan/.config/systemd/user/timers.target.wants/update-user-flatpaks.timer → /etc/systemd/user/update-user-flatpaks.timer.</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">System</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>systemctl <span class="nt">--system</span> <span class="nb">enable</span> <span class="nt">--now</span> update-system-flatpaks.timer
Created symlink /etc/systemd/system/timers.target.wants/update-system-flatpaks.timer → /etc/systemd/system/update-system-flatpaks.timer.</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
</ol>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You have removed a bit of distraction from your day.
With any luck, it wasn&#8217;t even too difficult.</p>
</div>
</div>
</div>]]></content><author><name>Jordan Williams</name></author><category term="Admin" /><category term="elementary" /><category term="Flatpak" /><category term="Linux" /><category term="systemd" /><category term="Ubuntu" /><summary type="html"><![CDATA[elementary OS 5.1 doesn&#8217;t automatically update Flatpak applications. Given the arbitrary appearance of updates, it&#8217;s a bit bothersome to be nagged about updates all day. Flatpak doesn&#8217;t provide an auto-update mechanism but instead leaves this up to software apps. GNOME Software has had this functionality baked-in since GNOME 3.30, for instance, according to the Phoronix article GNOME Software 3.30 Will Automatically Update Flatpaks By Default. Since I don&#8217;t want to have multiple app stores on my machine, I opted for using systemd to update Flatpaks.]]></summary></entry><entry><title type="html">Unattended Upgrades</title><link href="https://www.jwillikers.com/unattended-upgrades" rel="alternate" type="text/html" title="Unattended Upgrades" /><published>2021-03-16T00:00:00+00:00</published><updated>2021-03-16T00:00:00+00:00</updated><id>https://www.jwillikers.com/Unattended%20Upgrades</id><content type="html" xml:base="https://www.jwillikers.com/unattended-upgrades"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Suffering from obsessive updating syndrome?
Are you making frequent trips to the App Center or terminal to apply updates?
Do update notifications haunt you all day long?
If your on a Debian-based system, <em>unattended upgrades</em> can help.<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="tutorial">Tutorial</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Systems based on <a href="https://www.debian.org/">Debian</a> systems such as <a href="https://ubuntu.com/">Ubuntu</a> and <a href="https://elementary.io/">elementary OS</a> can use the <a href="https://github.com/mvo5/unattended-upgrades">unattended-upgrades</a> package to automate system updates with <a href="https://wiki.debian.org/Aptitude">Aptitude</a>.
The package provides a <a href="https://www.python.org/">Python</a> script by the same name.
This tutorial provides a quick run through to install and configure the package for those familiar with Linux, Debian, Aptitude, and the command-line.
The tutorial uses elementary OS 5.1 as the reference system.
My configuration choices were based off my preferences for a system I use as a general desktop workstation.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>This won&#8217;t update <a href="https://flatpak.org/">Flatpak</a> applications for you.
To do this, see <a href="automate-flatpak-updates-with-systemd.html">Automate Flatpak Updates With systemd</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Install the unattended-upgrades package.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>apt <span class="nt">-y</span> <span class="nb">install </span>unattended-upgrades</code></pre>
</div>
</div>
</li>
<li>
<p>Refine the update behavior in the configuration file <code>/etc/apt/apt.conf.d/50unattended-upgrades</code>.</p>
<div class="olist loweralpha">
<ol class="loweralpha" type="a">
<li>
<p>Apply updates from all repositories.</p>
<div class="openblock">
<div class="content">
<div class="paragraph">
<p>The <code>Unattended-Upgrade::Allowed-Origins</code> block contains specific repositories from which to update automatically.
Lines are commented with <code>//</code>.
Only Ubuntu repositories are listed in this file and some of the repositories are commented out.
Uncomment these to use them.
The following example enables <code>"Ubuntu:bionic-updates";</code> and <code>"Ubuntu:bionic-backports"</code> enabling Ubuntu updates and backports.
The lines for security updates were already uncommented, so I left those as they were.</p>
</div>
<div class="listingblock">
<div class="title">/etc/apt/apt.conf.d/50unattended-upgrades</div>
<div class="content">
<pre class="rouge highlight"><code>Unattended-Upgrade::Allowed-Origins {
        "Ubuntu:bionic";
        "Ubuntu:bionic-security";
        // Extended Security Maintenance; doesn't necessarily exist for
        // every release and this system may not have it installed, but if
        // available, the policy for updates is such that unattended-upgrades
        // should also install from here by default.
        "UbuntuESMApps:bionic-apps-security";
        "UbuntuESM:bionic-infra-security";
        "Ubuntu:bionic-updates";
//      "Ubuntu:bionic-proposed";
        "Ubuntu:bionic-backports";
};</code></pre>
</div>
</div>
<div class="paragraph">
<p>For my particular use case, I want to allow updates from all repositories I have configured.
I could add these manually to the <code>Allowed-Origins</code> block, but that&#8217;s more work than I&#8217;d like to do.
Instead, my configuration replaces the <code>Allowed-Origins</code> block with an <code>Origins-Pattern</code> block which allows any origin with the <code>*</code> wildcard.
This is shown in the following snippet.</p>
</div>
<div class="listingblock">
<div class="title">/etc/apt/apt.conf.d/50unattended-upgrades</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">Unattended-Upgrade::Origins-Pattern <span class="o">{</span>
        <span class="s2">"origin=*"</span><span class="p">;</span>
<span class="o">}</span><span class="p">;</span></code></pre>
</div>
</div>
</div>
</div>
</li>
<li>
<p>Remove unused dependencies.</p>
<div class="openblock">
<div class="content">
<div class="paragraph">
<p>I don&#8217;t want to keep old or unused dependencies around, so I uncommented the following line <code>Unattended-Upgrade::Remove-Unused-Dependencies</code> and set it to <em>true</em>.
The included comment is fairly self-explanatory.</p>
</div>
<div class="listingblock">
<div class="title">/etc/apt/apt.conf.d/50unattended-upgrades</div>
<div class="content">
<pre class="rouge highlight"><code>// Do automatic removal of new unused dependencies after the upgrade
// (equivalent to apt-get autoremove)
Unattended-Upgrade::Remove-Unused-Dependencies "true";</code></pre>
</div>
</div>
<div class="paragraph">
<p>The other options related to removing unused dependencies and kernels are already enabled by default.</p>
</div>
</div>
</div>
</li>
<li>
<p>Automatically reboot after upgrades when required.</p>
<div class="openblock">
<div class="content">
<div class="paragraph">
<p>I don&#8217;t think anyone appreciates their desktop suddenly rebooting immediately after it applies some updates.
I rarely leave my computer on for more than a few hours at a time.
However, I figured it&#8217;s good to make sure the computer reboots eventually if necessary.
The configuration below will automatically reboot but it does this at two in the morning to be as unobtrusive as possible.
To further reduce the chance of unexpected interruptions I&#8217;ve disallowed the computer from rebooting so long as users are logged in.</p>
</div>
<div class="listingblock">
<div class="title">/etc/apt/apt.conf.d/50unattended-upgrades</div>
<div class="content">
<pre class="rouge highlight"><code>// Automatically reboot *WITHOUT CONFIRMATION*
//  if the file /var/run/reboot-required is found after the upgrade
Unattended-Upgrade::Automatic-Reboot "true";

// If automatic reboot is enabled and needed, reboot at the specific
// time instead of immediately
//  Default: "now"
Unattended-Upgrade::Automatic-Reboot-Time "02:00";

// Automatically reboot even if users are logged in
Unattended-Upgrade::Automatic-Reboot-WithUsers "false";</code></pre>
</div>
</div>
</div>
</div>
</li>
</ol>
</div>
</li>
<li>
<p>Configure Aptitude&#8217;s schedule for unattended-upgrades and related functions.</p>
<div class="openblock">
<div class="content">
<div class="paragraph">
<p>Aptitude has it&#8217;s own scheduling configuration activated by systemd timers, namely <code>apt-daily.timer</code> and <code>apt-daily-upgrade.timer</code>.
Aptitude configuration resides under the <code>/etc/apt</code> directory.
The unattended-upgrades script should be enabled here.</p>
</div>
<div class="paragraph">
<p>The package <code>update-notifier-common</code> is installed on my elementary OS system, so I simply updated the existing configuration file <code>/etc/apt/apt.conf.d/10periodic</code> with the appropriate settings to enable unattended-upgrades.
Alternatively, you might create a new file with a higher precedence such as <code>/etc/apt/apt.conf.d/20auto-upgrades</code> or <code>/etc/apt/apt.conf</code> and put the configuration there.</p>
</div>
<div class="paragraph">
<p>The options shown below use numbers to indicate the frequency to apply the corresponding operation in days.
<code>Unattended-Upgrade</code> is set to one so that the unattended-upgrades script is run every day.
Similarly, <code>Update-Package-Lists</code> is set to one because the package lists should be updated from their repositories each day.
If the package lists aren&#8217;t updated automatically then packages wont be upgraded because updates won&#8217;t be detected, so enabling this is important.
In addition to setting these two variables, I also set <code>AutocleanInterval</code> to automatically clean out the package cache every week.</p>
</div>
<div class="listingblock">
<div class="title">/etc/apt/apt.conf.d/10periodic</div>
<div class="content">
<pre class="rouge highlight"><code>APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::Update-Package-Lists "1";</code></pre>
</div>
</div>
<div class="paragraph">
<p>These variables and more are described in detail in the script <code>/usr/lib/apt/apt.systemd.daily</code>.</p>
</div>
</div>
</div>
</li>
<li>
<p>Test the behavior of unattended upgrades by running the script manually with the <code>--dry-run</code> and <code>--debug</code> flags.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>unattended-upgrades <span class="nt">--dry-run</span> <span class="nt">--debug</span></code></pre>
</div>
</div>
</li>
<li>
<p>Monitor unattended upgrades by perusing the log files in <code>/var/log/unattended-upgrades/</code>.</p>
</li>
</ol>
</div>
</div>
</div>
<div class="sect1">
<h2 id="see_also">See Also</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/mvo5/unattended-upgrades">The README from the unattended-upgrades GitHub repository</a></p>
</li>
<li>
<p><a href="https://wiki.debian.org/UnattendedUpgrades">Debian Wiki - UnattendedUpgrades</a></p>
</li>
<li>
<p><a href="https://debian-handbook.info/browse/stable/sect.regular-upgrades.html">The Debian Administrator&#8217;s Handbook - 6.8. Keeping a System Up to Date</a></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You should know everything you need to get started automating package updates on Debian systems.</p>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. Side-effects may include sudden, irreversible blue-screen of death, failure to boot, changes in behavior, obscure glitches, and an increase in log messages. Talk to your system administrator before using <em>unattended upgrades</em>, especially for production systems. Use as prescribed.
</div>
</div>]]></content><author><name>Jordan Williams</name></author><category term="Admin" /><category term="Aptitude" /><category term="Debian" /><category term="elementary" /><category term="Linux" /><category term="systemd" /><category term="Ubuntu" /><category term="unattended-upgrades" /><summary type="html"><![CDATA[Suffering from obsessive updating syndrome? Are you making frequent trips to the App Center or terminal to apply updates? Do update notifications haunt you all day long? If your on a Debian-based system, unattended upgrades can help.[1] 1. Side-effects may include sudden, irreversible blue-screen of death, failure to boot, changes in behavior, obscure glitches, and an increase in log messages. Talk to your system administrator before using unattended upgrades, especially for production systems. Use as prescribed.]]></summary></entry><entry><title type="html">Podman Compose</title><link href="https://www.jwillikers.com/podman-compose" rel="alternate" type="text/html" title="Podman Compose" /><published>2021-03-14T00:00:00+00:00</published><updated>2021-03-14T00:00:00+00:00</updated><id>https://www.jwillikers.com/Podman%20Compose</id><content type="html" xml:base="https://www.jwillikers.com/podman-compose"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://podman.io/">Podman</a> supports the <a href="https://kubernetes.io/">Kubernetes</a> YAML format for configuring pods.
Unfortunately, I&#8217;m coming to the Podman scene from <a href="https://www.docker.com/">Docker</a> where the <a href="https://docs.docker.com/compose/">Docker Compose</a> format is common.
The Docker Compose format isn&#8217;t supported by Podman.
I don&#8217;t really want to invest the time in learning a new configuration file format right now, so what should I do?
Use <a href="https://github.com/containers/podman-compose">Podman Compose</a>!</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="tutorial">Tutorial</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This tutorial describes how to use a Docker Compose file with Podman to create a rootless container.
It uses the Docker Compose for the UniFi Controller described in the <a href="unifi-controller.html">UniFi Controller</a> post.
This tutorial targets <a href="https://ubuntu.com/">Ubuntu</a> 18.04, and you should be familiar with Linux Containers, Docker Compose, Podman, <a href="https://www.python.org/">Python</a>, and the command-line.
You&#8217;ll need to have Podman installed on your machine, which can be installed on Ubuntu 18.04 by following the instructions in the post <a href="install-podman-on-ubuntu.html">Install Podman on Ubuntu</a>.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Since Podman Compose is a Python tool, install Python 3 and pip.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>apt <span class="nt">-y</span> <span class="nb">install </span>python3 python3-pip</code></pre>
</div>
</div>
</li>
<li>
<p>Now using pip, install the latest development version of Podman Compose.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">pip3 <span class="nb">install</span> <span class="nt">--user</span> https://github.com/containers/podman-compose/archive/devel.tar.gz</code></pre>
</div>
</div>
</li>
<li>
<p>Add <code>~/.local/bin</code> to your <code>PATH</code>.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">fish</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">fish_add_path ~/.local/bin</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">ZSH</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">echo</span> <span class="s2">"set PATH=</span><span class="nv">$HOME</span><span class="s2">/.local/bin:</span><span class="nv">$PATH</span><span class="s2">"</span> <span class="o">&gt;&gt;</span> ~/.zshrc<span class="p">;</span> <span class="nb">source</span> ~/.zshrc</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">Bash</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">echo</span> <span class="s2">"set PATH=</span><span class="nv">$HOME</span><span class="s2">/.local/bin:</span><span class="nv">$PATH</span><span class="s2">"</span> <span class="o">&gt;&gt;</span> ~/.bashrc<span class="p">;</span> <span class="nb">source</span> ~/.bashrc</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
<li>
<p>Create a directory for the Docker Compose file.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">mkdir</span> <span class="nt">-p</span> ~/Projects/unifi-controller</code></pre>
</div>
</div>
</li>
<li>
<p>Change to the new directory.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">cd</span> ~/Projects/unifi-controller</code></pre>
</div>
</div>
</li>
<li>
<p>Create the Docker Compose file.</p>
<div class="openblock">
<div class="content">
<div class="listingblock">
<div class="title">~/Projects/unifi-controller/docker-compose.yml</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="yaml"><span class="nn">---</span>
<span class="na">version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2.1"</span>
<span class="na">services</span><span class="pi">:</span>
  <span class="na">unifi-controller</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">ghcr.io/linuxserver/unifi-controller</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">MEM_LIMIT=1024M</span> <span class="c1">#optional</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">data:/config</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">3478:3478/udp</span>
      <span class="pi">-</span> <span class="s">10001:10001/udp</span>
      <span class="pi">-</span> <span class="s">8080:8080</span>
      <span class="pi">-</span> <span class="s">8443:8443</span>
      <span class="pi">-</span> <span class="s">1900:1900/udp</span> <span class="c1">#optional</span>
      <span class="pi">-</span> <span class="s">8843:8843</span> <span class="c1">#optional</span>
      <span class="pi">-</span> <span class="s">8880:8880</span> <span class="c1">#optional</span>
      <span class="pi">-</span> <span class="s">6789:6789</span> <span class="c1">#optional</span>
      <span class="pi">-</span> <span class="s">5514:5514/udp</span> <span class="c1">#optional</span>
    <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">io.containers.autoupdate</span><span class="pi">:</span> <span class="s">image</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="na">volumes</span><span class="pi">:</span>
  <span class="na">data</span><span class="pi">:</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Spoiler! I&#8217;ll be describing how to automatically update container images with Podman in an upcoming blog post.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This Docker Compose uses the <a href="https://github.com/linuxserver/docker-unifi-controller">docker-unifi-controller</a> image provided by <a href="https://www.linuxserver.io/">LinuxServer.io</a> and is very close to the provided Docker Compose file.
It uses a volume to store persistent data.
The volume dubbed <em>data</em> here will use a Podman volume named <code>unifi-controller_data</code>.</p>
</div>
</div>
</div>
</li>
<li>
<p>From within the project directory, run Podman Compose to create the <em>unifi-controller</em> pod.</p>
<div class="openblock">
<div class="content">
<div class="paragraph">
<p>Just like when using Docker Compose, the <code>up</code> subcommand creates and starts the container, and the <code>-d</code> flag backgrounds the process.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">podman-compose up <span class="nt">-d</span></code></pre>
</div>
</div>
</div>
</div>
</li>
<li>
<p>Access the controller&#8217;s web console at <a href="https://127.0.0.1:8443/" class="bare">https://127.0.0.1:8443/</a>.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">fish</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">open http://127.0.0.1:8443</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">Other shells</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">xdg-open http://127.0.0.1:8443</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
</ol>
</div>
</div>
</div>
<div class="sect1">
<h2 id="see_also">See Also</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you&#8217;d like to learn more about using Podman Compose, checkout the article <a href="https://fedoramagazine.org/manage-containers-with-podman-compose/">Manage containers with Podman Compose</a> from <a href="https://fedoramagazine.org/">Fedora Magazine</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>That was fast, wasn&#8217;t it?
Love Podman yet?
If you want to simplify your workflow, checkout <a href="translate-docker-compose-to-kubernetes-with-podman.html">Translate Docker Compose to Kubernetes With Podman</a>.</p>
</div>
</div>
</div>]]></content><author><name>Jordan Williams</name></author><category term="Virtualization" /><category term="containers" /><category term="Docker" /><category term="DockerCompose" /><category term="Kubernetes" /><category term="Linux" /><category term="Podman" /><category term="PodmanCompose" /><category term="Ubuntu" /><summary type="html"><![CDATA[Podman supports the Kubernetes YAML format for configuring pods. Unfortunately, I&#8217;m coming to the Podman scene from Docker where the Docker Compose format is common. The Docker Compose format isn&#8217;t supported by Podman. I don&#8217;t really want to invest the time in learning a new configuration file format right now, so what should I do? Use Podman Compose!]]></summary></entry><entry><title type="html">Translate Docker Compose to Kubernetes With Podman</title><link href="https://www.jwillikers.com/translate-docker-compose-to-kubernetes-with-podman" rel="alternate" type="text/html" title="Translate Docker Compose to Kubernetes With Podman" /><published>2021-03-14T00:00:00+00:00</published><updated>2021-03-14T00:00:00+00:00</updated><id>https://www.jwillikers.com/Translate%20Docker%20Compose%20to%20Kubernetes%20With%20Podman</id><content type="html" xml:base="https://www.jwillikers.com/translate-docker-compose-to-kubernetes-with-podman"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://podman.io/">Podman</a> ships with built-in support for <a href="https://kubernetes.io/">Kubernetes</a> configuration files but not for <a href="https://docs.docker.com/compose/">Docker Compose</a>.
As described in <a href="podman-compose.html">Podman Compose</a>, the <a href="https://github.com/containers/podman-compose">Podman Compose</a> utility can use Docker Compose files to create Podman containers.
However, you might want to migrate to the Kubernetes format, eschewing Podman Compose and Docker Compose entirely.
This is what I ended up doing, and I describe the process here.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="tutorial">Tutorial</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This tutorial provides the steps necessary to convert a simple Docker Compose file to an equivalent Kubernetes configuration using Podman Compose and Podman.
It continues where <a href="podman-compose.html">Podman Compose</a> left off, having created a Podman container from the Docker Compose for the UniFi Controller from the <a href="unifi-controller.html">UniFi Controller</a> post.
So, complete this tutorial before following the steps below.
This tutorial targets <a href="https://ubuntu.com/">Ubuntu</a> 18.04, and you should be familiar with Linux Containers, Docker Compose, Podman, the command-line, and the Kubernetes configuration format.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Change into the directory containing the UniFi Controller&#8217;s Docker Compose file.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">cd</span> ~/Projects/unifi-controller</code></pre>
</div>
</div>
</li>
<li>
<p>Check for the previously created UniFi Controller pod with <a href="https://docs.podman.io/en/latest/markdown/podman-pod-ps.1.html">podman-pod-ps(1)</a>.</p>
<div class="openblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">podman pod ps
POD ID        NAME              STATUS   CREATED      INFRA ID      <span class="c"># OF CONTAINERS</span>
241f0bf222a3  unifi-controller  Running  2 hours ago  d5eaaf6d5625  2</code></pre>
</div>
</div>
<div class="paragraph">
<p>Okay, it&#8217;s present and accounted for!</p>
</div>
</div>
</div>
</li>
<li>
<p>To generate the Kubernetes configuration from a Podman container, use <a href="https://docs.podman.io/en/latest/markdown/podman-kube-generate.1.html">podman-kube-generate(1)</a>.</p>
<div class="openblock">
<div class="content">
<div class="paragraph">
<p>Here I output the configuration to the file <em>unifi-controller.yml</em> using the <code>-f</code> flag.
The <code>-s</code> flag produces the necessary network service configuration.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">podman kube generate <span class="nt">-s</span> <span class="nt">-f</span> unifi-controller.yml unifi-controller</code></pre>
</div>
</div>
</div>
</div>
</li>
<li>
<p>Examine the generated YAML file, reproduced below.</p>
<div class="openblock">
<div class="content">
<div class="listingblock">
<div class="title">~/Projects/unifi-controller/unifi-controller.yml</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="yaml"><span class="c1"># Generation of Kubernetes YAML is still under development!</span>
<span class="c1">#</span>
<span class="c1"># Save the output of this file and use kubectl create -f to import</span>
<span class="c1"># it into Kubernetes.</span>
<span class="c1">#</span>
<span class="c1"># Created with podman-3.0.1</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2021-03-14T15:41:03Z"</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">unifi-controller</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">unifi-controller</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">command</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">/init</span>
    <span class="na">env</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">PATH</span>
      <span class="na">value</span><span class="pi">:</span> <span class="s">/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">TERM</span>
      <span class="na">value</span><span class="pi">:</span> <span class="s">xterm</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">container</span>
      <span class="na">value</span><span class="pi">:</span> <span class="s">podman</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">HOME</span>
      <span class="na">value</span><span class="pi">:</span> <span class="s">/root</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">LANGUAGE</span>
      <span class="na">value</span><span class="pi">:</span> <span class="s">en_US.UTF-8</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">LANG</span>
      <span class="na">value</span><span class="pi">:</span> <span class="s">en_US.UTF-8</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">MEM_LIMIT</span>
      <span class="na">value</span><span class="pi">:</span> <span class="s">1024M</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">ghcr.io/linuxserver/unifi-controller</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">unifi-controllerunifi-controller1</span>
    <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">6789</span>
      <span class="na">hostPort</span><span class="pi">:</span> <span class="m">6789</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
    <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">3478</span>
      <span class="na">hostPort</span><span class="pi">:</span> <span class="m">3478</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">UDP</span>
    <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">5514</span>
      <span class="na">hostPort</span><span class="pi">:</span> <span class="m">5514</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">UDP</span>
    <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">8880</span>
      <span class="na">hostPort</span><span class="pi">:</span> <span class="m">8880</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
    <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">8080</span>
      <span class="na">hostPort</span><span class="pi">:</span> <span class="m">8080</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
    <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">8443</span>
      <span class="na">hostPort</span><span class="pi">:</span> <span class="m">8443</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
    <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">10001</span>
      <span class="na">hostPort</span><span class="pi">:</span> <span class="m">10001</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">UDP</span>
    <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">8843</span>
      <span class="na">hostPort</span><span class="pi">:</span> <span class="m">8843</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
    <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">1900</span>
      <span class="na">hostPort</span><span class="pi">:</span> <span class="m">1900</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">UDP</span>
    <span class="na">resources</span><span class="pi">:</span> <span class="pi">{}</span>
    <span class="na">securityContext</span><span class="pi">:</span>
      <span class="na">allowPrivilegeEscalation</span><span class="pi">:</span> <span class="kc">true</span>
      <span class="na">capabilities</span><span class="pi">:</span>
        <span class="na">drop</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s">CAP_MKNOD</span>
        <span class="pi">-</span> <span class="s">CAP_NET_RAW</span>
        <span class="pi">-</span> <span class="s">CAP_AUDIT_WRITE</span>
      <span class="na">privileged</span><span class="pi">:</span> <span class="kc">false</span>
      <span class="na">readOnlyRootFilesystem</span><span class="pi">:</span> <span class="kc">false</span>
      <span class="na">seLinuxOptions</span><span class="pi">:</span> <span class="pi">{}</span>
    <span class="na">workingDir</span><span class="pi">:</span> <span class="s">/usr/lib/unifi</span>
  <span class="na">dnsConfig</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">restartPolicy</span><span class="pi">:</span> <span class="s">Never</span>
<span class="na">status</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2021-03-14T15:41:03Z"</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">unifi-controller</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">unifi-controller</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">ports</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">6789"</span>
    <span class="na">nodePort</span><span class="pi">:</span> <span class="m">32062</span>
    <span class="na">port</span><span class="pi">:</span> <span class="m">6789</span>
    <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">0</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3478"</span>
    <span class="na">nodePort</span><span class="pi">:</span> <span class="m">32030</span>
    <span class="na">port</span><span class="pi">:</span> <span class="m">3478</span>
    <span class="na">protocol</span><span class="pi">:</span> <span class="s">UDP</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">0</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">5514"</span>
    <span class="na">nodePort</span><span class="pi">:</span> <span class="m">30747</span>
    <span class="na">port</span><span class="pi">:</span> <span class="m">5514</span>
    <span class="na">protocol</span><span class="pi">:</span> <span class="s">UDP</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">0</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">8880"</span>
    <span class="na">nodePort</span><span class="pi">:</span> <span class="m">30295</span>
    <span class="na">port</span><span class="pi">:</span> <span class="m">8880</span>
    <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">0</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">8080"</span>
    <span class="na">nodePort</span><span class="pi">:</span> <span class="m">32396</span>
    <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
    <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">0</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">8443"</span>
    <span class="na">nodePort</span><span class="pi">:</span> <span class="m">32319</span>
    <span class="na">port</span><span class="pi">:</span> <span class="m">8443</span>
    <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">0</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">10001"</span>
    <span class="na">nodePort</span><span class="pi">:</span> <span class="m">30786</span>
    <span class="na">port</span><span class="pi">:</span> <span class="m">10001</span>
    <span class="na">protocol</span><span class="pi">:</span> <span class="s">UDP</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">0</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">8843"</span>
    <span class="na">nodePort</span><span class="pi">:</span> <span class="m">31695</span>
    <span class="na">port</span><span class="pi">:</span> <span class="m">8843</span>
    <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">0</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">1900"</span>
    <span class="na">nodePort</span><span class="pi">:</span> <span class="m">31076</span>
    <span class="na">port</span><span class="pi">:</span> <span class="m">1900</span>
    <span class="na">protocol</span><span class="pi">:</span> <span class="s">UDP</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">0</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">unifi-controller</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">NodePort</span>
<span class="na">status</span><span class="pi">:</span>
  <span class="na">loadBalancer</span><span class="pi">:</span> <span class="pi">{}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>This generated file warrants some additional attention.
Most importantly, the generated Kubernetes configuration is conspicuously lacking any volumes.</p>
</div>
</div>
</div>
</li>
<li>
<p>Add a section for an associated named volume that will hold the persistent data.</p>
<div class="openblock">
<div class="content">
<div class="paragraph">
<p>In the Docker Compose file, a volume was created like so.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="yaml"><span class="na">version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2.1"</span>
<span class="na">services</span><span class="pi">:</span>
  <span class="na">unifi-controller</span><span class="pi">:</span>
  <span class="s">...</span>
    <span class="s">volumes</span><span class="err">:</span>
      <span class="pi">-</span> <span class="s">data:/config</span> <i class="conum" data-value="1"></i><b>(1)</b>
  <span class="s">...</span>
<span class="na">volumes</span><span class="pi">:</span>
  <span class="na">data</span><span class="pi">:</span> <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Associate the <em>unifi-controller</em> with the volume dubbed <em>data</em> which is mounted at <code>/config</code> inside the container.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Declare the named volume <em>data</em> which will be created automatically if it doesn&#8217;t exist.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The way to accomplish the same behavior in the Kubernetes YAML is to use a <em>Persistent Volume Claim</em>.
Podman has recently added support for using <em>Persistent Volume Claims</em> to associate Podman containers with named Podman volumes.
See Podman pull request <a href="https://github.com/containers/podman/pull/8497">#8497</a> for details.
This wasn&#8217;t in the generated YAML because the functionality to generate the corresponding YAML is still outstanding per Podman issue <a href="https://github.com/containers/podman/issues/5788">#5788</a>.</p>
</div>
<div class="paragraph">
<p>For the time being, we&#8217;ll just have to add this manually.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="yaml"><span class="na">spec</span><span class="pi">:</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">command</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">/init</span>
    <span class="s">...</span>
    <span class="na">volumeMounts</span><span class="pi">:</span> <i class="conum" data-value="1"></i><b>(1)</b>
      <span class="pi">-</span> <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/config</span>
        <span class="na">name</span><span class="pi">:</span> <span class="s">unifi-data</span>
  <span class="na">volumes</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">unifi-data</span> <i class="conum" data-value="2"></i><b>(2)</b>
      <span class="na">persistentVolumeClaim</span><span class="pi">:</span>
        <span class="na">claimName</span><span class="pi">:</span> <span class="s">unifi-controller-data</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Mount the volume dubbed <em>unifi-data</em> at <code>/config</code> inside the container.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Declare the <em>Persistent Volume Claim</em>, <em>unifi-data</em>, using the claim name <em>unifi-controller-data</em>.
Podman associates the claim name with the name of the Podman named volume to use for this particular pod.</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>In an attempt to preserve what little sanity remains in my possession in this moment, I named the volume using <code>-</code> as the separator.
This is inconsistent with the volume created by Podman Compose which is named <em>unifi-controller_data</em>.
Notice that underscore instead of a hyphen at the end?
You might already be using the volume <em>unifi-controller_data</em>.
If you want to keep using it with the container created from the Kubernetes YAML, change the claim name accordingly.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</li>
<li>
<p>Optionally, you can remove some of the environment variable cruft in the <code>env</code> section.
I reduced this to just the values below.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="yaml"><span class="na">env</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">container</span>
    <span class="na">value</span><span class="pi">:</span> <span class="s">podman</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">MEM_LIMIT</span>
    <span class="na">value</span><span class="pi">:</span> <span class="s">1024M</span></code></pre>
</div>
</div>
</li>
<li>
<p>If you want to allow automatic updates of the image, add the appropriate label.</p>
<div class="openblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2021-03-13T17:21:54Z"</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">unifi-controller</span>
    <span class="na">io.containers.autoupdate</span><span class="pi">:</span> <span class="s">image</span> <i class="conum" data-value="1"></i><b>(1)</b>
  <span class="na">name</span><span class="pi">:</span> <span class="s">unifi-controller</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Add the label <code>io.containers.autoupdate</code> and set it to <code>image</code> to enable automatic updates for the containers herein.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This is a bit of a tease for an upcoming blog post which will describe this in more detail.
You&#8217;ll need to make sure that Podman&#8217;s auto-update systemd timer is enabled.
Details forthcoming.</p>
</div>
</div>
</div>
</li>
<li>
<p>Before starting this pod up, use podman-compose to destroy the existing <em>unifi-controller</em> pod.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">podman-compose down</code></pre>
</div>
</div>
</li>
<li>
<p>Provide the generated Kubernetes YAML to <a href="https://docs.podman.io/en/latest/markdown/podman-kube-play.1.html">podman-kube-play(1)</a> to create and launch the pod.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">podman kube play ~/Projects/unifi-controller/unifi-controller.yml</code></pre>
</div>
</div>
</li>
<li>
<p>Access the controller&#8217;s web console at <a href="https://127.0.0.1:8443/" class="bare">https://127.0.0.1:8443/</a>.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">fish</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">open http://127.0.0.1:8443</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">Other shells</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">xdg-open http://127.0.0.1:8443</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
</ol>
</div>
</div>
</div>
<div class="sect1">
<h2 id="see_also">See Also</h2>
<div class="sectionbody">
<div class="paragraph">
<p>I have a <a href="https://github.com/jwillikers/unifi-controller">GitHub repository</a> for this Kubernetes configuration file which you might find helpful.
RedHat has several blog posts related to Podman and Kubernetes YAML including <a href="https://developers.redhat.com/blog/2019/01/29/podman-kubernetes-yaml/">Podman can now ease the transition to Kubernetes and CRI-O</a>, <a href="https://www.redhat.com/sysadmin/compose-kubernetes-podman">From Docker Compose to Kubernetes with Podman</a>, and <a href="https://www.redhat.com/sysadmin/podman-play-kube">The podman play kube command now supports deployments</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You should now have a better idea of how the Docker Compose format translates to the Kubernetes format plus how to get the conversion started with Podman and Podman Compose.
This also sets the stage for transitioning to using Kubernetes for managing container deployments.
Hopefully you&#8217;ve found this post helpful.
Posts on automatic image updates and setting up a Podman container as a <a href="https://systemd.io/">systemd</a> service to follow.</p>
</div>
</div>
</div>]]></content><author><name>Jordan Williams</name></author><category term="Virtualization" /><category term="containers" /><category term="Docker" /><category term="Docker-Compose" /><category term="Kubernetes" /><category term="Linux" /><category term="Podman" /><category term="podman-compose" /><category term="Ubuntu" /><summary type="html"><![CDATA[Podman ships with built-in support for Kubernetes configuration files but not for Docker Compose. As described in Podman Compose, the Podman Compose utility can use Docker Compose files to create Podman containers. However, you might want to migrate to the Kubernetes format, eschewing Podman Compose and Docker Compose entirely. This is what I ended up doing, and I describe the process here.]]></summary></entry><entry><title type="html">Btrfs Scrub</title><link href="https://www.jwillikers.com/btrfs-scrub" rel="alternate" type="text/html" title="Btrfs Scrub" /><published>2021-03-05T00:00:00+00:00</published><updated>2021-03-05T00:00:00+00:00</updated><id>https://www.jwillikers.com/Btrfs%20Scrub</id><content type="html" xml:base="https://www.jwillikers.com/btrfs-scrub"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>You probably want to take advantage of the data integrity checking offered by <a href="https://btrfs.wiki.kernel.org/index.php/Main_Page">Btrfs</a>.
Btrfs calculates checksums for all data written to disk.
These checksums are used to verify the data hasn&#8217;t been unduly altered.
While data is verified every time it is read, what about the data that isn&#8217;t read often?
How long may bit rot go unnoticed in that case?
That&#8217;s the crux of this blog post which will explain how to best preserve your data on Btrfs and detect corruption early.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="scrub">Scrub</h2>
<div class="sectionbody">
<div class="paragraph">
<p>To scrub you filesystem is to have all the data read from disk and validated against the stored checksums.
This detects corrupt data.
When coupled with redundancy such as a raid configuration, self-healing fully restores the damaged data on the disk.
If you don&#8217;t use redundancy, then the scrub will alert you to the corruption so that you can restore the data manually from backups.
Both Btrfs and <a href="https://openzfs.org/wiki/Main_Page">ZFS</a> handle scrubs in this manner.</p>
</div>
<div class="paragraph">
<p>To scrub a Btrfs filesystem use <a href="https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs-scrub">btrfs-scrub(8)</a>, and in case your interested, the equivalent ZFS command is <a href="https://openzfs.github.io/openzfs-docs/man/8/zpool-scrub.8.html">zpool-scrub(8)</a>.
Both of them also offer ways to cancel, pause, resume, and monitor scrubs.
Btrfs scrubs entire filesystems at a time which is provided by a device or just any directory&#8217;s path on the target filesystem.
I&#8217;m not exactly sure why it takes a directory path to anywhere on the filesystem since that seems a bit arbitrary.
You should probably use either a mount point or device path to make the intended target clear.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Even if the <code>btrfs-scrub</code> command accepts a directory path, it doesn&#8217;t necessarily just scrub that directory.
It will scrub the entire filesystem where that directory resides.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>To initiate a scrub in the background, use the start subcommand followed by the path or device.
Here I initiate a scrub on the device on which my root filesystem resides.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>btrfs scrub start <span class="o">(</span><span class="nb">df</span> <span class="nt">--output</span><span class="o">=</span><span class="nb">source</span> / | <span class="nb">tail</span> <span class="nt">-n</span> 1<span class="o">)</span>
scrub started on /dev/mapper/sda2_crypt, fsid 175792e7-4167-40d1-aebc-78b948d6d378 <span class="o">(</span><span class="nv">pid</span><span class="o">=</span>10555<span class="o">)</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>To check on the status of a scrub, use the status subcommand and the path or device.
Check the status of the previous scrub like so.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>btrfs scrub status <span class="o">(</span><span class="nb">df</span> <span class="nt">--output</span><span class="o">=</span><span class="nb">source</span> / | <span class="nb">tail</span> <span class="nt">-n</span> 1<span class="o">)</span>
scrub status <span class="k">for </span>175792e7-4167-40d1-aebc-78b948d6d378
	scrub started at Fri Mar  5 06:07:42 2021, running <span class="k">for </span>00:01:25
	total bytes scrubbed: 26.19GiB with 0 errors</code></pre>
</div>
</div>
<div class="paragraph">
<p>In many circumstances, you might want the scrub to block and return once it finishes.
This is ideal for people like me who don&#8217;t want to type a status command constantly and it&#8217;s ideal for running the scrub as a command in systemd.
Use the <code>-B</code> flag to scrub in the foreground.
This command scrubs my boot partition and returns once the scrub is complete.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>btrfs scrub start <span class="nt">-B</span> /boot
scrub <span class="k">done for </span>264b42a6-a09c-40cc-b754-88926d43b395
	scrub started at Fri Mar  5 06:13:23 2021 and finished after 00:00:01
	total bytes scrubbed: 159.55MiB with 0 errors</code></pre>
</div>
</div>
<div class="paragraph">
<p>That didn&#8217;t take long!
There&#8217;s also subcommands to pause, resume, and cancel scrubs as needed.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="schedule">Schedule</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Scheduling regular scrubs is a necessary component of proper maintenance
You can regularly run scrubs manually or automate the process of running them yet it&#8217;s critical that you monitor the results either way.
If you go to the trouble to automate your scrubs you&#8217;ll want to make sure to regularly check the results.
Ideally you&#8217;d use something like www.nagios.org[Nagios] for monitoring this aspect of your systems.</p>
</div>
<div class="admonitionblock caution">
<table>
<tr>
<td class="icon">
<i class="fa icon-caution" title="Caution"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Don&#8217;t rely on alerts whether that is through email or desktop notifications.
If they fail silently, you won&#8217;t realize when something has gone horribly wrong.
Set aside time regularly to check your systems' status and health.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Arch Linux provides a handy <a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html">systemd.service</a> and <a href="https://www.freedesktop.org/software/systemd/man/systemd.timer.html">systemd.timer</a> to automate scrubs.
The <a href="https://github.com/kdave/btrfsmaintenance">Btrfs maintenance toolbox</a> provides similar functionality.
We&#8217;ll take a look at the instantiable systemd units provided by Arch Linux for how to make scheduling regular scrubs a breeze.
The Arch Linux Wiki&#8217;s Btrfs Scrub section has a subsection on these systemd units, <a href="https://wiki.archlinux.org/index.php/btrfs#Start_with_a_service_or_timer">Start with a service or timer</a>.
The systemd units here should be dropped in the standard system directory <code>/etc/systemd/system</code>.</p>
</div>
<div class="sect2">
<h3 id="service">Service</h3>
<div class="paragraph">
<p>Below is the <a href="https://github.com/archlinux/svntogit-packages/blob/packages/btrfs-progs/trunk/btrfs-scrub%40.service">Arch Linux systemd Btrfs scrub service</a>.</p>
</div>
<div class="listingblock">
<div class="title">/etc/systemd/system/btrfs-scrub@.service</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="systemd"><span class="k">[Unit]</span>
<span class="nt">Description</span><span class="p">=</span>Btrfs scrub on %f
<span class="nt">ConditionPathIsMountPoint</span><span class="p">=</span>%f
<span class="nt">RequiresMountsFor</span><span class="p">=</span>%f

<span class="k">[Service]</span>
<span class="nt">Nice</span><span class="p">=</span>19
<span class="nt">IOSchedulingClass</span><span class="p">=</span>idle
<span class="nt">KillSignal</span><span class="p">=</span>SIGINT
<span class="nt">ExecStart</span><span class="p">=</span>/usr/bin/btrfs scrub start -B %f</code></pre>
</div>
</div>
<div class="paragraph">
<p>This <a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html">systemd.service</a> is an <a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html#Service%20Templates">instantiated service</a> which expects that a properly escaped path is provided after the <code>@</code> and before the <code>.service</code> extension.
systemd uses special escaping rules to map filesystem paths to unit file names.
The <a href="https://www.freedesktop.org/software/systemd/man/systemd-escape.html">systemd-escape(1)</a> tool makes it quite easy to convert a given path.</p>
</div>
<div class="paragraph">
<p>This service requires that the path of the service unit is indeed a mount point and that it exists with <a href="https://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConditionPathIsMountPoint=">ConditionPathIsMountPoint</a>.
The argument <code>%f</code> represents the unescaped path used to instantiate this systemd unit.
Similarly, the <code>%i</code> flag is the escaped version of the path used to instantiate this unit, that is the string between <code>@</code> and before <code>.service</code> when starting the unit.
<a href="https://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor=">RequiresMountsFor</a> will ensure that any mount points on the given path are mounted before executing the unit.</p>
</div>
<div class="paragraph">
<p>One might opt to use {BindsTo} and {After} instead of <code>RequiresMountsFor</code> to define a stronger relationship to the <a href="https://www.freedesktop.org/software/systemd/man/systemd.mount.html">systemd.mount</a> unit responsible for mounting the filesystem at the given mount point.
systemd mount units are usually generated automatically from entries in <a href="https://manpages.ubuntu.com/manpages/focal/man8/fsck.8.html">/etc/fstab</a>.
For this dependency relationship to work, a corresponding systemd mount unit needs to exist.
You&#8217;ll want the filesystem your scrubbing to have an entry in fstab or otherwise provide the mount unit in some other way.
<code>BindsTo</code> requires that the filesystem at the mount point be available the entire time this unit is running.
If it becomes unavailable for some reason, the mount unit fails and the scrub service is killed along with it.
The <code>After</code> keyword requires that the target be mounted before this service runs.
Both of these would be set to <code>%i.mount</code>, the name of the corresponding systemd mount unit.</p>
</div>
<div class="paragraph">
<p>The <a href="https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Nice=">Nice</a> directive sets the scheduling priority to the lowest possible value, <em>19</em>, giving the scrub a very low priority to avoid hogging the system CPU time.
The <a href="https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IOSchedulingClass=">IOSchedulingClass</a> directive is set to <code>idle</code> which effectively means that the IO activity of the process shouldn&#8217;t impact normal system activity.
the scrub will only use the disk when no other programs are using it.
<a href="https://www.freedesktop.org/software/systemd/man/systemd.kill.html#KillSignal=">KillSignal</a> sets the signal used to kill the process to SIGINT, i.e. <kbd>Ctrl-C</kbd>.
Finally, the <a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=">ExecStart</a> executes the scrub command on the unescaped path used to instantiate the service but uses <code>-B</code> to avoid immediately returning.</p>
</div>
<div class="paragraph">
<p>The <a href="https://www.freedesktop.org/software/systemd/man/systemctl.html#">systemctl(1)</a> command handles interacting with systemd services and units.
To start a scrub directly with the systemd service, start the the systemd unit with <code><a href="https://www.freedesktop.org/software/systemd/man/systemctl.html#start%20PATTERN%E2%80%A6">systemctl start</a></code>.
Here, I start the unit on the root path of the filesystem which is converted by systemd to <code>-</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>systemctl start btrfs-scrub@<span class="o">(</span>systemd-escape <span class="nt">-p</span> /<span class="o">)</span>.service</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can then check the status of the systemd service with <code><a href="https://www.freedesktop.org/software/systemd/man/systemctl.html#status%20PATTERN%E2%80%A6%7CPID%E2%80%A6%5D">systemctl status</a></code> as follows.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>systemctl status btrfs-scrub@<span class="o">(</span>systemd-escape <span class="nt">-p</span> /<span class="o">)</span>.service
● btrfs-scrub@-.service - Btrfs scrub on /
   Loaded: loaded <span class="o">(</span>/etc/systemd/system/btrfs-scrub@.service<span class="p">;</span> static<span class="p">;</span> vendor preset: enabled<span class="o">)</span>
   Active: inactive <span class="o">(</span>dead<span class="o">)</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="timer">Timer</h3>
<div class="paragraph">
<p>Below is the <a href="https://github.com/archlinux/svntogit-packages/blob/packages/btrfs-progs/trunk/btrfs-scrub%40.timer">Arch Linux systemd Btrfs scrub timer</a> albeit with a small modification on my part.
The <a href="https://www.freedesktop.org/software/systemd/man/systemd.timer.html">systemd.timer</a> runs on the first and fifteenth of every month instead of only once a month.
Weekly is also a good option which can be configured by setting <a href="https://www.freedesktop.org/software/systemd/man/systemd.timer.html#OnCalendar=">OnCalendar</a> to <code>weekly</code>.</p>
</div>
<div class="listingblock">
<div class="title">/etc/systemd/system/btrfs-scrub@.timer</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="systemd"><span class="k">[Unit]</span>
<span class="nt">Description</span><span class="p">=</span>Btrfs scrub on %f twice per month

<span class="k">[Timer]</span>
<span class="nt">OnCalendar</span><span class="p">=</span>*-*-1,15
<span class="nt">AccuracySec</span><span class="p">=</span>1d
<span class="nt">RandomizedDelaySec</span><span class="p">=</span>1w
<span class="nt">Persistent</span><span class="p">=</span>true

<span class="k">[Install]</span>
<span class="nt">WantedBy</span><span class="p">=</span>timers.target</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <a href="https://www.freedesktop.org/software/systemd/man/systemd.timer.html#Persistent=">Persistent</a> keyword ensures the service runs even if the timer would have fired previously but the system was not available.
If you miss a scrub due to your machine being powered off, the scrub will happen the next time you boot up.</p>
</div>
<div class="paragraph">
<p>Use <code><a href="https://www.freedesktop.org/software/systemd/man/systemctl.html#enable%20UNIT%E2%80%A6">systemctl enable</a></code> to activate the timer.
Here I set the timer to scrub the root filesystem automatically activate at boot while starting the timer immediately with <code><a href="https://www.freedesktop.org/software/systemd/man/systemctl.html#--now">--now</a></code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>systemctl <span class="nb">enable</span> <span class="nt">--now</span> btrfs-scrub@<span class="o">(</span>systemd-escape <span class="nt">-p</span> /<span class="o">)</span>.timer
Created symlink /etc/systemd/system/timers.target.wants/btrfs-scrub@-.timer → /etc/systemd/system/btrfs-scrub@.timer.</code></pre>
</div>
</div>
<div class="paragraph">
<p>As with the service, you can check the status of the systemd timer which is shown here.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>systemctl status btrfs-scrub@<span class="o">(</span>systemd-escape <span class="nt">-p</span> /<span class="o">)</span>.timer
● btrfs-scrub@boot.timer - Btrfs scrub on / twice per month
   Loaded: loaded <span class="o">(</span>/etc/systemd/system/btrfs-scrub@.timer<span class="p">;</span> indirect<span class="p">;</span> vendor preset: enabled<span class="o">)</span></code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>That&#8217;s a scrub!
Hopefully you&#8217;ve got some valuable insight into scrubbing and managing scrubs with Btrfs.
Happy scrubbing!</p>
</div>
</div>
</div>]]></content><author><name>Jordan Williams</name></author><category term="Data Storage" /><category term="Arch" /><category term="Btrfs" /><category term="Linux" /><category term="scrub" /><category term="systemd" /><category term="ZFS" /><summary type="html"><![CDATA[You probably want to take advantage of the data integrity checking offered by Btrfs. Btrfs calculates checksums for all data written to disk. These checksums are used to verify the data hasn&#8217;t been unduly altered. While data is verified every time it is read, what about the data that isn&#8217;t read often? How long may bit rot go unnoticed in that case? That&#8217;s the crux of this blog post which will explain how to best preserve your data on Btrfs and detect corruption early.]]></summary></entry><entry><title type="html">GNOME Boxes on Btrfs</title><link href="https://www.jwillikers.com/gnome-boxes-on-btrfs" rel="alternate" type="text/html" title="GNOME Boxes on Btrfs" /><published>2021-03-04T00:00:00+00:00</published><updated>2021-03-04T00:00:00+00:00</updated><id>https://www.jwillikers.com/GNOME%20Boxes%20on%20Btrfs</id><content type="html" xml:base="https://www.jwillikers.com/gnome-boxes-on-btrfs"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>So, you&#8217;ve got <a href="https://libvirt.org/">libvirt</a> installed on your Linux box and your looking for a simple application for running virtual machines.
Look no further than <a href="https://wiki.gnome.org/Apps/Boxes">Boxes</a>, so far as it meets your needs, of course.
What&#8217;s that you ask?
What do you need to figure out to run on this on a <a href="https://btrfs.wiki.kernel.org/index.php/Main_Page">Btrfs</a> filesystem?
Well, you&#8217;ve come to the right place!
This post describes how to install and accommodate Boxes on Btrfs.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="tutorial">Tutorial</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This tutorial describes how to install GNOME <a href="https://wiki.gnome.org/Apps/Boxes">Boxes</a> on a Btrfs filesystem on <a href="https://elementary.io/">elementary OS</a> 5.1 which is based on <a href="https://ubuntu.com/">Ubuntu</a> 18.04.
You&#8217;ll need to have libvirt installed.
Instructions for this are available in the post <a href="install-libvirt-on-elementary-os-5-1.html">Install libvirt on elementary OS 5.1</a>, which addresses Btrfs concerns.
You should be familiar with installing software on Ubuntu and elementary OS, <a href="https://flatpak.org/">Flatpak</a>, the command-line, and Btrfs.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>For more robust configurations and anything that doesn&#8217;t <em>just work</em> in Boxes, try <a href="https://virt-manager.org/">virt-manager</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="install">Install</h3>
<div class="paragraph">
<p>Boxes is readily available in two formats, as a Flatpak and a deb package from Ubuntu&#8217;s repositories.
You can install in one or both ways.
The Flatpak will receive updates to newer versions where the deb package won&#8217;t be updated beyond the minor version provided, currently 3.28.
While the Flatpak will be a much newer version, development in Flatpak is still necessary to expose and connect all the necessary system components for virtualization.
Some things may not work quite right yet with the Flatpak, but I&#8217;ve found it to work well enough.</p>
</div>
<div class="sect3">
<h4 id="flatpak">Flatpak</h4>
<div class="paragraph">
<p>A Flatpak can be installed system-wide or for an individual user.
The instructions below describe both methods.
systemd,</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Add the Flathub remote.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">User</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">flatpak <span class="nt">--user</span> remote-add <span class="nt">--if-not-exists</span> flathub https://flathub.org/repo/flathub.flatpakrepo</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">System</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>flatpak <span class="nt">--system</span> remote-add <span class="nt">--if-not-exists</span> flathub https://flathub.org/repo/flathub.flatpakrepo</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
<li>
<p>Install the GNOME Boxes Flatpak.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">User</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">flatpak <span class="nt">--user</span> <span class="nb">install</span> <span class="nt">-y</span> flathub org.gnome.Boxes</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">System</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>flatpak <span class="nt">--system</span> <span class="nb">install</span> <span class="nt">-y</span> flathub org.gnome.Boxes</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
</ol>
</div>
</div>
<div class="sect3">
<h4 id="system_package">System Package</h4>
<div class="paragraph">
<p>Install the GNOME Boxes Ubuntu package.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>apt <span class="nt">-y</span> <span class="nb">install </span>gnome-boxes</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="btrfs">Btrfs</h3>
<div class="paragraph">
<p>By default, Boxes uses the copy-on-write <a href="https://qemu.readthedocs.io/en/master/system/images.html#cmdoption-image-formats-arg-qcow2">qcow2</a> disk image format.
If you use Btrfs on your system like I do, then you&#8217;ll want to avoid placing these <em>CoW</em> disk images on a <em>CoW</em> Btrfs filesystem.
You&#8217;ll probably want to exclude the disk images from Btrfs snapshots as well and opt to manage you disk image snapshots independently using the built-in features of qcow2.
In the future, perhaps libvirt will provide a native Btrfs storage pool making the qcow2 format unnecessary along with these workarounds.</p>
</div>
<div class="paragraph">
<p>The sections here demonstrate a couple of ways to disable <em>CoW</em> for the disk image directory used by Boxes and how to create a separate subvolume for that directory.
The location of the Boxes disk image directory depends on whether it is installed as a Flatpak or a deb package.
Refer to {Where does Boxes store disk images} in the Boxes documentation for more information.
Commands are provide for both locations where feasible.</p>
</div>
<div class="sect3">
<h4 id="exclude_from_btrfs_snapshots">Exclude From Btrfs Snapshots</h4>
<div class="paragraph">
<p>If you snapshot your filesystem, take care to exclude the Boxes virtual disk image directory by making the directory a subvolume.
Btrfs subvolumes are automatically excluded from snapshots of their parent subvolumes.
Snapshots for virtual disk images should be handled in the disk image itself.
Snapshots are provided by default qcow2 format used by Boxes.
Here&#8217;s how to create the subvolume.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Delete the current images directory.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">Flatpak</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">rmdir</span> ~/.var/app/org.gnome.Boxes/data/gnome-boxes/images</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">System Package</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">rmdir</span> ~/.local/share/gnome-boxes/images</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</li>
<li>
<p>Create a subvolume in its place.</p>
<div class="dlist">
<dl>
<dt class="hdlist1">Flatpak</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">btrfs subvolume create ~/.var/app/org.gnome.Boxes/data/gnome-boxes/images
Create subvolume <span class="s1">'/home/jordan/.var/app/org.gnome.Boxes/data/gnome-boxes/images'</span></code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">System Package</dt>
</dl>
</div>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre>btrfs subvolume create ~/.local/share/gnome-boxes/images
Create subvolume '/home/jordan/.local/share/gnome-boxes/images'</pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="disable_cow">Disable <em>CoW</em></h4>
<div class="paragraph">
<p>The two most straightforward ways to disable <em>CoW</em> for a directory, or subvolume, are to use a file attribute or libvirt&#8217;s storage pool feature.
Use whichever one you prefer.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>There&#8217;s also the <code>nodatacow</code> mount option, but a <a href="https://btrfs.wiki.kernel.org/index.php/SysadminGuide#Flat">flat layout</a> in a home directory isn&#8217;t exactly the pinnacle of convenience.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect4">
<h5 id="chattr">chattr</h5>
<div class="paragraph">
<p>The simplest way to disable <em>CoW</em> on a particular directory or file is with <a href="https://manpages.ubuntu.com/manpages/bionic/man1/chattr.1.html">chattr(1)</a> as described in <a href="https://btrfs.wiki.kernel.org/index.php/FAQ#Can_copy-on-write_be_turned_off_for_data_blocks.3F">Can copy-on-write be turned off for data blocks?</a>.
This makes it easy to disable <em>CoW</em> on the Boxes disk image directory.
To do this, <em>add</em> the <em>no copy on write</em> attribute with the <code>+C</code> option followed by the directory.
The following commands disable <em>CoW</em> on Boxes' image directory.</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">Flatpak</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">chattr +C ~/.var/app/org.gnome.Boxes/data/gnome-boxes/images</code></pre>
</div>
</div>
</dd>
<dt class="hdlist1">System Package</dt>
<dd>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">chattr +C ~/.local/share/gnome-boxes/images</code></pre>
</div>
</div>
</dd>
</dl>
</div>
</div>
<div class="sect4">
<h5 id="libvirt_storage_pool_feature">libvirt Storage Pool Feature</h5>
<div class="paragraph">
<p>Boxes creates a dedicated libvirt storage pool.
libvirt uses the concept of storage pools to abstract the complexities involved in managing the underlying virtual machine disk images in a variety of situations.
There&#8217;s a bit to it, but I&#8217;ll leave out the lengthy explanation for brevity.
libvirt has fantastic documentation on its <a href="https://libvirt.org/storage.html">Storage Management</a> if you wish to learn more.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>elementary OS 5.1 and Ubuntu 18.04 only ship with access to libvirt 4.0.0, so you&#8217;ll need to get newer version by some external means for this to work.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p><em>CoW</em> can be disabled on the libvirt storage pool by configuring the appropriate storage pool feature.
libvirt stores pretty much all configuration in XML files.
This is the case for storage pools and the XML can be viewed and edited with <a href="https://libvirt.org/manpages/virsh.html">virsh(1)</a>.
The steps here walk through the steps to disable <em>CoW</em> on the Boxes storage pool.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Find the Boxes storage pool with the <a href="https://libvirt.org/manpages/virsh.html#pool-list">pool-list</a> subcommand.</p>
<div class="openblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">virsh pool-list
 Name                 State      Autostart
<span class="nt">-------------------------------------------</span>
 default              active     <span class="nb">yes
 </span>gnome-boxes          active     <span class="nb">yes</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>libvirt&#8217;s default pool is simply called <em>default</em> while Boxes' pool is named <em>gnome-boxes</em>.</p>
</div>
</div>
</div>
</li>
<li>
<p>To view the current XML configuration for a pool, use the <a href="https://libvirt.org/manpages/virsh.html#pool-dumpxml">pool-dumpxml</a> subcommand followed by the pool&#8217;s name.
Here I output the default pool&#8217;s XML configuration where you can verify <em>path</em> is as expected for the Flatpak.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">virsh pool-dumpxml gnome-boxes
&lt;pool <span class="nb">type</span><span class="o">=</span><span class="s1">'dir'</span><span class="o">&gt;</span>
  &lt;name&gt;images&lt;/name&gt;
  &lt;uuid&gt;02814071-7a82-4444-80f1-295cfc6f947d&lt;/uuid&gt;
  &lt;capacity <span class="nv">unit</span><span class="o">=</span><span class="s1">'bytes'</span><span class="o">&gt;</span>1999372288000&lt;/capacity&gt;
  &lt;allocation <span class="nv">unit</span><span class="o">=</span><span class="s1">'bytes'</span><span class="o">&gt;</span>191017480192&lt;/allocation&gt;
  &lt;available <span class="nv">unit</span><span class="o">=</span><span class="s1">'bytes'</span><span class="o">&gt;</span>1808354807808&lt;/available&gt;
  &lt;<span class="nb">source</span><span class="o">&gt;</span>
  &lt;/source&gt;
  &lt;target&gt;
    &lt;path&gt;/home/jordan/.var/app/org.gnome.Boxes/data/gnome-boxes/images&lt;/path&gt;
    &lt;permissions&gt;
      &lt;mode&gt;0775&lt;/mode&gt;
      &lt;owner&gt;1001&lt;/owner&gt;
      &lt;group&gt;1001&lt;/group&gt;
    &lt;/permissions&gt;
  &lt;/target&gt;
&lt;/pool&gt;</code></pre>
</div>
</div>
</li>
<li>
<p>To edit a pool&#8217;s configuration, use the <a href="https://libvirt.org/manpages/virsh.html#pool-edit">pool-edit</a> subcommand.
To modify the Boxes pool, the command would appear as follows.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">virsh pool-edit gnome-boxes</code></pre>
</div>
</div>
</li>
<li>
<p>To disable <em>CoW</em>, set the <em>cow</em> feature with <code>state=no</code> in the pool&#8217;s XML.</p>
<div class="openblock">
<div class="content">
<div class="paragraph">
<p>The snippet here illustrates the necessary XML.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="nt">&lt;features&gt;</span>
  <span class="nt">&lt;cow</span> <span class="na">state=</span><span class="s">'no'</span><span class="nt">&gt;</span>
<span class="nt">&lt;/features&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>For Boxes' storage pool, the resulting XML to disable <em>CoW</em> could appear like so.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="nt">&lt;pool</span> <span class="na">type=</span><span class="s">'dir'</span><span class="nt">&gt;</span>
  <span class="nt">&lt;name&gt;</span>gnome-boxes<span class="nt">&lt;/name&gt;</span>
  <span class="nt">&lt;uuid&gt;</span>02814071-7a82-4444-80f1-295cfc6f947d<span class="nt">&lt;/uuid&gt;</span>
  <span class="nt">&lt;capacity</span> <span class="na">unit=</span><span class="s">'bytes'</span><span class="nt">&gt;</span>1999372288000<span class="nt">&lt;/capacity&gt;</span>
  <span class="nt">&lt;allocation</span> <span class="na">unit=</span><span class="s">'bytes'</span><span class="nt">&gt;</span>191017480192<span class="nt">&lt;/allocation&gt;</span>
  <span class="nt">&lt;available</span> <span class="na">unit=</span><span class="s">'bytes'</span><span class="nt">&gt;</span>1808354807808<span class="nt">&lt;/available&gt;</span>
  <span class="nt">&lt;features&gt;</span>
    <span class="nt">&lt;cow</span> <span class="na">state=</span><span class="s">'no'</span><span class="nt">&gt;</span>
  <span class="nt">&lt;/features&gt;</span>
  <span class="nt">&lt;source&gt;</span>
  <span class="nt">&lt;/source&gt;</span>
  <span class="nt">&lt;target&gt;</span>
    <span class="nt">&lt;path&gt;</span>/home/jordan/.var/app/org.gnome.Boxes/data/gnome-boxes/images<span class="nt">&lt;/path&gt;</span>
    <span class="nt">&lt;permissions&gt;</span>
      <span class="nt">&lt;mode&gt;</span>0775<span class="nt">&lt;/mode&gt;</span>
      <span class="nt">&lt;owner&gt;</span>1001<span class="nt">&lt;/owner&gt;</span>
      <span class="nt">&lt;group&gt;</span>1001<span class="nt">&lt;/group&gt;</span>
    <span class="nt">&lt;/permissions&gt;</span>
  <span class="nt">&lt;/target&gt;</span>
<span class="nt">&lt;/pool&gt;</span></code></pre>
</div>
</div>
</div>
</div>
</li>
</ol>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>That should be everything you need to get started with GNOME Boxes on a Btrfs filesystem.
Enjoy that simple virtualization.</p>
</div>
</div>
</div>]]></content><author><name>Jordan Williams</name></author><category term="Virtualization" /><category term="Boxes" /><category term="Btrfs" /><category term="elementary" /><category term="GNOME" /><category term="KVM" /><category term="libvirt" /><category term="Linux" /><category term="QEMU" /><category term="Ubuntu" /><category term="virt-manager" /><category term="VM" /><summary type="html"><![CDATA[So, you&#8217;ve got libvirt installed on your Linux box and your looking for a simple application for running virtual machines. Look no further than Boxes, so far as it meets your needs, of course. What&#8217;s that you ask? What do you need to figure out to run on this on a Btrfs filesystem? Well, you&#8217;ve come to the right place! This post describes how to install and accommodate Boxes on Btrfs.]]></summary></entry><entry><title type="html">Install libvirt on elementary OS 5.1</title><link href="https://www.jwillikers.com/install-libvirt-on-elementary-os-5-1" rel="alternate" type="text/html" title="Install libvirt on elementary OS 5.1" /><published>2021-03-03T00:00:00+00:00</published><updated>2021-03-03T00:00:00+00:00</updated><id>https://www.jwillikers.com/Install%20libvirt%20on%20elementary%20OS%205.1</id><content type="html" xml:base="https://www.jwillikers.com/install-libvirt-on-elementary-os-5-1"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>If you want to run virtual machines on Linux, chances are you&#8217;re going to use <a href="https://libvirt.org/">libvirt</a>.
I make use of it all the time, especially for testing these blog posts in a clean environment.
libvirt provides a common interface around various underlying tools for virtual machine management.
It not only offers features for guest management but for networking and storage management as well.
It&#8217;s standard XML schema also makes for a powerful and versatile configuration format.
On Linux, libvirt is typically utilizing <a href="https://www.linux-kvm.org/page/Main_Page">KVM</a>, the virtualization layer in the kernel, and, in userspace, <a href="https://www.qemu.org/">QEMU</a>, a generic machine emulator and virtualizer.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="tutorial">Tutorial</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This tutorial provides the necessary steps to verify your system supports hardware virtualization and install libvirt on <a href="https://elementary.io/">elementary OS</a> 5.1.
Most of these steps are the same for <a href="https://ubuntu.com/">Ubuntu</a> 18.04.
This tutorial takes into account special considerations for systems using the <a href="https://btrfs.wiki.kernel.org/index.php/Main_Page">Btrfs</a> filesystem.
There is also a brief section on installing the graphical user interface for libvirt, <a href="https://virt-manager.org/">virt-manager</a>.
It is assumed that you are familiar with installing software on Ubuntu, using the command-line, and the Btrfs filesystem.</p>
</div>
<div class="sect2">
<h3 id="check">Check</h3>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Check that the system supports hardware virtualization.</p>
<div class="openblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">egrep <span class="nt">-c</span> <span class="s1">'(vmx|svm)'</span> /proc/cpuinfo
8</code></pre>
</div>
</div>
<div class="paragraph">
<p>If the output is not zero, then your CPU supports virtualization.</p>
</div>
</div>
</div>
</li>
<li>
<p>Install the tool for checking that your CPU is compatible with KVM.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>apt <span class="nt">-y</span> <span class="nb">install </span>cpu-checker</code></pre>
</div>
</div>
</li>
<li>
<p>Verify that the system supports KVM.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used</code></pre>
</div>
</div>
</li>
</ol>
</div>
<div class="paragraph">
<p>If all checks passed, then you should be able to continue installation of libvirt without issue.
Otherwise, you&#8217;d better switch to some compatible hardware before proceeding.</p>
</div>
</div>
<div class="sect2">
<h3 id="install">Install</h3>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>If you want to get a more up-to-date virtualization stack, add the <a href="https://launchpad.net/~jacob/+archive/ubuntu/virtualisation">virtualization PPA</a> to your system.</p>
<div class="olist loweralpha">
<ol class="loweralpha" type="a">
<li>
<p>The software-properties-common package includes a command for easily adding PPA&#8217;s.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>apt <span class="nt">-y</span> software-properties-common</code></pre>
</div>
</div>
</li>
<li>
<p>Add the <a href="https://launchpad.net/~jacob/+archive/ubuntu/virtualisation">virtualization PPA</a>.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>add-apt-repository <span class="nt">-uy</span> ppa:jacob/virtualisation</code></pre>
</div>
</div>
</li>
</ol>
</div>
</li>
<li>
<p>Install libvirt.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>apt <span class="nt">-y</span> <span class="nb">install </span>qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils</code></pre>
</div>
</div>
</li>
<li>
<p>Add the current user to the <em>kvm</em> and <em>libvirt</em> groups.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>usermod <span class="nt">-aG</span> kvm,libvirt <span class="nv">$USER</span></code></pre>
</div>
</div>
</li>
<li>
<p>Reload the current user&#8217;s group assignments.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">newgrp -</code></pre>
</div>
</div>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="greeter">Greeter</h3>
<div class="paragraph">
<p>On elementary OS 5.1, there&#8217;s a bit of a glitch after installing libvirt on the system.
That is, a new <em>libvirt-qemu</em> user appears as a logon option in Greeter.
This isn&#8217;t supposed to happen but luckily there&#8217;s a workaround.
The steps here hide the <em>libvirt-qemu</em> login in Greeter.
The steps were come from <a href="https://askubuntu.com/a/940069">this solution</a> on Stack Overflow.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Set the libvirt-qemu user account as a system account for the accountsservices package to hide it in the login menu.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">printf</span> <span class="s2">"[User]</span><span class="se">\n</span><span class="s2">SystemAccount=true</span><span class="se">\n</span><span class="s2">"</span> <span class="se">\</span>
  | <span class="nb">sudo tee</span> /var/lib/AccountsService/users/libvirt-qemu</code></pre>
</div>
</div>
</li>
<li>
<p>Restart the accounts service.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>systemctl restart accounts-daemon.service</code></pre>
</div>
</div>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="btrfs">Btrfs</h3>
<div class="paragraph">
<p>If you use Btrfs on your system like I do, then you&#8217;ll want to avoid <em>CoW</em> on <em>CoW</em> when using virtual machine disk images.
Using the default <em>CoW</em> qcow2 format for virtual disk images on top of a Btrfs filesystem is asking for trouble.
This section demonstrates the various ways of disabling <em>CoW</em> for virtual disk images on Btrfs filesystems.
If you snapshot your filesystem, take care to place virtual disk images in a subvolume that is excluded from snapshots.
Snapshots for virtual disk images should be handled in the disk image itself as is the case with the <a href="https://qemu.readthedocs.io/en/master/system/images.html#cmdoption-image-formats-arg-qcow2">qcow2</a> format.
At least, that&#8217;s the way until a Btrfs storage driver appears for libvirt.
I can hope.</p>
</div>
<div class="sect3">
<h4 id="qemu_img">qemu-img</h4>
<div class="paragraph">
<p>When creating a <a href="https://qemu.readthedocs.io/en/master/system/images.html#cmdoption-image-formats-arg-qcow2">qcow2</a> image directly with <a href="https://qemu.readthedocs.io/en/master/tools/qemu-img.html">qemu-img(1)</a>, the <a href="https://qemu.readthedocs.io/en/master/system/images.html#cmdoption-qcow2-arg-nocow">nocow</a> option can be used to disable <em>CoW</em> for that file.
The following command creates a 25 gigabyte qcow2 image named <code>my-vm-image.qcow2</code> with <em>CoW</em> disabled.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">qemu-img create <span class="nt">-o</span> nocow my-vm-image.qcow2 25G</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="libvirt_storage_pool_features">libvirt Storage Pool Features</h4>
<div class="paragraph">
<p>In <a href="https://www.libvirt.org/news.html#v6-6-0-2020-08-02">libvirt 6.6.0</a>, <a href="https://libvirt.org/formatstorage.html#StoragePoolFeatures">Storage Pool Features</a> were introduced, including the <em>cow</em> feature.
This version of libvirt disabled <em>CoW</em> by default on Btrfs filesystems.
This default behavior was quickly rescinded in <a href="https://www.libvirt.org/news.html#v6-7-0-2020-09-01">libvirt 6.7.0</a> which re-enabled <em>CoW</em> by default.
The change leaves the decision to disable <em>CoW</em> in the hands of system administrators.
If your lucky enough to be using libvirt 6.6.0 or newer, you can take advantage of this feature.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>elementary OS 5.1 and Ubuntu 18.04 only ship with access to libvirt 4.0.0
Even if you use the virtualization PPA, it only goes up to version 4.7.0 for Ubuntu 18.04.
You&#8217;ll need to get newer version by some external means or use a newer version of Ubuntu for this to work.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>libvirt uses the concept of storage pools to abstract the complexities involved in managing the underlying virtual machine disk images in a variety of situations.
I won&#8217;t delve into the details here.
Refer to <a href="https://libvirt.org/storage.html">Storage Management</a> for more information.
For the purposes of this post you should know that libvirt&#8217;s default directory for disk images is its default storage pool.
This pool is a simple <a href="https://libvirt.org/storage.html#StorageBackendDir">Directory pool</a>.
libvirt stores pretty much all configuration in XML files.
This is the case for storage pools and the XML can be viewed and edited with <a href="https://libvirt.org/manpages/virsh.html">virsh(1)</a>.
The steps here walk through the steps to disable <em>CoW</em> on the default storage pool.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>List storage pools with the <a href="https://libvirt.org/manpages/virsh.html#pool-list">pool-list</a> subcommand.
The default pool is just called <em>default</em>.
No surprises here.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">virsh pool-list
 Name                 State      Autostart
<span class="nt">-------------------------------------------</span>
 default              active     <span class="nb">yes</span></code></pre>
</div>
</div>
</li>
<li>
<p>To simply view the XML, use the <a href="https://libvirt.org/manpages/virsh.html#pool-dumpxml">pool-dumpxml</a> subcommand followed by the pool&#8217;s name.
Here I output the default pool&#8217;s XML configuration where you can see that <em>path</em> is indeed <code>/var/lib/libvirt/images</code>.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">virsh pool-dumpxml default
&lt;pool <span class="nb">type</span><span class="o">=</span><span class="s1">'dir'</span><span class="o">&gt;</span>
  &lt;name&gt;default&lt;/name&gt;
  &lt;uuid&gt;4f779eae-e312-4e4d-bf9f-fafe0e334f63&lt;/uuid&gt;
  &lt;capacity <span class="nv">unit</span><span class="o">=</span><span class="s1">'bytes'</span><span class="o">&gt;</span>1999372288000&lt;/capacity&gt;
  &lt;allocation <span class="nv">unit</span><span class="o">=</span><span class="s1">'bytes'</span><span class="o">&gt;</span>191017480192&lt;/allocation&gt;
  &lt;available <span class="nv">unit</span><span class="o">=</span><span class="s1">'bytes'</span><span class="o">&gt;</span>1808354807808&lt;/available&gt;
  &lt;<span class="nb">source</span><span class="o">&gt;</span>
  &lt;/source&gt;
  &lt;target&gt;
    &lt;path&gt;/var/lib/libvirt/images&lt;/path&gt;
    &lt;permissions&gt;
      &lt;mode&gt;0755&lt;/mode&gt;
      &lt;owner&gt;0&lt;/owner&gt;
      &lt;group&gt;0&lt;/group&gt;
    &lt;/permissions&gt;
  &lt;/target&gt;
&lt;/pool&gt;</code></pre>
</div>
</div>
</li>
<li>
<p>Edit a pool&#8217;s configuration with the <a href="https://libvirt.org/manpages/virsh.html#pool-edit">pool-edit</a> subcommand.
To modify the default pool&#8217;s XML, the command would appear as follows.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh">virsh pool-edit default</code></pre>
</div>
</div>
</li>
<li>
<p>To disable <em>CoW</em>, set the <em>cow</em> feature with <code>state=no</code> in the pool&#8217;s XML.</p>
<div class="openblock">
<div class="content">
<div class="paragraph">
<p>The snippet here demonstrates the XML to disable <em>CoW</em>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="nt">&lt;features&gt;</span>
  <span class="nt">&lt;cow</span> <span class="na">state=</span><span class="s">'no'</span><span class="nt">&gt;</span>
<span class="nt">&lt;/features&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>For the default storage pool, the resulting XML to disable <em>CoW</em> could appear like so.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="nt">&lt;pool</span> <span class="na">type=</span><span class="s">'dir'</span><span class="nt">&gt;</span>
  <span class="nt">&lt;name&gt;</span>default<span class="nt">&lt;/name&gt;</span>
  <span class="nt">&lt;uuid&gt;</span>4f779eae-e312-4e4d-bf9f-fafe0e334f63<span class="nt">&lt;/uuid&gt;</span>
  <span class="nt">&lt;capacity</span> <span class="na">unit=</span><span class="s">'bytes'</span><span class="nt">&gt;</span>1999372288000<span class="nt">&lt;/capacity&gt;</span>
  <span class="nt">&lt;allocation</span> <span class="na">unit=</span><span class="s">'bytes'</span><span class="nt">&gt;</span>191017480192<span class="nt">&lt;/allocation&gt;</span>
  <span class="nt">&lt;available</span> <span class="na">unit=</span><span class="s">'bytes'</span><span class="nt">&gt;</span>1808354807808<span class="nt">&lt;/available&gt;</span>
  <span class="nt">&lt;features&gt;</span>
    <span class="nt">&lt;cow</span> <span class="na">state=</span><span class="s">'no'</span><span class="nt">&gt;</span>
  <span class="nt">&lt;/features&gt;</span>
  <span class="nt">&lt;source&gt;</span>
  <span class="nt">&lt;/source&gt;</span>
  <span class="nt">&lt;target&gt;</span>
    <span class="nt">&lt;path&gt;</span>/var/lib/libvirt/images<span class="nt">&lt;/path&gt;</span>
    <span class="nt">&lt;permissions&gt;</span>
      <span class="nt">&lt;mode&gt;</span>0755<span class="nt">&lt;/mode&gt;</span>
      <span class="nt">&lt;owner&gt;</span>0<span class="nt">&lt;/owner&gt;</span>
      <span class="nt">&lt;group&gt;</span>0<span class="nt">&lt;/group&gt;</span>
    <span class="nt">&lt;/permissions&gt;</span>
  <span class="nt">&lt;/target&gt;</span>
<span class="nt">&lt;/pool&gt;</span></code></pre>
</div>
</div>
</div>
</div>
</li>
</ol>
</div>
</div>
<div class="sect3">
<h4 id="chattr">chattr</h4>
<div class="paragraph">
<p>The simplest way to disable <em>CoW</em> on a particular directory or file is with <a href="https://manpages.ubuntu.com/manpages/bionic/man1/chattr.1.html">chattr(1)</a> as described in <a href="https://btrfs.wiki.kernel.org/index.php/FAQ#Can_copy-on-write_be_turned_off_for_data_blocks.3F">Can copy-on-write be turned off for data blocks?</a>.
To do this, <em>add</em> the <em>no copy on write</em> attribute with the <code>+C</code> option.
The following commands disable <em>CoW</em> on libvirt&#8217;s image directory.</p>
</div>
<div class="paragraph">
<p>Disable <em>CoW</em> on the <code>/var/lib/libvirt/images</code> directory.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>chattr +C /var/lib/libvirt/images</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="a_flat_layout_subvolume">A Flat Layout Subvolume</h4>
<div class="paragraph">
<p>A dedicated Btrfs subvolume for <code>/var/lib/libvirt/images</code> is probably your best option since it excludes the disk images from snapshots.
The subvolume can have <em>CoW</em> disabled via chattr, but <em>CoW</em> can also be disabled with the mount option <code>nodatacow</code> when using a subvolume in a <a href="https://btrfs.wiki.kernel.org/index.php/SysadminGuide#Flat">flat layout</a>.
The steps here create a dedicated subvolume for libvirt&#8217;s disk image directory and mount it with <em>CoW</em> disabled.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Mount the root Btrfs filesystem to create a subvolume.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>mount <span class="o">(</span><span class="nb">df</span> <span class="nt">--output</span><span class="o">=</span><span class="nb">source</span> / | <span class="nb">tail</span> <span class="nt">-n</span> 1<span class="o">)</span> /mnt</code></pre>
</div>
</div>
</li>
<li>
<p>Create a dedicated Btrfs subvolume for libvirt&#8217;s virtual disk images.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>btrfs subvolume create /mnt/var-lib-libvirt-images
Create subvolume <span class="s1">'/mnt/var-lib-libvirt-images'</span></code></pre>
</div>
</div>
</li>
<li>
<p>Add the subvolume to <a href="http://manpages.ubuntu.com/manpages/bionic/man5/fstab.5.html">fstab(5)</a>.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">echo</span> <span class="o">(</span><span class="nb">df</span> <span class="nt">--output</span><span class="o">=</span><span class="nb">source</span> / <span class="se">\</span>
  | <span class="nb">tail</span> <span class="nt">-n</span> 1<span class="o">)</span><span class="s2">" /var/lib/libvirt/images btrfs defaults,nodatacow,noatime,subvol=var-lib-libvirt-images 0 0"</span> <span class="se">\</span>
  | <span class="nb">sudo tee</span> <span class="nt">-a</span> /etc/fstab
/dev/mapper/sda2_crypt /var/lib/libvirt/images btrfs defaults,nodatacow,noatime,subvol<span class="o">=</span>var-lib-libvirt-images 0 0</code></pre>
</div>
</div>
</li>
<li>
<p>Verify there are no errors in fstab.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>findmnt <span class="nt">--verify</span> <span class="nt">--verbose</span></code></pre>
</div>
</div>
</li>
<li>
<p>Now mount the subvolume according to the rule just added in fstab.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>mount /var/lib/libvirt/images</code></pre>
</div>
</div>
</li>
<li>
<p>Don&#8217;t forget to unmount <code>/mnt</code>.</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>umount /mnt</code></pre>
</div>
</div>
</li>
</ol>
</div>
<div class="paragraph">
<p>That&#8217;s it!
The default storage pool for libvirt will store virtual disk images in this subvolume.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="virt_manager">virt-manager</h3>
<div class="paragraph">
<p><a href="https://virt-manager.org/">virt-manager</a> is an application for managing virtual machines with libvirt graphically.
It&#8217;s a handy one for the toolbox, though some might prefer the simplicity of <a href="https://wiki.gnome.org/Apps/Boxes">Boxes</a>.</p>
</div>
<div class="paragraph">
<p>Install virt-manager.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sh"><span class="nb">sudo </span>apt <span class="nt">-y</span> <span class="nb">install </span>virt-manager</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you haven&#8217;t logged out and back in since installing libvirt, you&#8217;ll need to that before running virt-manager.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You should now be able to get virtual machines up and running without issue.
Now that you have all the components in place for virtualization, why not make your life easier with <a href="https://wiki.gnome.org/Apps/Boxes">Boxes</a>?
I&#8217;ll cover all the details of installing the GNOME Boxes Flatpak on a Btrfs system in an upcoming post, so stay tuned!</p>
</div>
</div>
</div>]]></content><author><name>Jordan Williams</name></author><category term="Virtualization" /><category term="Boxes" /><category term="Btrfs" /><category term="elementary" /><category term="KVM" /><category term="libvirt" /><category term="Linux" /><category term="QEMU" /><category term="systemd" /><category term="Ubuntu" /><category term="virsh" /><category term="virt-manager" /><category term="VM" /><summary type="html"><![CDATA[If you want to run virtual machines on Linux, chances are you&#8217;re going to use libvirt. I make use of it all the time, especially for testing these blog posts in a clean environment. libvirt provides a common interface around various underlying tools for virtual machine management. It not only offers features for guest management but for networking and storage management as well. It&#8217;s standard XML schema also makes for a powerful and versatile configuration format. On Linux, libvirt is typically utilizing KVM, the virtualization layer in the kernel, and, in userspace, QEMU, a generic machine emulator and virtualizer.]]></summary></entry></feed>