<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Podman on Major Hayden</title><link>https://major.io/tags/podman/</link><description>Recent content in Podman on Major Hayden</description><generator>Hugo</generator><language>en</language><managingEditor>major@mhtx.net (Major Hayden)</managingEditor><webMaster>major@mhtx.net (Major Hayden)</webMaster><copyright>All content licensed [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)</copyright><lastBuildDate>Thu, 09 Jul 2026 20:41:49 +0000</lastBuildDate><atom:link href="https://major.io/tags/podman/index.xml" rel="self" type="application/rss+xml"/><item><title>Getting podman quadlets talking to each other</title><link>https://major.io/p/quadlet-networking/</link><pubDate>Thu, 25 Sep 2025 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/quadlet-networking/</guid><description>&lt;p&gt;Quadlets are a handy way to manage containers using systemd unit files.
Containers running via quadlets have access to the external network by default, but they don&amp;rsquo;t automatically communicate with each other like they do in a &lt;code&gt;docker-compose&lt;/code&gt; setup.
Adding networking only requires a few extra steps.&lt;/p&gt;
&lt;h2 id="setting-up-some-quadlets"&gt;Setting up some quadlets&lt;/h2&gt;
&lt;p&gt;I often need a postgres server laying around on my local machine for quick tasks or testing something I&amp;rsquo;m working on.
Lately, I&amp;rsquo;ve been focused on RAG databases and that usually involves &lt;a href="https://github.com/pgvector/pgvector"&gt;pgvector&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The pgvector extension adds vector data types and functions to PostgreSQL, which is great for storing embeddings from machine learning models.
You can search via all of the usual SQL queries that you&amp;rsquo;re used to, but pgvector adds new capabilities for searching rows based on vector similarity.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the quadlet for pgvector in &lt;code&gt;~/.config/containers/systemd/pgvector.container&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;pgvector container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Container]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/pgvector/pgvector:pg17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;pgvector:/var/lib/postgresql/data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;POSTGRES_USER=postgres&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;POSTGRES_PASSWORD=secrete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;POSTGRES_DB=postgres&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;PublishPort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5432:5432&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This gets a postgres server with pgvector up and running with a persistent volume.
It&amp;rsquo;s listening on the default port 5432.&lt;/p&gt;
&lt;p&gt;Sometimes I&amp;rsquo;m in a hurry and &lt;a href="https://www.pgadmin.org/"&gt;pgadmin4&lt;/a&gt; is a quick way to poke around the database.
It&amp;rsquo;s also a good example here since it needs to talk to the pgvector container.
Here&amp;rsquo;s the quadlet for pgadmin4 in &lt;code&gt;~/.config/containers/systemd/pgadmin4.container&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;pgAdmin4 container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Container]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/dpage/pgadmin4:latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;pgadmin4:/var/lib/pgadmin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;PGADMIN_DEFAULT_EMAIL=major@mhtx.net&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;PGADMIN_DEFAULT_PASSWORD=secrete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;PGADMIN_CONFIG_SERVER_MODE=False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED=False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;PublishPort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8080:80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Awesome! Let&amp;rsquo;s reload the systemd configuration for my user account and start these containers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user daemon-reload
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user start pgvector
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user start pgadmin4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can check the running containers:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;gt; podman ps --format &amp;#34;table {{.ID}}\t{{.Names}}&amp;#34;
CONTAINER ID NAMES
b099fdaa6b18 valkey
f8ab764c299c systemd-pgadmin4
052c160fb45b systemd-pgvector
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="testing-communication"&gt;Testing communication&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s hop into the pgadmin4 container and see if we can connect to the pgvector database:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;gt; podman exec -it systemd-pgadmin4 ping pgvector -c 4
ping: bad address &amp;#39;pgvector&amp;#39;
&amp;gt; podman exec -it systemd-pgadmin4 ping systemd-pgvector -c 4
ping: bad address &amp;#39;systemd-pgvector&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This isn&amp;rsquo;t great.
There are two problems here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The containers aren&amp;rsquo;t on the same network&lt;/li&gt;
&lt;li&gt;I want to refer to the pgvector container as &lt;code&gt;pgvector&lt;/code&gt;, not &lt;code&gt;systemd-pgvector&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s fix that.&lt;/p&gt;
&lt;h2 id="fixing-communication"&gt;Fixing communication&lt;/h2&gt;
&lt;p&gt;Open up the &lt;code&gt;~/.config/containers/systemd/pgvector.container&lt;/code&gt; file and make the two changes noted below with comments:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;pgvector container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Container]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Use a consistent name 👇&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ContainerName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;pgvector&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/pgvector/pgvector:pg17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;pgvector:/var/lib/postgresql/data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;POSTGRES_USER=postgres&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;POSTGRES_PASSWORD=secrete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;POSTGRES_DB=postgres&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Add the container to a network 👇&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;db-network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;PublishPort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5432:5432&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also open the &lt;code&gt;~/.config/containers/systemd/pgadmin4.container&lt;/code&gt; file and make the same network change:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;pgAdmin4 container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Container]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/dpage/pgadmin4:latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;pgadmin4:/var/lib/pgadmin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;PGADMIN_DEFAULT_EMAIL=major@mhtx.net&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;PGADMIN_DEFAULT_PASSWORD=secrete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;PGADMIN_CONFIG_SERVER_MODE=False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED=False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Add the container to a network 👇&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;db-network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;PublishPort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8080:80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create the network:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman network create db-network
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, reload the systemd configuration and restart the containers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user daemon-reload
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user restart pgvector
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user restart pgadmin4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="testing-communication-again"&gt;Testing communication again&lt;/h2&gt;
&lt;p&gt;Now, let&amp;rsquo;s hop into the pgadmin4 container and see if we can connect to the pgvector database:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;gt; podman exec -it systemd-pgadmin4 ping pgvector -c 4
PING pgvector (10.89.5.6): 56 data bytes
64 bytes from 10.89.5.6: seq=0 ttl=42 time=0.026 ms
64 bytes from 10.89.5.6: seq=1 ttl=42 time=0.036 ms
64 bytes from 10.89.5.6: seq=2 ttl=42 time=0.034 ms
64 bytes from 10.89.5.6: seq=3 ttl=42 time=0.088 ms

--- pgvector ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.026/0.046/0.088 ms
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Perfect!&lt;/strong&gt; 🎉 🎉 🎉&lt;/p&gt;
&lt;h2 id="extra-credit"&gt;Extra credit&lt;/h2&gt;
&lt;p&gt;If you want to deploy your system with automation and avoid the manual network creation, you can add one extra file to your &lt;code&gt;~/.config/containers/systemd/&lt;/code&gt; directory.
Save this as &lt;code&gt;db-network.network&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Network]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;app=db-network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Automatic container updates with Podman quadlets</title><link>https://major.io/p/podman-quadlet-automatic-updates/</link><pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/podman-quadlet-automatic-updates/</guid><description>&lt;p&gt;Running containers at home or in production often means juggling updates across multiple services.
While orchestration platforms like Kubernetes handle this automatically, what about those simple deployments on a single host?&lt;/p&gt;
&lt;p&gt;Podman&amp;rsquo;s quadlet system integrates containers directly with systemd, and when combined with automatic updates, you get a robust solution that keeps your containers fresh without manual intervention.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s explore how to set up automatic container updates using Podman quadlets on Fedora, turning container management into a hands-off operation that just works.&lt;/p&gt;
&lt;h2 id="setting-up-a-basic-quadlet"&gt;Setting up a basic quadlet&lt;/h2&gt;
&lt;p&gt;First, let&amp;rsquo;s create a simple quadlet for running a Valkey database service under a user account.
Quadlet files for user services live in &lt;code&gt;~/.config/containers/systemd/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Create the directory if it doesn&amp;rsquo;t exist:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p ~/.config/containers/systemd/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then create your quadlet file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ~/.config/containers/systemd/valkey.container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Container]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ContainerName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;valkey&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/valkey/valkey:latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;io.containers.autoupdate=registry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;PublishPort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;16379:6379&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;valkey_data:/data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;default.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The magic happens with the &lt;code&gt;Label=io.containers.autoupdate=registry&lt;/code&gt; line.
This label tells Podman that this container should be automatically updated when a newer image is available in the registry.&lt;/p&gt;
&lt;p&gt;After creating the file, reload systemd and start your container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user daemon-reload
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user start valkey.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Your container is now running as a user systemd service!
Check its status with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user status valkey.service
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman ps
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="enabling-automatic-updates"&gt;Enabling automatic updates&lt;/h2&gt;
&lt;p&gt;Podman ships with a systemd timer that checks for container updates.
The &lt;code&gt;podman-auto-update.timer&lt;/code&gt; runs daily by default, but you need to enable it for your user:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user &lt;span class="nb"&gt;enable&lt;/span&gt; --now podman-auto-update.timer
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can check when the next update will run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user list-timers podman-auto-update.timer
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When the timer triggers, it runs &lt;code&gt;podman auto-update&lt;/code&gt;, which:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Checks all containers with the &lt;code&gt;io.containers.autoupdate&lt;/code&gt; label&lt;/li&gt;
&lt;li&gt;Pulls newer images if available&lt;/li&gt;
&lt;li&gt;Restarts containers with the new image&lt;/li&gt;
&lt;li&gt;Keeps the old image in case you need to roll back&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="customizing-update-behavior"&gt;Customizing update behavior&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;io.containers.autoupdate&lt;/code&gt; label supports different values for various update strategies:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Always pull the latest image from the registry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;io.containers.autoupdate=registry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Only update if the local image changes (useful for locally built images)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;io.containers.autoupdate=local&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also customize when updates occur by creating a timer override:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user edit podman-auto-update.timer
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add these lines to run updates every 6 hours instead of daily:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Timer]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;OnCalendar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;OnCalendar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;*-*-* 00,06,12,18:00:00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="monitoring-updates"&gt;Monitoring updates&lt;/h2&gt;
&lt;p&gt;Track what&amp;rsquo;s happening with your automatic updates using journalctl:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# View recent auto-update logs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;journalctl --user -u podman-auto-update.service -n &lt;span class="m"&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Follow updates in real-time&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;journalctl --user -u podman-auto-update.service -f
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Check a specific container&amp;#39;s restart history&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;journalctl --user -u valkey.service &lt;span class="p"&gt;|&lt;/span&gt; grep Started
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="further-reading"&gt;Further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.podman.io/en/latest/markdown/podman-auto-update.1.html"&gt;Podman documentation on auto-updates&lt;/a&gt; - Official documentation for the auto-update feature&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html"&gt;Systemd Quadlet documentation&lt;/a&gt; - Complete reference for quadlet unit files&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/sysadmin/quadlet-podman"&gt;Red Hat&amp;rsquo;s guide to Podman quadlets&lt;/a&gt; - Excellent introduction with more examples&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>How I learned to stop worrying and love the CoreOS</title><link>https://major.io/p/why-coreos/</link><pubDate>Fri, 13 Oct 2023 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/why-coreos/</guid><description>&lt;hr&gt;
&lt;p&gt;It&amp;rsquo;s quite clear that I&amp;rsquo;ve been on a &lt;a href="https://major.io/tags/coreos/"&gt;CoreOS&lt;/a&gt; blogging streak lately.
I keep getting asked by people inside and outside my company about what makes CoreOS special and why I&amp;rsquo;ve switched over so many workloads to it.&lt;/p&gt;
&lt;p&gt;The answer is pretty basic.
&lt;strong&gt;It makes my life easier.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m a Dad.
I&amp;rsquo;m on the PTC (Parent Teacher Club) at one of my children&amp;rsquo;s schools.
I volunteer as an IT person for a non-profit.
I write software.
I have other time consuming hobbies, such as ham radio, reading, and becoming a longer distance runner&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;My available time for my own IT projects is &lt;strong&gt;extremely limited&lt;/strong&gt; and CoreOS plays a part in keeping that part of my life as efficient as possible.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s what this blog post is about!&lt;/p&gt;
&lt;h1 id="updates"&gt;Updates&lt;/h1&gt;
&lt;p&gt;First and foremost, I love how CoreOS does updates.
I encourage you to &lt;a href="https://docs.fedoraproject.org/en-US/fedora-coreos/auto-updates/"&gt;read the docs&lt;/a&gt; on this topic, but here&amp;rsquo;s a short explanation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Updates are automatically retrieved and they&amp;rsquo;re loaded into a slot.&lt;/li&gt;
&lt;li&gt;Your system reboots into the new update but your original OS tree remains in place.&lt;/li&gt;
&lt;li&gt;Did the update boot? Awesome. You&amp;rsquo;re good to go.&lt;/li&gt;
&lt;li&gt;Did something break? The system reverts back to the known good tree.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this way, it&amp;rsquo;s a lot like your smartphone.&lt;/p&gt;
&lt;p&gt;You have full control over when a node looks for an update and how often it checks for them.
Check out the &lt;a href="https://coreos.github.io/zincati/usage/updates-strategy/"&gt;Zincati docs&lt;/a&gt; for tons of controls over updates and reboots.&lt;/p&gt;
&lt;p&gt;Some of mine are timed so well that I set maintenance windows with my monitoring provider when I know an update might take place.
The updates come through, monitoring shuts off, the node reboots, and monitoring comes back.
The nodes almost always come back before the monitoring even alerts me.&lt;/p&gt;
&lt;p&gt;It also removes the reminders I would set for myself to update packages and run reboots.
I know that my CoreOS nodes will do this automatically, so I don&amp;rsquo;t need to think about it.&lt;/p&gt;
&lt;p&gt;Also, updates are rarely ever impactful to my workloads since all of them are running inside containers.
My containers come right back up as soon as the node finishes its reboot.&lt;/p&gt;
&lt;h1 id="toolbox"&gt;Toolbox&lt;/h1&gt;
&lt;p&gt;To be fair, you can get toolbox running on lots of different Linux distributions outside of CoreOS, but that&amp;rsquo;s the first place I ever used it.
Toolbox, also called &lt;a href="https://containertoolbx.org/"&gt;toolbx&lt;/a&gt;, gives you a utility container on your CoreOS node for all kinds of adminsitrative and diagnostic capabilities.&lt;/p&gt;
&lt;p&gt;You might need a certain package for diagnosting a hardware issue or you might want to install some helpful utilities for the command line.
Do that in a toolbox container.
Just run &lt;code&gt;toolbox enter&lt;/code&gt; and if you&amp;rsquo;ve never created a toolbox container before, you&amp;rsquo;ll get a Fedora container that matches your CoreOS release.&lt;/p&gt;
&lt;p&gt;But it gets better.&lt;/p&gt;
&lt;p&gt;Toolbox automatically saves your container when you&amp;rsquo;re done with it so all of your installed packages stay there for next time.
Also, these containers have seamless access to anything you have in your home directory, including sockets.
You&amp;rsquo;re running inside a container, but it&amp;rsquo;s almost like you&amp;rsquo;re running on the host itself inside your home directory.
You get the best of both worlds.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t want Fedora?
You have lots of distribution options through toolbox.
Read the details on &lt;a href="https://containertoolbx.org/install/"&gt;custom images&lt;/a&gt; to create your own!&lt;/p&gt;
&lt;h1 id="layering"&gt;Layering&lt;/h1&gt;
&lt;p&gt;Okay, there are those situations where you really want a package on CoreOS and toolbox might not be sufficient.
My muscle memory for &lt;code&gt;vim&lt;/code&gt; is so strong and CoreOS only comes with &lt;code&gt;vi&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You have a couple of options here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;sudo rpm-ostree install vim&lt;/code&gt;, reboot, and you have &lt;code&gt;vim&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;sudo rpm-ostree install --apply-live vim&lt;/code&gt; and you have &lt;code&gt;vim&lt;/code&gt; right now!
&lt;em&gt;(And it&amp;rsquo;s there after a reboot as well.)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When a new update comes down for the base OS from CoreOS, any packages you&amp;rsquo;ve added will be layered on the base image and available after a reboot.
Layering is generally chosen as a last resort option for adding packages to the system but you shouldn&amp;rsquo;t run into issues if you&amp;rsquo;re installing small utilities or command line tools.&lt;/p&gt;
&lt;h1 id="declarative-provisioning"&gt;Declarative provisioning&lt;/h1&gt;
&lt;p&gt;If you&amp;rsquo;ve provisioned Linux distributions on cloud instances in the past, you&amp;rsquo;ve likely provided metadata that cloud-init uses to provision your system.
CoreOS has something that acts a lot earlier in the boot process and has more power to get things done: &lt;a href="https://coreos.github.io/ignition/"&gt;ignition&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a handy &lt;a href="https://coreos.github.io/butane/"&gt;butane&lt;/a&gt; file forma that you use for writing your configuration.
You use the &lt;code&gt;butane&lt;/code&gt; utility to get it into ignition format.
The ignition format is highly compressed to ensure you can fit your configuration into most cloud providers&amp;rsquo; metadata fields.&lt;/p&gt;
&lt;p&gt;For a real example of what you can do with ignition, check out my &lt;a href="https://major.io/p/quadlets-replace-docker-compose/"&gt;quadlets post&lt;/a&gt; where I provisioned an entire Wordpress container stack using a single ignition file.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s lots of documentation for &lt;a href="https://docs.fedoraproject.org/en-US/fedora-coreos/producing-ign/"&gt;writing butane configuration&lt;/a&gt; files for common situations.
It&amp;rsquo;s easy to add files, configure Wireguard, set up users, and launch containers immediately on the first boot.&lt;/p&gt;
&lt;h1 id="pets-and-cattle"&gt;Pets and cattle&lt;/h1&gt;
&lt;p&gt;CoreOS works well for systems that I only need online for a short time.
These might be situations where I need to test a few containers and throw it away.
There&amp;rsquo;s no OS to mess with and no updates to worry about.&lt;/p&gt;
&lt;p&gt;It also works well for systems that I keep online for a long time.
I have a few physical systems at home that run CoreOS and they&amp;rsquo;ve been extremely stable.
I also have cloud instances on Hetzner, VULTR, and Digital Ocean that have run CoreOS for months without issues.&lt;/p&gt;
&lt;h1 id="more-questions"&gt;More questions?&lt;/h1&gt;
&lt;p&gt;Feel free to &lt;a href="mailto:major+coreos@mhtx.net"&gt;send me an email&lt;/a&gt;.
I&amp;rsquo;ll update this post if I get some good ones!&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Completing a half marathon without keeling over is the current goal! 👟&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Quadlets might make me finally stop using docker-compose</title><link>https://major.io/p/quadlets-replace-docker-compose/</link><pubDate>Mon, 25 Sep 2023 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/quadlets-replace-docker-compose/</guid><description>&lt;hr&gt;
&lt;p&gt;I&amp;rsquo;ve &lt;a href="https://major.io/tags/containers/"&gt;written a lot about containers&lt;/a&gt; on this blog.
Why do I love containers so much?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They start quickly&lt;/li&gt;
&lt;li&gt;They make your workloads portable&lt;/li&gt;
&lt;li&gt;They disconnect your application stack from the OS that runs underneath&lt;/li&gt;
&lt;li&gt;You can send your application through CI as a single container image&lt;/li&gt;
&lt;li&gt;You can isolate workloads on the network and limit their resource usage much like a VM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, I&amp;rsquo;m still addicted to &lt;a href="https://docs.docker.com/compose/"&gt;docker-compose&lt;/a&gt;.
Can podman&amp;rsquo;s &lt;a href="https://www.redhat.com/sysadmin/quadlet-podman"&gt;quadlets&lt;/a&gt; change that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Yes, I think they can.&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id="whats-a-quadlet"&gt;What&amp;rsquo;s a quadlet?&lt;/h1&gt;
&lt;p&gt;Podman introduced support for quadlets in version 4.4 and it&amp;rsquo;s a simpler way of letting systemd manage your containers.
There was an option in the past to have podman generate systemd unit files, but those were unwieldy and full of podman command line options inside a unit file.
These unit files weren&amp;rsquo;t easy to edit or even parse with eyeballs.&lt;/p&gt;
&lt;p&gt;Quadlets make this easier by giving you a simple ini-style file that you can easily read and edit.
This blog post will include some quadlets later, but here&amp;rsquo;s an example one for Wordpress:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Wordpress Quadlet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Container]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/library/wordpress:fpm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ContainerName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;wordpress&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;AutoUpdate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;registry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/core/.config/containers/containers-environment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;wordpress.volume:/var/www/html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;wordpress.network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;TimeoutStartSec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;900&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;caddy.service multi-user.target default.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lots of the lines under &lt;code&gt;[Container]&lt;/code&gt; should look familiar to most readers who have worked with containers before.
However, there&amp;rsquo;s something new here.&lt;/p&gt;
&lt;p&gt;Check out the &lt;code&gt;AutoUpdate=registry&lt;/code&gt; line.
This tells podman to keep your container updated on a regular basis with the upstream container registry.
I&amp;rsquo;ve used &lt;a href="https://major.io/p/podman-quadlet-watchtower/"&gt;watchtower&lt;/a&gt; in the past for this, but it requires a privileged container and it&amp;rsquo;s yet another external dependency.&lt;/p&gt;
&lt;p&gt;Also, at the very end, you&amp;rsquo;ll see a &lt;code&gt;WantedBy&lt;/code&gt; line.
This is a great place to set up container dependencies.
In this example, the container that runs &lt;code&gt;caddy&lt;/code&gt; (a web server) can&amp;rsquo;t start until Wordpress is up and running.&lt;/p&gt;
&lt;h1 id="so-why-not-stick-with-docker-compose"&gt;So why not stick with docker-compose?&lt;/h1&gt;
&lt;p&gt;There&amp;rsquo;s no denying that docker-compose is an awesome tool.
You specify the desired outcome, tell it to bring up containers, and it gets containers into the state you specified.
It handles volumes, networks, and complicated configuration without a lot of legwork.
The YAML files are pretty easy to read, too.&lt;/p&gt;
&lt;p&gt;However, as with watchtower, that&amp;rsquo;s another external dependency.&lt;/p&gt;
&lt;p&gt;My container deployments are often done at instance boot time and I don&amp;rsquo;t make too many changes afterwards.
I found myself using docker-compose for the initial deployment and then I didn&amp;rsquo;t really use it again.&lt;/p&gt;
&lt;p&gt;Why not remove it entirely and use what&amp;rsquo;s built into CoreOS already?&lt;/p&gt;
&lt;h1 id="quaint-quadlets-quickly"&gt;Quaint quadlets quickly!&lt;/h1&gt;
&lt;p&gt;Before we start, we&amp;rsquo;re going to need a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An easy to read &lt;a href="https://coreos.github.io/butane/"&gt;butane&lt;/a&gt; configuration which gets transformed into a tiny &lt;a href="https://coreos.github.io/ignition/"&gt;ignition&lt;/a&gt; configuration for CoreOS&lt;/li&gt;
&lt;li&gt;Some quadlets&lt;/li&gt;
&lt;li&gt;Extra system configuration&lt;/li&gt;
&lt;li&gt;A cloud provider with CoreOS images &lt;em&gt;(using &lt;a href="https://www.vultr.com/?ref=9544589-8H"&gt;VULTR&lt;/a&gt; for this)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;ve packed all of these items into my &lt;a href="https://github.com/major/quadlets-wordpress"&gt;quadlets-wordpress&lt;/a&gt; repository to make it easy.
Start by looking at the &lt;a href="https://github.com/major/quadlets-wordpress/blob/main/config.butane"&gt;config.butane&lt;/a&gt; file.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s break it down here.
First up, we add an ssh key for the default &lt;code&gt;core&lt;/code&gt; user.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;fcos&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1.5.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ssh_authorized_keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDyoH6gU4lgEiSiwihyD0Rxk/o5xYIfA3stVDgOGM9N0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next up, we enable the &lt;code&gt;podman-auto-update.timer&lt;/code&gt; so we get container updates automatically:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/home/core/.config/systemd/user/timers.target.wants/podman-auto-update.timer&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/usr/lib/systemd/user/podman-auto-update.timer&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next is the long &lt;code&gt;files&lt;/code&gt; section:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Ensure the `core` user can keep processes running after they&amp;#39;re logged out.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/var/lib/systemd/linger/core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0644&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Allow caddy to listen on 80 and 443.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Allow it to ask for bigger network buffers, too.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/etc/sysctl.d/90-caddy.conf&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; net.ipv4.ip_unprivileged_port_start = 80
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; net.core.rmem_max=2500000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; net.core.wmem_max=2500000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Set up an an environment file that containers can read to configure themselves.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/home/core/.config/containers/containers-environment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; MYSQL_DATABASE=wordpress
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; MYSQL_USER=wordpress
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; MYSQL_ROOT_PASSWORD=mariadb-needs-a-secure-password
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; MYSQL_PASSWORD=wordpress-needs-a-secure-password
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; WORDPRESS_DB_HOST=mariadb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; WORDPRESS_DB_USER=wordpress
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; WORDPRESS_DB_PASSWORD=wordpress-needs-a-secure-password
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; WORDPRESS_DB_NAME=wordpress&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0644&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Deploy the caddy configuration file from the repository.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/home/core/.config/caddy/Caddyfile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;caddy/Caddyfile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0644&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Add some named volumes for caddy and wordpress.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/home/core/.config/containers/systemd/caddy-config.volume&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; [Volume]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/home/core/.config/containers/systemd/caddy-data.volume&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; [Volume]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/home/core/.config/containers/systemd/wordpress.volume&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; [Volume]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Create a network for all the containers to use and enable the&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# DNS plugin. This allows containers to find each other using&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# the container names.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/home/core/.config/containers/systemd/wordpress.network&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; [Network]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; DisableDNS=false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Internal=false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Add the wordpress container.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/home/core/.config/containers/systemd/wordpress.container&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;quadlets/wordpress.container&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0644&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Add the MariaDB container.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/home/core/.config/containers/systemd/mariadb.container&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;quadlets/mariadb.container&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0644&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Add the caddy container.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/home/core/.config/containers/systemd/caddy.container&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;quadlets/caddy.container&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0644&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;a href="https://github.com/major/quadlets-wordpress/blob/main/caddy/Caddyfile"&gt;Caddyfile&lt;/a&gt; is also in the repository and will be deployed by the butane configuration shown above.&lt;/p&gt;
&lt;p&gt;We can go through each quadlet in detail.
First up is MariaDB.
We tell systemd that the wordpress container will want to have this one started first.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;MariaDB Quadlet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Container]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/library/mariadb:11&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ContainerName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;mariadb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;AutoUpdate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;registry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/core/.config/containers/containers-environment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;mariadb.volume:/var/lib/mysql&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;wordpress.network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;TimeoutStartSec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;900&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;wordpress.service multi-user.target default.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The wordpress quadlet is much the same as the MariaDB one, but we tell systemd that caddy will want wordpress started first.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Wordpress Quadlet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Container]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/library/wordpress:fpm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ContainerName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;wordpress&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;AutoUpdate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;registry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/core/.config/containers/containers-environment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;wordpress.volume:/var/www/html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;wordpress.network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;TimeoutStartSec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;900&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;caddy.service multi-user.target default.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, the caddy quadlet contains four volumes and some published ports.
These ports will be published to the container host.
Also, you&amp;rsquo;ll note that the wordpress volume is mounted here, too.
This is because caddy can serve static files &lt;em&gt;much faster&lt;/em&gt; than wordpress can.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Caddy Quadlet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Container]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/library/caddy:latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ContainerName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;caddy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;AutoUpdate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;registry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/core/.config/containers/containers-environment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;caddy-data.volume:/data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;caddy-config.volume:/config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/core/.config/caddy/Caddyfile:/etc/caddy/Caddyfile:Z&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;wordpress.volume:/var/www/html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;PublishPort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;80:80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;PublishPort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;443:443&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;wordpress.network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;TimeoutStartSec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;900&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target default.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="launch-the-quadlets"&gt;Launch the quadlets&lt;/h1&gt;
&lt;p&gt;There&amp;rsquo;s a &lt;a href="https://github.com/major/quadlets-wordpress/blob/main/launch-instance"&gt;launch script&lt;/a&gt; that ships this configuration to VULTR and launches a CoreOS instance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# This command starts up a CoreOS instance on Vultr using the vultr-cli&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vultr-cli instance create &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --os &lt;span class="m"&gt;391&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --plan vhp-1c-1gb-amd &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --region dfw &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --notify &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --ipv6 &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -u &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;butane --files-dir . config.butane&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -l &lt;span class="s2"&gt;&amp;#34;coreos-&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;date &lt;span class="s2"&gt;&amp;#34;+%s&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To launch an instance, get your &lt;a href="https://my.vultr.com/settings/#settingsapi"&gt;VULTR API key&lt;/a&gt; first.
Then install vultr-cli and butane:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo dnf -y install butane vultr-cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After launch, check to see what your containers are doing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;[core@vultr ~]$&lt;/span&gt; podman ps
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;afa2d6501593 docker.io/library/caddy:latest caddy run --confi... 54 seconds ago Up 53 seconds 0.0.0.0:80-&amp;gt;80/tcp, 0.0.0.0:443-&amp;gt;443/tcp caddy
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;460426f39e6c docker.io/library/mariadb:11 mariadbd 35 seconds ago Up 35 seconds mariadb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;92ece6538d5a docker.io/library/wordpress:fpm php-fpm 28 seconds ago Up 29 seconds wordpress
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We should be able to talk to wordpress through caddy on port 80:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;[core@vultr ~]$&lt;/span&gt; curl -si http://localhost/wp-admin/install.php &lt;span class="p"&gt;|&lt;/span&gt; head -n &lt;span class="m"&gt;25&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;HTTP/1.1 200 OK
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Cache-Control: no-cache, must-revalidate, max-age=0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Content-Type: text/html; charset=utf-8
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Expires: Wed, 11 Jan 1984 05:00:00 GMT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Server: Caddy
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;X-Powered-By: PHP/8.0.30
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Date: Mon, 25 Sep 2023 21:43:40 GMT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Transfer-Encoding: chunked
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;lt;!DOCTYPE html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;lt;html lang=&amp;#34;en-US&amp;#34; xml:lang=&amp;#34;en-US&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;lt;head&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;	&amp;lt;meta name=&amp;#34;viewport&amp;#34; content=&amp;#34;width=device-width&amp;#34; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;	&amp;lt;meta http-equiv=&amp;#34;Content-Type&amp;#34; content=&amp;#34;text/html; charset=utf-8&amp;#34; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;	&amp;lt;meta name=&amp;#34;robots&amp;#34; content=&amp;#34;noindex,nofollow&amp;#34; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;	&amp;lt;title&amp;gt;WordPress &amp;amp;rsaquo; Installation&amp;lt;/title&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;	&amp;lt;link rel=&amp;#39;stylesheet&amp;#39; id=&amp;#39;dashicons-css&amp;#39; href=&amp;#39;http://localhost/wp-includes/css/dashicons.min.css?ver=6.3.1&amp;#39; type=&amp;#39;text/css&amp;#39; media=&amp;#39;all&amp;#39; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;lt;link rel=&amp;#39;stylesheet&amp;#39; id=&amp;#39;buttons-css&amp;#39; href=&amp;#39;http://localhost/wp-includes/css/buttons.min.css?ver=6.3.1&amp;#39; type=&amp;#39;text/css&amp;#39; media=&amp;#39;all&amp;#39; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;lt;link rel=&amp;#39;stylesheet&amp;#39; id=&amp;#39;forms-css&amp;#39; href=&amp;#39;http://localhost/wp-admin/css/forms.min.css?ver=6.3.1&amp;#39; type=&amp;#39;text/css&amp;#39; media=&amp;#39;all&amp;#39; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;lt;link rel=&amp;#39;stylesheet&amp;#39; id=&amp;#39;l10n-css&amp;#39; href=&amp;#39;http://localhost/wp-admin/css/l10n.min.css?ver=6.3.1&amp;#39; type=&amp;#39;text/css&amp;#39; media=&amp;#39;all&amp;#39; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;lt;link rel=&amp;#39;stylesheet&amp;#39; id=&amp;#39;install-css&amp;#39; href=&amp;#39;http://localhost/wp-admin/css/install.min.css?ver=6.3.1&amp;#39; type=&amp;#39;text/css&amp;#39; media=&amp;#39;all&amp;#39; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;lt;/head&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;lt;body class=&amp;#34;wp-core-ui language-chooser&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;lt;p id=&amp;#34;logo&amp;#34;&amp;gt;WordPress&amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Awesome! 🎉&lt;/p&gt;
&lt;h1 id="managing-containers"&gt;Managing containers&lt;/h1&gt;
&lt;p&gt;Containers will automatically update on a schedule and you can check the timer:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;[core@vultr ~]$&lt;/span&gt; systemctl status --user podman-auto-update.timer
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;● podman-auto-update.timer - Podman auto-update timer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Loaded: loaded (/usr/lib/systemd/user/podman-auto-update.timer; enabled; preset: disabled)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Active: active (waiting) since Mon 2023-09-25 21:41:31 UTC; 3min 14s ago
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Trigger: Tue 2023-09-26 00:04:46 UTC; 2h 20min left
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Triggers: ● podman-auto-update.service
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Sep 25 21:41:31 vultr.guest systemd[1786]: Started podman-auto-update.timer - Podman auto-update timer.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Quadlets are just regular systemd units:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;[core@vultr ~]$&lt;/span&gt; systemctl list-units --user &lt;span class="p"&gt;|&lt;/span&gt; grep -i Quadlet
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; caddy.service loaded active running Caddy Quadlet
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; mariadb.service loaded active running MariaDB Quadlet
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; wordpress.service loaded active running Wordpress Quadlet
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As an example, you can make changes to caddy&amp;rsquo;s config file and restart it easily:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;[core@vultr ~]$&lt;/span&gt; systemctl restart --user caddy
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;[core@vultr ~]$&lt;/span&gt; systemctl status --user caddy
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;● caddy.service - Caddy Quadlet
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Loaded: loaded (/var/home/core/.config/containers/systemd/caddy.container; generated)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Drop-In: /usr/lib/systemd/user/service.d
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; └─10-timeout-abort.conf
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Active: active (running) since Mon 2023-09-25 21:46:28 UTC; 5s ago
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Main PID: 2652 (conmon)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Tasks: 18 (limit: 1023)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Memory: 15.1M
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; CPU: 207ms
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you need to change a quadlet&amp;rsquo;s configuration, just open up the configuration file in your favorite editor under &lt;code&gt;~/.config/containers/systemd&lt;/code&gt;, reload systemd, and restart the container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; vi ~/.config/containers/systemd/caddy.container
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;--- make your edits and save the quadlet configuration ---
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; systemctl daemon-reload --user
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; systemctl restart --user caddy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Enjoy!&lt;/p&gt;</description></item><item><title>Fixing a ghost database migration failure</title><link>https://major.io/p/ghost-db-migration-failure/</link><pubDate>Thu, 31 Aug 2023 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/ghost-db-migration-failure/</guid><description>&lt;p&gt;I love learning about the &lt;em&gt;behind the scenes&lt;/em&gt; aspects of just about everything.
I do &lt;a href="https://w5wut.com"&gt;ham radio&lt;/a&gt;, I self-host lots of my personal infrastructure, and I&amp;rsquo;ve been learning more about the math behind the stock market for the last year or two.&lt;/p&gt;
&lt;p&gt;That led me to start a blog on &lt;a href="https://unsplash.com/photos/l6mLi-iKUW0"&gt;Ghost&lt;/a&gt; to share my findings with others.
I started &lt;a href="https://thetanerd.com"&gt;Theta Nerd&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; earlier this summer.&lt;/p&gt;
&lt;p&gt;My deployment looked great when I started!
Everything was automatically updated with &lt;a href="https://major.io/p/watchtower/"&gt;watchtower&lt;/a&gt; and running with &lt;a href="https://major.io/p/docker-compose-on-coreos/"&gt;docker-compose on Fedora CoreOS&lt;/a&gt;. &lt;em&gt;(Click these links to read the posts on both topics!)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;However, I woke up one morning to my monitoring going off and my site was down. 😱&lt;/p&gt;
&lt;h1 id="why-is-the-site-down"&gt;Why is the site down?&lt;/h1&gt;
&lt;p&gt;Anyone who has worked in IT knows this sinking feeling.
Something is down, you don&amp;rsquo;t know why, and you suspect the worst possible scenarios.&lt;/p&gt;
&lt;p&gt;The instance hosting the blog was online and responsive, so I started digging into the logs with &lt;code&gt;docker-compose logs&lt;/code&gt;.
I suddenly found a wall of text in the logs for the Ghost container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2023-08-03 11:10:16] INFO Adding members.email_disabled column
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2023-08-03 11:10:16] INFO Setting email_disabled to true for all members that have their email on the suppression list
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2023-08-03 11:10:16] INFO Setting nullable: stripe_products.product_id
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2023-08-03 11:10:16] INFO Adding table: donation_payment_events
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2023-08-03 11:10:16] INFO Rolling back: alter table `donation_payment_events` add constraint `donation_payment_events_member_id_foreign` foreign key (`member_id`) references `members` (`id`) on delete SET NULL - Referencing column &amp;#39;member_id&amp;#39; and referenced column &amp;#39;id&amp;#39; in foreign key constraint &amp;#39;donation_payment_events_member_id_foreign&amp;#39; are incompatible..
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2023-08-03 11:10:16] INFO Dropping table: donation_payment_events
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2023-08-03 11:10:16] INFO Dropping nullable: stripe_products.product_id with foreign keys disabled
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2023-08-03 11:10:16] INFO Setting email_disabled to false for all members
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2023-08-03 11:10:16] INFO Removing members.email_disabled column
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2023-08-03 11:10:16] INFO Rollback was successful.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2023-08-03 11:10:16] ERROR alter table `donation_payment_events` add constraint `donation_payment_events_member_id_foreign` foreign key (`member_id`) references `members` (`id`) on delete SET NULL - Referencing column &amp;#39;member_id&amp;#39; and referenced column &amp;#39;id&amp;#39; in foreign key constraint &amp;#39;donation_payment_events_member_id_foreign&amp;#39; are incompatible.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;alter table `donation_payment_events` add constraint `donation_payment_events_member_id_foreign` foreign key (`member_id`) references `members` (`id`) on delete SET NULL - Referencing column &amp;#39;member_id&amp;#39; and referenced column &amp;#39;id&amp;#39; in foreign key constraint &amp;#39;donation_payment_events_member_id_foreign&amp;#39; are incompatible.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{&amp;#34;config&amp;#34;:{&amp;#34;transaction&amp;#34;:false},&amp;#34;name&amp;#34;:&amp;#34;2023-07-27-11-47-49-create-donation-events.js&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;Error occurred while executing the following migration: 2023-07-27-11-47-49-create-donation-events.js&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error ID:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 300
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error Code: 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ER_FK_INCOMPATIBLE_COLUMNS
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;----------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error: alter table `donation_payment_events` add constraint `donation_payment_events_member_id_foreign` foreign key (`member_id`) references `members` (`id`) on delete SET NULL - Referencing column &amp;#39;member_id&amp;#39; and referenced column &amp;#39;id&amp;#39; in foreign key constraint &amp;#39;donation_payment_events_member_id_foreign&amp;#39; are incompatible.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at /var/lib/ghost/versions/5.57.2/node_modules/knex-migrator/lib/index.js:1032:19
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at Packet.asError (/var/lib/ghost/versions/5.57.2/node_modules/mysql2/lib/packets/packet.js:728:17)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at Query.execute (/var/lib/ghost/versions/5.57.2/node_modules/mysql2/lib/commands/command.js:29:26)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at Connection.handlePacket (/var/lib/ghost/versions/5.57.2/node_modules/mysql2/lib/connection.js:478:34)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at PacketParser.onPacket (/var/lib/ghost/versions/5.57.2/node_modules/mysql2/lib/connection.js:97:12)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at PacketParser.executeStart (/var/lib/ghost/versions/5.57.2/node_modules/mysql2/lib/packet_parser.js:75:16)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at Socket.&amp;lt;anonymous&amp;gt; (/var/lib/ghost/versions/5.57.2/node_modules/mysql2/lib/connection.js:104:25)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at Socket.emit (node:events:513:28)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at addChunk (node:internal/streams/readable:315:12)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at readableAddChunk (node:internal/streams/readable:289:9)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at Socket.Readable.push (node:internal/streams/readable:228:10)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at TCP.onStreamRead (node:internal/stream_base_commons:190:23)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ah, so a failed database migration in the upgrade to 5.57.2 is the culprit! 👏&lt;/p&gt;
&lt;p&gt;I brought the site back online quickly by changing the container version for Ghost back to the previous version (5.55.2).&lt;/p&gt;
&lt;h1 id="why-did-the-database-migration-fail"&gt;Why did the database migration fail?&lt;/h1&gt;
&lt;p&gt;The error message from above boils down to this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error: alter table `donation_payment_events` add constraint 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;`donation_payment_events_member_id_foreign` foreign key (`member_id`)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;references `members` (`id`) on delete SET NULL - Referencing column
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#39;member_id&amp;#39; and referenced column &amp;#39;id&amp;#39; in foreign key constraint 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#39;donation_payment_events_member_id_foreign&amp;#39; are incompatible.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Adjusting the &lt;code&gt;donation_payment_events.member_id&lt;/code&gt; column to be a foreign key of &lt;code&gt;members.id&lt;/code&gt; is failing because they are incompatible types.
However, as I examined both tables, both were regular &lt;code&gt;varchar(24)&lt;/code&gt; columns without anything special attached to them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mysql&amp;gt; describe members;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+------------------------------+---------------+------+-----+---------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| Field | Type | Null | Key | Default | Extra |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+------------------------------+---------------+------+-----+---------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| id | varchar(24) | NO | PRI | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| uuid | varchar(36) | YES | UNI | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| email | varchar(191) | NO | UNI | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| status | varchar(50) | NO | | free | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| name | varchar(191) | YES | | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| expertise | varchar(191) | YES | | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| note | varchar(2000) | YES | | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| geolocation | varchar(2000) | YES | | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| enable_comment_notifications | tinyint(1) | NO | | 1 | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| email_count | int unsigned | NO | | 0 | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| email_opened_count | int unsigned | NO | | 0 | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| email_open_rate | int unsigned | YES | MUL | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| last_seen_at | datetime | YES | | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| last_commented_at | datetime | YES | | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| created_at | datetime | NO | | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| created_by | varchar(24) | NO | | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| updated_at | datetime | YES | | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| updated_by | varchar(24) | YES | | NULL | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+------------------------------+---------------+------+-----+---------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;18 rows in set (0.00 sec)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="going-upstream"&gt;Going upstream&lt;/h1&gt;
&lt;p&gt;I went to Ghost&amp;rsquo;s GitHub repository and &lt;a href="https://github.com/TryGhost/Ghost/issues/17584"&gt;opened an issue&lt;/a&gt; with as much data as I can find.&lt;/p&gt;
&lt;p&gt;One of the &lt;a href="https://github.com/TryGhost/Ghost/issues/17584#issuecomment-1671134556"&gt;first replies&lt;/a&gt; mentioned something about database collations.
Long story short, collations describe how databases handle sorting and comparing data for different languages.
Comparing some languages to other languages can be particularly challenging and this can lead to problems.&lt;/p&gt;
&lt;p&gt;I made a switch from MariaDB to MySQL recently for the blog.
Could that be related?&lt;/p&gt;
&lt;h1 id="more-searching"&gt;More searching&lt;/h1&gt;
&lt;p&gt;I figured that I wasn&amp;rsquo;t the first one to stumble into this problem, and sure enough &amp;ndash; I wasn&amp;rsquo;t!
There&amp;rsquo;s a &lt;a href="https://dnsmichi.at/2022/06/01/ghost-v5-upgrade-with-mysql-8-collation-migration-in-docker-compose/"&gt;great blog post&lt;/a&gt; about a broken migration from MySQL 5 to 8 with Ghost.&lt;/p&gt;
&lt;p&gt;In short, it required several steps to fix it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Stop the Ghost container&lt;/li&gt;
&lt;li&gt;Back up the database first (always a good idea)&lt;/li&gt;
&lt;li&gt;Do a quick find/replace on the dumped database to change the collations&lt;/li&gt;
&lt;li&gt;Drop the &lt;code&gt;ghost&lt;/code&gt; database from the database 😱&lt;/li&gt;
&lt;li&gt;Import the database back into MySQL&lt;/li&gt;
&lt;li&gt;Start Ghost again&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Dropping databases always makes me pause, but that&amp;rsquo;s what backups are for! 😉&lt;/p&gt;
&lt;h1 id="how-i-fixed-it"&gt;How I fixed it&lt;/h1&gt;
&lt;p&gt;In my case, my MySQL container is called &lt;code&gt;ghostmysql&lt;/code&gt; and my Ghost database is &lt;code&gt;ghostdb&lt;/code&gt;.
Then I made a backup of the database using &lt;code&gt;mysqldump&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;sudo docker-compose exec ghostmysql mysqldump \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; -u root -psuper-secret-password ghostdb &amp;gt; backup-ghost-db.sql
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, I copied the SQL file to another directory &lt;em&gt;just in case&lt;/em&gt; I accidentally deleted this backup with an errant command.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;cp backup-ghost-db.sql ../
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then I made a copy of the SQL file in the current directory and ran the find and replace on that copy.
This changes the collations from the wrong one, &lt;code&gt;utf8mb4_general_ci&lt;/code&gt;, to the right one, &lt;code&gt;utf8mb4_0900_ai_ci&lt;/code&gt;&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;cp backup-ghost-db.sql backup-ghost-db-new.sql
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;sed -i &amp;#39;s/utf8mb4_general_ci/utf8mb4_0900_ai_ci/g&amp;#39; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; backup-ghost-db-new.sql
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now I have the collations right for importing the database back into MySQL.
But first, I have to drop the existing database.
&lt;strong&gt;This is a good time to double check your backups!&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;sudo docker-compose exec ghostmysql mysql -u root \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; -psuper-secret-password
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;mysql&amp;gt; DROP DATABASE ghostdb;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can import the modified backup:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;cat backup-ghost-db-new.sql | sudo docker-compose exec -T \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; ghostmysql mysql -u root -psuper-secret-password ghostdb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Start all the containers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;sudo docker-compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ghost was back online with the older version and everything looked good!
I updated my &lt;code&gt;docker-compose.yaml&lt;/code&gt; back to use &lt;code&gt;latest&lt;/code&gt; for the Ghost version and ran &lt;code&gt;sudo docker-compose up -d&lt;/code&gt; once more.&lt;/p&gt;
&lt;p&gt;Within seconds, the new container image was in place and the container was running!
Both migrations completed in seconds and the blog was back online with the newest version. 🎉&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Theta is one of many &lt;a href="https://en.wikipedia.org/wiki/Greeks_(finance)"&gt;financial Greeks&lt;/a&gt; that measure certain aspects of options contracts in the market.
It&amp;rsquo;s also a &lt;a href="https://en.wikipedia.org/wiki/Theta"&gt;letter in the Greek alphabet&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;The default collation in MySQL 8 is &lt;code&gt;utf8mb4_0900_ai_ci&lt;/code&gt;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Launch a watchtower container via podman quadlets</title><link>https://major.io/p/podman-quadlet-watchtower/</link><pubDate>Wed, 31 May 2023 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/podman-quadlet-watchtower/</guid><description>&lt;p&gt;Most of my container workloads run on independent CoreOS cloud instances that I &lt;a href="https://major.io/p/coreos-as-pet/"&gt;treat like pets&lt;/a&gt;.
Keeping containers update remains a constant battle, but it&amp;rsquo;s still easier than running kubernetes.&lt;/p&gt;
&lt;p&gt;I wrote about &lt;a href="https://major.io/p/watchtower/"&gt;using watchtower&lt;/a&gt; in the past to keep containers updated.
It&amp;rsquo;s a simple container that does a few important things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It monitors (via docker/podman socket) the running containers on the host&lt;/li&gt;
&lt;li&gt;It tracks the versions/tags of each container image&lt;/li&gt;
&lt;li&gt;It looks for updated versions of the container image in their upstream repositories&lt;/li&gt;
&lt;li&gt;Based on a configurable schedule, it pulls a new container image and restarts the container for updates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I encourage you to &lt;a href="https://github.com/containrrr/watchtower"&gt;read more about watchtower on GitHub&lt;/a&gt;.
There&amp;rsquo;s plenty you can configure, including update intervals, how updates are handled, and how you can get notifications when an update happens.&lt;/p&gt;
&lt;p&gt;My new deployments always need watchtower running.
Luckily, we can combine Fedora CoreOS&amp;rsquo; initial provisioning system, called &lt;a href="https://coreos.github.io/ignition/"&gt;ignition&lt;/a&gt;, with podman&amp;rsquo;s new &lt;a href="https://www.redhat.com/sysadmin/quadlet-podman"&gt;quadlet&lt;/a&gt; feature and launch watchtower automatically on the first boot.&lt;/p&gt;
&lt;h1 id="quadlets"&gt;Quadlets&lt;/h1&gt;
&lt;p&gt;So what&amp;rsquo;s a quadlet?&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://www.redhat.com/sysadmin/quadlet-podman"&gt;blog post&lt;/a&gt; explains it well by making containers more declarative via a familiar systemd syntax.
Here&amp;rsquo;s an example &lt;code&gt;.container&lt;/code&gt; file from the post:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;The sleep container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;local-fs.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Container]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;registry.access.redhat.com/ubi9-minimal:latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Exec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sleep 1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Start by default on boot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target default.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can toss this into &lt;code&gt;$HOME/.config/containers/systemd/mysleep.container&lt;/code&gt; for rootless user containers or in &lt;code&gt;/etc/containers/systemd/mysleep.container&lt;/code&gt; for a container running as root.&lt;/p&gt;
&lt;h1 id="configure-a-quadlet-on-boot"&gt;Configure a quadlet on boot&lt;/h1&gt;
&lt;p&gt;As I mentioned earlier, I want a watchtower container running on my CoreOS nodes at first boot.
Let&amp;rsquo;s start with a fairly basic &lt;a href="https://coreos.github.io/butane/"&gt;butane&lt;/a&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;fcos&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1.4.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;major&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;wheel&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ssh_authorized_keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDyoH6gU4lgEiSiwihyD0Rxk/o5xYIfA3stVDgOGM9N0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/etc/containers/systemd/watchtower.container&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; [Unit]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Description=Watchtower container updater
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Wants=network-online.target
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; After=network-online.target
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; [Container]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; ContainerName=watchtower
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Image=ghcr.io/containrrr/watchtower:1.5.3@sha256:a924a9aaef50016b7e69c7f618c7eb81ba02f06711558af57da0f494a76e7aca
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Environment=WATCHTOWER_CLEANUP=true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Environment=WATCHTOWER_POLL_INTERVAL=3600
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Volume=/var/run/docker.sock:/var/run/docker.sock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; SecurityLabelDisable=true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; [Install]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; WantedBy=multi-user.target default.target&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s break this file down:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I start by adding a user named &lt;code&gt;major&lt;/code&gt; that has administrative privileges an an ssh key
&lt;em&gt;(this is optional, but I like using my own username rather than &lt;code&gt;core&lt;/code&gt;)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;The quadlet unit file lands in &lt;code&gt;/etc/containers/systemd/watchtower.container&lt;/code&gt; and starts at boot time&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The quadlet file has some important configurations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I added environment variables to clean up outdated container images and check for updates once an hour&lt;/li&gt;
&lt;li&gt;The podman socket is mounted inside the watchtower container&lt;/li&gt;
&lt;li&gt;Security labels are disabled to allow for communication with the podman socket&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
&lt;strong&gt;Mounting the podman socket and disabling security labels is not an ideal security approach.&lt;/strong&gt;
However, I&amp;rsquo;ve found that watchtower&amp;rsquo;s configuration and automation fits my needs really well and I retreive the image from a trusted source.
If this won&amp;rsquo;t work for you, you can use &lt;a href="https://docs.podman.io/en/latest/markdown/podman-auto-update.1.html"&gt;podman&amp;rsquo;s built-in auto-update&lt;/a&gt; feature instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;From here, we convert the butane configuration into an ignition configuration.
I&amp;rsquo;m launching this CoreOS node on &lt;a href="https://www.vultr.com/?ref=6941438"&gt;VULTR&lt;/a&gt;, so I&amp;rsquo;ve named my files accordingly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; butane vultr-coreos.butane &amp;gt; vultr-coreos.ign
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="lets-go-"&gt;Let&amp;rsquo;s go 🚀&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;m using VULTR&amp;rsquo;s CLI here in Fedora, but you can do the same steps via VULTR&amp;rsquo;s portal if needed.
Just paste in the ignition configuration into the large text box before launch.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install vultr-cli in Fedora&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install vultr-cli
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Launch the instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vultr-cli instance create --region dfw --plan vhp-2c-2gb-amd &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --os &lt;span class="m"&gt;391&lt;/span&gt; --label coreos-dfw-1 --host coreos-dfw-1 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --userdata &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat vultr-coreos.ign&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s see how the container is doing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; ssh major@COREOS_HOST
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Fedora CoreOS 38.20230430.3.1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Tracker: https://github.com/coreos/fedora-coreos-tracker
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Discuss: https://discussion.fedoraproject.org/tag/coreos
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;[major@coreos-dfw-1 ~]$&lt;/span&gt; sudo podman ps
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;a0024712c95d ghcr.io/containrrr/watchtower@sha256:a924a9aaef50016b7e69c7f618c7eb81ba02f06711558af57da0f494a76e7aca About a minute ago Up About a minute watchtower
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;[major@coreos-dfw-1 ~]$&lt;/span&gt; sudo podman logs watchtower
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;time=&amp;#34;2023-05-31T14:01:12Z&amp;#34; level=info msg=&amp;#34;Watchtower 1.5.3&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;time=&amp;#34;2023-05-31T14:01:12Z&amp;#34; level=info msg=&amp;#34;Using no notifications&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;time=&amp;#34;2023-05-31T14:01:12Z&amp;#34; level=info msg=&amp;#34;Checking all containers (except explicitly disabled with label)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;time=&amp;#34;2023-05-31T14:01:12Z&amp;#34; level=info msg=&amp;#34;Scheduling first run: 2023-05-31 15:01:12 +0000 UTC&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;time=&amp;#34;2023-05-31T14:01:12Z&amp;#34; level=info msg=&amp;#34;Note that the first check will be performed in 59 minutes, 59 seconds&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Awesome!&lt;/strong&gt; 🥳&lt;/p&gt;
&lt;p&gt;My system rebooted for an ostree update shortly after provisioning and the container came up automatically both times.&lt;/p&gt;</description></item><item><title>CoreOS as a pet</title><link>https://major.io/p/coreos-as-pet/</link><pubDate>Thu, 25 May 2023 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/coreos-as-pet/</guid><description>&lt;p&gt;Anyone working with containers has likely heard of &lt;a href="https://fedoraproject.org/coreos/"&gt;CoreOS&lt;/a&gt; by this point
Haven&amp;rsquo;t heard about it?
Don&amp;rsquo;t despair.
I&amp;rsquo;ll catch you up on what you missed.&lt;/p&gt;
&lt;p&gt;Fedora CoreOS offers a really fast pathway to running containers on hardware, in virtual machines, or in clouds.
It delivers a lightweight operating system with all of the container technology that you need for running simple containers or launching a kubernetes deployment.&lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s not the best part.&lt;/p&gt;
&lt;p&gt;CoreOS really shines due to its &lt;strong&gt;immutable OS layer&lt;/strong&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.
The OS underneath your containers ships as a single unit and it automatically updates itself much like your mobile phone.
An update rolls down, CoreOS sets it up as a secondary OS, reboots into that new update, and rolls back to the original one if there were any issues.&lt;/p&gt;
&lt;p&gt;Many people use CoreOS as the workhorse underneath kubernetes.
Red Hat uses it underneath &lt;a href="https://docs.openshift.com/container-platform/4.13/installing/installing_bare_metal/installing-bare-metal.html#creating-machines-bare-metal_installing-bare-metal"&gt;OpenShift&lt;/a&gt; as well.
It&amp;rsquo;s even supported by the super light weight kubernetes distribution &lt;a href="https://k3s.io/"&gt;k3s&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But can you use CoreOS as a &lt;em&gt;pet&lt;/em&gt; type instance that you use and maintain for long periods of time just like any other server?
&lt;strong&gt;Absolutely!&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id="whats-this-pet-stuff-about"&gt;What&amp;rsquo;s this &lt;em&gt;pet&lt;/em&gt; stuff about?&lt;/h1&gt;
&lt;p&gt;Whether you like it or not, there&amp;rsquo;s a cattle versus pets paradigm that took hold in the world of IT at some point.
The basic ideas are these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you take care of cattle, you take care of them as a group.
Losing one or more of them would make you sad, but you know you have many others.&lt;/li&gt;
&lt;li&gt;As for pets, you spend a lot of time taking care of them and playing with them.
If you lost one, it would be devastating.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A fleet of web servers could be treated like cattle.
Keep lots of them online and replace any instances that have issues.&lt;/p&gt;
&lt;p&gt;On the other hand, databases or tier zero systems (everyone feels if it they went down) are like pets.
You carefully build, maintain, and monitor these.&lt;/p&gt;
&lt;h1 id="how-does-coreos-fit-in"&gt;How does CoreOS fit in?&lt;/h1&gt;
&lt;p&gt;Many people do use CoreOS as a container hosting platform as part of a bigger system.
It works really well for that.
But it&amp;rsquo;s great as a regular cloud server, too.&lt;/p&gt;
&lt;p&gt;You can run a single node CoreOS deployment and manage containers via the tools that you know and love.
For example, &lt;a href="https://github.com/docker/compose"&gt;docker-compose&lt;/a&gt; works great on CoreOS.
I even used it to host my own &lt;a href="https://major.io/p/self-hosted-mastodon-second-try/"&gt;Mastodon deployment&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also load up more user-friendly tools such as &lt;a href="https://www.portainer.io/"&gt;portainer&lt;/a&gt; to manage containers in a browser.&lt;/p&gt;
&lt;h1 id="my-development-tools-are-missing"&gt;My development tools are missing!&lt;/h1&gt;
&lt;p&gt;😱 No &lt;code&gt;vim&lt;/code&gt;?
This is too minimal!
&lt;strong&gt;What are we going to do?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Luckily CoreOS comes with &lt;a href="https://github.com/containers/toolbox"&gt;toolbox&lt;/a&gt;. 🧰&lt;/p&gt;
&lt;p&gt;Toolbox gives you the ability to run a utility container on the system with some handy benefits:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Toolbox environments have seamless access to the user&amp;rsquo;s home directory, the Wayland and X11 sockets, networking (including Avahi), removable devices (like USB sticks), systemd journal, SSH agent, D-Bus, ulimits, /dev and the udev database, etc..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This means that the toolbox feels like a second OS on the system and it has all of the elevated privileges that you need to do your work.
Simply run &lt;code&gt;toolbox enter&lt;/code&gt;, follow the prompts, and you&amp;rsquo;ll end up with a Fedora toolbox that matches your CoreOS version.
Need a different version, such as Fedora Rawhide?
Just specify the Fedora release you want on the prompt:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; toolbox enter --release &lt;span class="m"&gt;39&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;No toolbox containers found. Create now? [y/N] y
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Image required to create toolbox container.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Download registry.fedoraproject.org/fedora-toolbox:39 (500MB)? [y/N]: y
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Welcome to the Toolbox; a container where you can install and run
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;all your tools.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; - Use DNF in the usual manner to install command line tools.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; - To create a new tools container, run &amp;#39;toolbox create&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;For more information, see the documentation.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;⬢[major@toolbox ~]$ 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Look at the &lt;code&gt;toolbox create --help&lt;/code&gt; output to see how to create lots of different toolbox containers with different names and releases.
If you go overboard and need to delete some toolboxes, just list your toolboxes with &lt;code&gt;toolbox list&lt;/code&gt; and follow it up with &lt;code&gt;toolbox rm&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id="my-tool-wont-work-in-the-toolbox"&gt;My tool won&amp;rsquo;t work in the toolbox.&lt;/h1&gt;
&lt;p&gt;Some applications have issues running inside a container, even one that has elevated privileges on the system.
CoreOS offers an option for layering packages on top of the underlying immutable OS.&lt;/p&gt;
&lt;p&gt;Simply run &lt;code&gt;rpm-ostree install PACKAGE&lt;/code&gt; to layer a package on top of the OS.
When &lt;code&gt;rpm-ostree&lt;/code&gt; runs, it creates a new layer and sets that layer to be active on the next boot.
That means that you need to reboot before you can use the package.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t want to reboot?
There&amp;rsquo;s another option, but I recommend against it if you can avoid it&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;You can apply a package layer &lt;em&gt;live&lt;/em&gt; on the system without a reboot with the &lt;code&gt;--apply-live&lt;/code&gt; flag.
Installing a package like &lt;a href="https://github.com/traviscross/mtr"&gt;mtr&lt;/a&gt; would look like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ sudo rpm-ostree install --apply-live mtr
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As soon as &lt;code&gt;rpm-ostree&lt;/code&gt; finishes its work, &lt;code&gt;mtr&lt;/code&gt; should be available on the system for you to use.&lt;/p&gt;
&lt;h1 id="how-do-updates-work"&gt;How do updates work?&lt;/h1&gt;
&lt;p&gt;There are two main technologies at work here.&lt;/p&gt;
&lt;p&gt;First, &lt;a href="https://github.com/coreos/zincati"&gt;zincati&lt;/a&gt; checks for updates to your immutable OS tree.
It runs on a &lt;a href="https://docs.fedoraproject.org/en-US/fedora-coreos/auto-updates/#_os_update_finalization"&gt;configurable schedule&lt;/a&gt; that you can adjust based on your preferences.&lt;/p&gt;
&lt;p&gt;Second, &lt;code&gt;rpm-ostree&lt;/code&gt; handles the OS layers and switches between them at boot time.
If you&amp;rsquo;re running off layer A and an update comes down (layer B), that layer is written to the disk and activated on the next boot.
Should there be any issues booting up layer B later, &lt;code&gt;rpm-ostree&lt;/code&gt; switches the system back to layer A.
In these situations, your downtime might be extended a bit due to two reboots.
Your system will come back up with the original OS layer activated.&lt;/p&gt;
&lt;p&gt;You also get a choice of &lt;a href="https://docs.fedoraproject.org/en-US/fedora-coreos/update-streams/"&gt;update streams&lt;/a&gt;.
Want to live a bit more on the edge?
Go for &lt;em&gt;next&lt;/em&gt; or &lt;em&gt;testing&lt;/em&gt;.
You&amp;rsquo;re on the &lt;em&gt;stable&lt;/em&gt; stream by default.&lt;/p&gt;
&lt;p&gt;Although I haven&amp;rsquo;t landed in this situation, it&amp;rsquo;s possible that the system boots into a new update where you notice a problem that doesn&amp;rsquo;t affect the boot.
You can &lt;a href="https://docs.fedoraproject.org/en-US/fedora-coreos/auto-updates/#_manual_rollbacks"&gt;manually roll back&lt;/a&gt; to fix it.&lt;/p&gt;
&lt;h1 id="i-have-more-questions"&gt;I have more questions.&lt;/h1&gt;
&lt;p&gt;Your first stop should be the &lt;a href="https://docs.fedoraproject.org/en-US/fedora-coreos/"&gt;Fedora CoreOS docs&lt;/a&gt;.
There are also lots of ways to &lt;a href="https://docs.fedoraproject.org/en-US/fedora-coreos/getting-started/#_getting_in_touch"&gt;contact the development team and talk with the community&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Love the idea of an immutable OS but you wish you had it for your desktop or laptop?
Go check out &lt;a href="https://fedoraproject.org/silverblue/"&gt;Fedora Silverblue&lt;/a&gt;. 💻&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Okay, so it&amp;rsquo;s &lt;em&gt;mostly&lt;/em&gt; immutable.
You can edit configuration in &lt;code&gt;/etc&lt;/code&gt; and you can layer more packages on top of the base OS layer if you need them.
However, CoreOS maintainers discourage adding layered packages if you can avoid it.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;When you apply some packages and make them available immediately, you may lose track of which ones were applied live and which ones are available on the next reboot.
Things can get a bit confusing if you suddenly change your mind about applying a package live or not.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Run Xorg applications with podman</title><link>https://major.io/p/run-xorg-applications-with-podman/</link><pubDate>Sun, 17 Oct 2021 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/run-xorg-applications-with-podman/</guid><description>&lt;p&gt;Containers are a great way to deliver and run all kinds of applications.
Although many people build containers for server applications, you can also use
them for client applications on your local workstation. This helps when you want
to test new applications without disrupting your existing system or when you use
an immutable system such as &lt;a href="https://silverblue.fedoraproject.org/"&gt;Fedora Silverblue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Podman takes this further by allowing you to run a client application without
root access or daemons. This post covers how to build a container with an Xorg
application and run it on a Fedora system.&lt;/p&gt;
&lt;h2 id="building-the-container"&gt;Building the container&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start with a simple container that contains &lt;code&gt;xeyes&lt;/code&gt;. This simple
application simply puts a pair of eyes on your screen that follow your mouse
movements around the desktop. It has very few dependencies and it&amp;rsquo;s a great way
to test several capabilities on the desktop.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a very simple container build file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-dockerfile" data-lang="dockerfile"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# xeyes-container&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;registry.fedoraproject.org/fedora:latest&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;RUN&lt;/span&gt; dnf -y install xeyes&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CMD&lt;/span&gt; xeyes&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s install podman and build the container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo dnf -y install podman
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; podman build -t xeyes -f xeyes-container .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="run-the-container"&gt;Run the container&lt;/h2&gt;
&lt;p&gt;Now that we have our &lt;code&gt;xeyes&lt;/code&gt; container, let&amp;rsquo;s run it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; podman run --rm xeyes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Error: Can&amp;#39;t open display:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;re missing the &lt;code&gt;DISPLAY&lt;/code&gt; variable inside the container. Let&amp;rsquo;s add it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$DISPLAY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;:0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; podman run --rm -e DISPLAY xeyes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Error: Can&amp;#39;t open display: :0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Well, we have the display variable inside now, but there&amp;rsquo;s another problem.
Inside the container, &lt;code&gt;xeyes&lt;/code&gt; can&amp;rsquo;t make a connection to our X daemon. This
socket normally appears in &lt;code&gt;/tmp/.X11-unix&lt;/code&gt;, but the container doesn&amp;rsquo;t have it.
Let&amp;rsquo;s try adding this inside the container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; podman run --rm -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix xeyes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Error: Can&amp;#39;t open display: :0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Darn! This should be working. Let&amp;rsquo;s check the system journal:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AVC avc: denied { write } for pid=10817 comm=&amp;#34;xeyes&amp;#34; name=&amp;#34;X0&amp;#34; dev=&amp;#34;tmpfs&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ino=42 scontext=system_u:system_r:container_t:s0:c143,c574
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tcontext=system_u:object_r:user_tmp_t:s0 tclass=sock_file permissive=0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Uh oh. SELinux is upset that a container is trying to mess with the &lt;code&gt;X0&lt;/code&gt; socket
for our Xorg server that sits in &lt;code&gt;/tmp/.X11-unix&lt;/code&gt;. You may be tempted to run
&lt;code&gt;setenforce 0&lt;/code&gt;, but wait. We can fix this with podman!&lt;/p&gt;
&lt;p&gt;Podman allows you to set security options for a particular container with
&lt;code&gt;--security-opt&lt;/code&gt;. We need to run this container with an SELinux context that
allows it to talk to something in &lt;code&gt;tmpfs&lt;/code&gt;. Examining the &lt;a href="https://github.com/containers/container-selinux/blob/main/container.te#L77"&gt;container-selinux
project&lt;/a&gt; shows that &lt;code&gt;container_runtime_t&lt;/code&gt; can work with &lt;code&gt;tmpfs&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;type container_runtime_tmp_t alias docker_tmp_t;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;files_tmp_file(container_runtime_tmp_t)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s try adding this to our podman command now:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; podman run --rm -e DISPLAY &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; -v /tmp/.X11-unix:/tmp/.X11-unix \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; --security-opt label=type:container_runtime_t xeyes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I now have a set of eyeballs on my desktop! 👀&lt;/p&gt;
&lt;p&gt;&lt;img alt="2021-10-17-xeyes.png" loading="lazy" src="https://major.io/p/run-xorg-applications-with-podman/2021-10-17-xeyes.png" title="xeyes running on my desktop"&gt;&lt;/p&gt;
&lt;h2 id="extra-credit"&gt;Extra credit&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;xeyes&lt;/code&gt; application is extremely simple, but you can run much more complex
applications using this same method. Keep in mind that certain applications
might require extra packages inside the container, such as fonts or GTK themes.
Jessie Frazelle has a &lt;a href="https://github.com/jessfraz/dockerfiles"&gt;great repository&lt;/a&gt; full of containers that she uses
regularly and this might give you inspiration to create some of your own! 🤓&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Photo credit: &lt;a href="https://unsplash.com/photos/gBr5Hmx1STc"&gt;Jonny Gios on Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Rootless container management with docker-compose and podman</title><link>https://major.io/p/rootless-container-management-with-docker-compose-and-podman/</link><pubDate>Fri, 09 Jul 2021 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/rootless-container-management-with-docker-compose-and-podman/</guid><description>&lt;p&gt;Everyone has an opinion for the best way to manage containers, and there are
many contenders depending on how much complexity you can handle and how much
automation you require. One of my favorite ways to manage containers is
&lt;a href="https://docs.docker.com/compose/"&gt;docker-compose&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="overview-of-docker-compose"&gt;Overview of docker-compose&lt;/h2&gt;
&lt;p&gt;docker-compose uses a simple YAML syntax to explain what your desired end state
should look like. The &lt;a href="https://github.com/compose-spec/compose-spec/blob/master/spec.md"&gt;compose specification&lt;/a&gt; covers all of the relevant
configurations for containers, volumes, networks, and more. After each change,
docker-compose compares your configuration to the running containers and makes
all of the required changes.&lt;/p&gt;
&lt;p&gt;This provides some advantages over using &lt;code&gt;docker run ...&lt;/code&gt; or &lt;code&gt;podman run ...&lt;/code&gt;
since you can put the YAML into version control and track your configuration
changes all in one place. I was tracking the configuration in shell scripts that
ran &lt;code&gt;docker&lt;/code&gt; with lots of parameters and that became difficult to manage.&lt;/p&gt;
&lt;h2 id="what-about-podman"&gt;What about Podman?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://podman.io/"&gt;Podman&lt;/a&gt; is a tool for managing containers, much like Docker, but it has some
distinct advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No daemons are needed&lt;/li&gt;
&lt;li&gt;You can run containers as your user, or as root&lt;/li&gt;
&lt;li&gt;The commands and arguments are &lt;em&gt;nearly&lt;/em&gt; identical to &lt;code&gt;docker&lt;/code&gt; &lt;em&gt;(no swarm
support)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Podman 3 added a &lt;a href="https://docs.podman.io/en/latest/_static/api.html"&gt;complete Docker-compatible API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This last part, the Docker-compatible API is quite interesting and this allows
docker-compose to work with podman as well as it does with docker.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s try it out!&lt;/p&gt;
&lt;h2 id="getting-everything-ready"&gt;Getting everything ready&lt;/h2&gt;
&lt;p&gt;Start with a working Fedora 34 system and install some packages:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💣 &lt;strong&gt;HEADS UP:&lt;/strong&gt; The &lt;code&gt;podman-docker&lt;/code&gt; package brings in podman, an alias for the
&lt;code&gt;docker&lt;/code&gt; command that actually runs &lt;code&gt;podman&lt;/code&gt;, and the docker-compatible API via
a socket. If you want to run podman and docker side by side on the same machine,
install &lt;code&gt;podman&lt;/code&gt; instead of &lt;code&gt;podman-docker&lt;/code&gt; here. If you had docker installed
already, you may need to remove it with &lt;code&gt;dnf remove docker-ce docker-ce-cli&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;dnf install docker-compose podman-docker
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;re going to do something different here. Intead of starting the podman socket
or docker daemon as root, we&amp;rsquo;re going to start the podman socket as a regular
user. Switch to a regular user and start the socket:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; --now --user podman.socket
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Created symlink /home/major/.config/systemd/user/sockets.target.wants/podman.socket → /usr/lib/systemd/user/podman.socket.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But wait, where&amp;rsquo;s the socket?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; ls -al &lt;span class="nv"&gt;$XDG_RUNTIME_DIR&lt;/span&gt;/podman/podman.sock
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;srw-rw----. 1 major major 0 Jul 9 16:49 /run/user/1000/podman/podman.sock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s a podman socket running as my user and exposing a docker-compatible API.
🎉&lt;/p&gt;
&lt;h2 id="time-for-docker-compose"&gt;Time for docker-compose&lt;/h2&gt;
&lt;p&gt;Now it&amp;rsquo;s time to use docker-compose with podman as a regular user and run a
container as our regular user.&lt;/p&gt;
&lt;p&gt;We can use &lt;a href="https://github.com/librespeed/speedtest"&gt;librespeed&lt;/a&gt; for this example, and the &lt;a href="https://docs.linuxserver.io/images/docker-librespeed"&gt;LinuxServer librespeed
container&lt;/a&gt; is a great way to deploy it. It&amp;rsquo;s a self-hosted speed test
application that works well with desktops and mobile devices.&lt;/p&gt;
&lt;p&gt;First, we begin with the suggested docker-compose configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2.1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;librespeed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ghcr.io/linuxserver/librespeed&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;container_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;librespeed&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;PUID=1000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;PGID=1000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;TZ=Etc/UTC&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;librespeed:/config&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;librespeed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Save that as &lt;code&gt;docker-compose.yml&lt;/code&gt; in your current directory.&lt;/p&gt;
&lt;p&gt;Keep in mind that docker-compose is expecting to find our docker socket in
&lt;code&gt;/var/run/docker.sock&lt;/code&gt;, but we&amp;rsquo;re running the podman socket as our regular user.
Let&amp;rsquo;s export the &lt;code&gt;DOCKER_HOST&lt;/code&gt; variable and run docker-compose to bring up our
new container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;DOCKER_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;unix:&lt;/span&gt;&lt;span class="nv"&gt;$XDG_RUNTIME_DIR&lt;/span&gt;&lt;span class="s2"&gt;/podman/podman.sock&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; docker-compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Pulling librespeed (ghcr.io/linuxserver/librespeed:)...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;10f45b17b9ab: Download complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;f23b92877416: Download complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;a5bf9c523af4: Download complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;00fe9b963179: Download complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;bfafa0ba1dc9: Download complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;c583b34264f1: Download complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;9d26cce56b8d: Download complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;70de87880afd: Download complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;0ad6c2578069: Download complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;a8792749de3b: Download complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;2d31530d2d8b: Download complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Creating librespeed ... done
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; docker-compose ps
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Name Command State Ports
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;--------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;librespeed /init Up () :8080-&amp;gt;80/tcp
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The container is up and running as our user. Let&amp;rsquo;s check the &lt;code&gt;nginx&lt;/code&gt; process
inside the container to be sure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; ps -xu &lt;span class="p"&gt;|&lt;/span&gt;grep &lt;span class="s2"&gt;&amp;#34;nginx: master&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;major 3805 0.0 0.4 5860 4692 ? Ss 16:53 0:00 nginx: master process /usr/sbin/nginx -c /config/nginx/nginx.conf
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sweet! 🥳&lt;/p&gt;
&lt;h2 id="time-for-a-speed-test"&gt;Time for a speed test&lt;/h2&gt;
&lt;p&gt;If we&amp;rsquo;ve come this far, we might as well test our internet speed to ensure the
container works!&lt;/p&gt;
&lt;p&gt;&lt;img alt="2021-07-09-speedtest-prior.png" loading="lazy" src="https://major.io/p/rootless-container-management-with-docker-compose-and-podman/2021-07-09-speedtest-prior.png" title="Librespeed speed test interface before testing"&gt;&lt;/p&gt;
&lt;p&gt;Remember that we used port 8080 as a replacement for 80 in our docker-compose
file to avoid issues with regular users being denied access to create a listener
on ports under 1024.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see how fast my connection is today:&lt;/p&gt;
&lt;p&gt;&lt;img alt="2021-07-09-speedtest-prior.png" loading="lazy" src="https://major.io/p/rootless-container-management-with-docker-compose-and-podman/2021-07-09-speedtest-prior.png" title="Librespeed speed test interface after testing"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Photo credit: &lt;a href="https://unsplash.com/photos/gXN8cfWlYCo"&gt;Michael D Beckwith on Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Get faster GitLab runners with a ramdisk</title><link>https://major.io/p/get-faster-gitlab-runners-with-a-ramdisk/</link><pubDate>Fri, 16 Aug 2019 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/get-faster-gitlab-runners-with-a-ramdisk/</guid><description>&lt;p&gt;When you build tons of kernels every day like my team does, you look for
speed improvements anywhere you can. Caching repositories, artifacts, and
compiled objects makes kernel builds faster and it reduces infrastructure
costs.&lt;/p&gt;
&lt;h2 id="need-for-speed"&gt;Need for speed&lt;/h2&gt;
&lt;p&gt;We use GitLab CI in plenty of places, and that means we have a lot of
gitlab-runner configurations for OpenShift (using the kubernetes executor)
and AWS (using the docker-machine executor). The runner&amp;rsquo;s built-in &lt;a href="https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnerscache-section"&gt;caching&lt;/a&gt;
makes it easy to upload and download cached items from object storage
repositories like Google Cloud Storage or Amazon S3.&lt;/p&gt;
&lt;p&gt;However, there&amp;rsquo;s an often overlooked feature hiding in the &lt;a href="https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnersdocker-section"&gt;configuration for
the docker executor&lt;/a&gt; that provides a great performance boost: mounting tmpfs
inside your container. Not familiar with tmpfs? Arch Linux has a &lt;a href="https://wiki.archlinux.org/index.php/Tmpfs"&gt;great wiki
page for tmpfs&lt;/a&gt; and James Coyle has a &lt;a href="https://www.jamescoyle.net/knowledge/951-the-difference-between-a-tmpfs-and-ramfs-ram-disk"&gt;well-written blog post&lt;/a&gt; about what
makes it unique from the older ramfs.&lt;/p&gt;
&lt;p&gt;RAM is much faster than your average cloud provider&amp;rsquo;s block storage. It also
has incredibly low latency relative to most storage media. There&amp;rsquo;s a &lt;a href="https://people.eecs.berkeley.edu/~rcs/research/interactive_latency.html"&gt;great
interactive latency page&lt;/a&gt; that allows you to use a slider to travel back in
time to 1990 and compare all kinds of storage performance numbers. &lt;em&gt;(It&amp;rsquo;s
really fun! Go drag the slider and be amazed.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Better yet, many cloud providers give you lots of RAM per CPU on their
instances, so if your work isn&amp;rsquo;t terribly memory intensive, you can use a lot
of this RAM for faster storage.&lt;/p&gt;
&lt;h2 id="enabling-tmpfs-in-docker-containers"&gt;Enabling tmpfs in Docker containers&lt;/h2&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Beware of the dangers of tmpfs before adjusting your runner
configuration!&lt;/strong&gt; See the warnings at the end of this post.&lt;/p&gt;
&lt;p&gt;This configuration is buried in the middle of the &lt;a href="https://docs.gitlab.com/runner/executors/docker.html#mounting-a-directory-in-ram"&gt;docker executor
documentation&lt;/a&gt;. You will need to add some extra configuration to your
&lt;code&gt;[runners.docker]&lt;/code&gt; section to make it work:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;runners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;runners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tmpfs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/ramdisk&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rw,noexec&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This configuration mounts a tmpfs volume underneath &lt;code&gt;/ramdisk&lt;/code&gt; inside the
container. By default, this directory will be mounted with &lt;code&gt;noexec&lt;/code&gt;, but if
you need to execute scripts from that directory, change &lt;code&gt;noexec&lt;/code&gt; to &lt;code&gt;exec&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;runners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;runners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tmpfs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/ramdisk&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rw,exec&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In our case, compiling kernels requires executing scripts, so we use &lt;code&gt;exec&lt;/code&gt;
for our tmpfs mounts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You must be specific for &lt;code&gt;exec&lt;/code&gt;!&lt;/strong&gt; As an example, this tmpfs volume will be
mounted with &lt;code&gt;noexec&lt;/code&gt; since that is the default:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;runners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;runners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tmpfs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/ramdisk&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rw&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="extra-speed"&gt;Extra speed&lt;/h2&gt;
&lt;p&gt;For even more speed, we moved the objects generated by &lt;code&gt;ccache&lt;/code&gt; to the
ramdisk. The seek times are much lower and this allows &lt;code&gt;ccache&lt;/code&gt; to look for
its cached objects much more quickly.&lt;/p&gt;
&lt;p&gt;Git repositories are also great things to stash on tmpfs. Big kernel
repositories are usually 1.5GB to 2GB in size with tons of files. Checkouts
are really fast when they&amp;rsquo;re done in tmpfs.&lt;/p&gt;
&lt;h2 id="dangers-are-lurking"&gt;Dangers are lurking&lt;/h2&gt;
&lt;p&gt;⚠️ As mentioned earlier, &lt;strong&gt;beware of the dangers of tmpfs.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;All of the containers on the machine will share the same amount of RAM for
their tmpfs volumes. Be sure to account for how much each container will use
and how many containers &lt;em&gt;could&lt;/em&gt; be present on the same machine.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Be aware of how much memory your tests will use when they run. In our case,
kernel compiles can consume 2-4GB of RAM, depending on configuration, so we
try our best to leave some memory free.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;These volumes also have no limits on how much data can go into the volume.
However, if you put too much data into the tmpfs volume and your system runs
critically low on available RAM, you could see a huge drop in performance,
system instability, or even a crash. 🔥&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>buildah error: vfs driver does not support overlay.mountopt options</title><link>https://major.io/p/buildah-error-vfs-driver-does-not-support-overlay-mountopt-options/</link><pubDate>Tue, 13 Aug 2019 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/buildah-error-vfs-driver-does-not-support-overlay-mountopt-options/</guid><description>&lt;p&gt;Buildah and podman make a great pair for building, managing and running
containers on a Linux system. You can even &lt;a href="https://major.io/2019/05/24/build-containers-in-gitlab-ci-with-buildah/"&gt;use them with GitLab CI&lt;/a&gt; with a
few small adjustments, namely the switch from the overlayfs to vfs storage
driver.&lt;/p&gt;
&lt;p&gt;I have some regularly scheduled GitLab CI jobs that attempt to build fresh
containers each morning and I use these to get the latest packages and find
out early when something is broken in the build process. A failed build
appeared in my inbox earlier this week with the following error:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+ buildah bud -f builds/builder-fedora30 -t builder-fedora30 .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vfs driver does not support overlay.mountopt options
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;My container build script&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; is fairly basic, but it does include a change to
use the vfs storage driver:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Use vfs with buildah. Docker offers overlayfs as a default, but buildah&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# cannot stack overlayfs on top of another overlayfs filesystem.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;STORAGE_DRIVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vfs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The script doesn&amp;rsquo;t change any mount options during the build process. A quick
glance at the &lt;code&gt;/etc/containers/storage.conf&lt;/code&gt; revealed a possible problem:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[storage.options]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Storage options to be passed to underlying storage drivers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# mountopt specifies comma separated list of extra mount options&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;mountopt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;nodev,metacopy=on&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These mount options make sense when used with an overlayfs filesystem, but
they are not used with vfs. I commented out the &lt;code&gt;mountopt&lt;/code&gt; option, saved the
file, and ran a test build locally. &lt;strong&gt;Success!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Fixing the build script involved a small change to the &lt;code&gt;storage.conf&lt;/code&gt; just
before building the container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Use vfs with buildah. Docker offers overlayfs as a default, but buildah&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# cannot stack overlayfs on top of another overlayfs filesystem.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;STORAGE_DRIVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vfs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Newer versions of podman/buildah try to set overlayfs mount options when&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# using the vfs driver, and this causes errors.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sed -i &lt;span class="s1"&gt;&amp;#39;/^mountopt =.*/d&amp;#39;&lt;/span&gt; /etc/containers/storage.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;My containers are happily building again in GitLab.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;The original build script is no longer available, but the &lt;a href="https://gitlab.com/cki-project/containers/"&gt;remainder of
the repository&lt;/a&gt; still exists.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Build containers in GitLab CI with buildah</title><link>https://major.io/p/build-containers-in-gitlab-ci-with-buildah/</link><pubDate>Fri, 24 May 2019 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/build-containers-in-gitlab-ci-with-buildah/</guid><description>&lt;p&gt;My team at Red Hat depends heavily on &lt;a href="https://about.gitlab.com/product/continuous-integration/"&gt;GitLab CI&lt;/a&gt; and we build containers
often to run all kinds of tests. Fortunately, GitLab offers up CI to build
containers and a &lt;a href="https://about.gitlab.com/2016/05/23/gitlab-container-registry/"&gt;container registry&lt;/a&gt; in every repository to hold the
containers we build.&lt;/p&gt;
&lt;p&gt;This is really handy because it keeps everything together in one place: your
container build scripts, your container build infrastructure, and the
registry that holds your containers. Better yet, you can put multiple types
of containers underneath a single git repository if you need to build
containers based on different Linux distributions.&lt;/p&gt;
&lt;h2 id="building-with-docker-in-gitlab-ci"&gt;Building with Docker in GitLab CI&lt;/h2&gt;
&lt;p&gt;By default, GitLab offers up a &lt;a href="https://docs.gitlab.com/ee/ci/docker/using_docker_build.html"&gt;Docker builder&lt;/a&gt; that works just fine. The CI
system clones your repository, builds your containers and pushes them
wherever you want. There&amp;rsquo;s even a &lt;a href="https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#using-docker-caching"&gt;simple CI YAML file&lt;/a&gt; that does everything
end-to-end for you.&lt;/p&gt;
&lt;p&gt;However, I have two issues with the Docker builder:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Larger images:&lt;/strong&gt; The Docker image layering is handy, but the images end up
being a bit larger, especially if you don&amp;rsquo;t do a little cleanup in each
stage.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Additional service:&lt;/strong&gt; It requires an additional service inside the CI
runner for the &lt;code&gt;dind&lt;/code&gt; (&amp;ldquo;Docker in Docker&amp;rdquo;) builder. This has caused some CI
delays for me several times.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="building-with-buildah-in-gitlab-ci"&gt;Building with buildah in GitLab CI&lt;/h2&gt;
&lt;p&gt;On my local workstation, I use &lt;a href="https://podman.io/"&gt;podman&lt;/a&gt; and &lt;a href="https://buildah.io/"&gt;buildah&lt;/a&gt; all the time to build,
run, and test containers. These tools are handy because I don&amp;rsquo;t need to
remember to start the Docker daemon each time I want to mess with a
container. I also don&amp;rsquo;t need sudo.&lt;/p&gt;
&lt;p&gt;All of my containers are stored beneath my home directory. That&amp;rsquo;s good for
keeping disk space in check, but it&amp;rsquo;s especially helpful on shared servers
since each user has their own unique storage. My container pulls and builds
won&amp;rsquo;t disrupt anyone else&amp;rsquo;s work on the server and their work won&amp;rsquo;t disrupt
mine.&lt;/p&gt;
&lt;p&gt;Finally, buildah offers some nice options out of the box. First, when you
build a container with &lt;code&gt;buildah bud&lt;/code&gt;, you end up with only three layers by
default:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Original OS layer (example: &lt;code&gt;fedora:30&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Everything you added on top of the OS layer&lt;/li&gt;
&lt;li&gt;Tiny bit of metadata&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is incredibly helpful if you use package managers like &lt;code&gt;dnf&lt;/code&gt;, &lt;code&gt;apt&lt;/code&gt;, and
&lt;code&gt;yum&lt;/code&gt; that download a bunch of metadata before installing packages. You would
normally have to clear the metadata carefully for the package manager so that
your container wouldn&amp;rsquo;t grow in size. Buildah takes care of that by squashing
all the stuff you add into one layer.&lt;/p&gt;
&lt;p&gt;Of course, if you want to be more aggressive, buildah offers the &lt;code&gt;--squash&lt;/code&gt;
option which squashes the whole image down into one layer. This can be
helpful if disk space is at a premium and you change the layers often.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting started&lt;/h2&gt;
&lt;p&gt;I have a repository called &lt;a href="https://gitlab.com/majorhayden/os-containers"&gt;os-containers&lt;/a&gt; in GitLab that maintains fully
updated containers for Fedora 29 and 30. The &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file calls
&lt;code&gt;build.sh&lt;/code&gt; for two containers: &lt;em&gt;fedora29&lt;/em&gt; and &lt;em&gt;fedora30&lt;/em&gt;. Open the &lt;code&gt;build.sh&lt;/code&gt;
file and follow along here:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Use vfs with buildah. Docker offers overlayfs as a default, but buildah&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# cannot stack overlayfs on top of another overlayfs filesystem.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;STORAGE_DRIVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vfs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First off, we need to tell buildah to use the vfs storage driver. Docker uses
overlayfs by default and stacking overlay filesystems will definitely lead to
problems. Buildah won&amp;rsquo;t let you try it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Write all image metadata in the docker format, not the standard OCI format.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Newer versions of docker can handle the OCI format, but older versions, like&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# the one shipped with Fedora 30, cannot handle the format.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;BUILDAH_FORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default, buildah uses the &lt;a href="https://github.com/opencontainers/image-spec"&gt;&lt;em&gt;oci&lt;/em&gt; container format&lt;/a&gt;. This sometimes causes
issues with older versions of Docker that don&amp;rsquo;t understand how to parse that
type of metadata. By setting the format to &lt;code&gt;docker&lt;/code&gt;, we&amp;rsquo;re using a format
that almost all container runtimes can understand.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Log into GitLab&amp;#39;s container repository.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;REGISTRY_AUTH_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/auth.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$CI_REGISTRY_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; buildah login -u &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$CI_REGISTRY_USER&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; --password-stdin &lt;span class="nv"&gt;$CI_REGISTRY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we set a path for the &lt;code&gt;auth.json&lt;/code&gt; that contains the credentials for
talking to the container repository. We also use buildah to authenticate to
GitLab&amp;rsquo;s built-in container repository. GitLab automatically exports these
variables for us (and hides them in the job output), so we can use them here.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;buildah bud -f builds/&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; -t &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;re now building the container and storing it temporarily as the bare image
name, such as &lt;em&gt;fedora30&lt;/em&gt;. This is roughly equivalent to &lt;code&gt;docker build&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;CONTAINER_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;buildah from &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;buildah commit --squash &lt;span class="nv"&gt;$CONTAINER_ID&lt;/span&gt; &lt;span class="nv"&gt;$FQ_IMAGE_NAME&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we are making a reference to our container with &lt;code&gt;buildah from&lt;/code&gt; and using
that reference to squash that container down into a single layer. This keeps
the container as small as possible.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;commit&lt;/code&gt; step also tags the resulting image using our fully qualified
image name (in this case, it&amp;rsquo;s
&lt;code&gt;registry.gitlab.com/majorhayden/os-containers/fedora30:latest&lt;/code&gt;)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;buildah push &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FQ_IMAGE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the same as &lt;code&gt;docker push&lt;/code&gt;. There&amp;rsquo;s not much special to see here.&lt;/p&gt;
&lt;h2 id="maintaining-containers"&gt;Maintaining containers&lt;/h2&gt;
&lt;p&gt;GitLab allows you to take things to the next level with CI schedules. In my
repository, there is a schedule to build my containers once a day to catch
the latest updates. I use these containers a lot and they need to be up to
date before I can run tests.&lt;/p&gt;
&lt;p&gt;If the container build fails for some reason, GitLab will send me an email to
let me know.&lt;/p&gt;</description></item></channel></rss>