<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Coreos on Major Hayden</title><link>https://major.io/tags/coreos/</link><description>Recent content in Coreos 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 &lt;a href="https://creativecommons.org/licenses/by-sa/4.0/"&gt;CC BY-SA 4.0&lt;/a&gt;</copyright><lastBuildDate>Mon, 09 Nov 2026 20:39:15 -0600</lastBuildDate><atom:link href="https://major.io/tags/coreos/index.xml" rel="self" type="application/rss+xml"/><item><title>Connect Caddy to Porkbun</title><link>https://major.io/p/caddy-porkbun/</link><pubDate>Thu, 29 Feb 2024 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/caddy-porkbun/</guid><description>&lt;p&gt;I recently told a coworker about &lt;a href="https://caddyserver.com/"&gt;Caddy&lt;/a&gt;, a small web and proxy server with a very simple configuration.&#10;It also has a handy feature where it manages your TLS certificate for you automatically.&lt;/p&gt;&#10;&lt;p&gt;However, one problem I had at home with my &lt;a href="https://fedoraproject.org/coreos/"&gt;CoreOS&lt;/a&gt; deployment is that I don&amp;rsquo;t have inbound network access to handle the certificate verification process.&#10;Most automated certificate vendors need to reach your web server to verify that you have control over your domain.&lt;/p&gt;&#10;&lt;p&gt;This post talks about how to work around this problem with domains registered at &lt;a href="https://porkbun.com/"&gt;Porkbun&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 id="dns-validation"&gt;DNS validation&lt;/h2&gt;&#10;&lt;p&gt;Certificate providers usually default to verifying domains by making a request to your server and retrieving a validation code.&#10;If your systems are all behind a firewall without inbound access from the internet, you can use DNS validation instead.&lt;/p&gt;&#10;&lt;p&gt;The process looks something like this:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;You tell the certificate provider the domain names you want on your certificate&lt;/li&gt;&#10;&lt;li&gt;The certificate provider gives you some DNS records to add wherever you host your DNS records&lt;/li&gt;&#10;&lt;li&gt;You add the DNS records&lt;/li&gt;&#10;&lt;li&gt;You get your certificates once the certificate provider verifies the records.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;You can do this manually with something like &lt;a href="https://github.com/acmesh-official/acme.sh"&gt;acme.sh&lt;/a&gt; today, but it&amp;rsquo;s &lt;strong&gt;painful&lt;/strong&gt;:&lt;/p&gt;&#10;&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;# Make the initial certificate request&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;acme.sh --issue --dns -d example.com &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --yes-I-know-dns-manual-mode-enough-go-ahead-please&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Add your DNS records manually.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Verify the DNS records and issue the certificates.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;acme.sh --issue --dns -d example.com &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --yes-I-know-dns-manual-mode-enough-go-ahead-please --renew&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Copy the keys/certificates and configure your webserver.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We don&amp;rsquo;t want to live this way.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="do-not-want.gif" alt="do-not-want.gif"&gt;&lt;/p&gt;&#10;&lt;p&gt;Let&amp;rsquo;s talk about how Caddy can help.&lt;/p&gt;&#10;&lt;h2 id="adding-porkbun-support-to-caddy"&gt;Adding Porkbun support to Caddy&lt;/h2&gt;&#10;&lt;p&gt;Caddy is a minimal webserver and &lt;a href="https://github.com/caddy-dns/porkbun"&gt;Porkbun support&lt;/a&gt; doesn&amp;rsquo;t get included by default.&#10;However, we can quickly add it via a simple container build:&lt;/p&gt;&#10;&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="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;caddy:2.7.6-builder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;&lt;span class="err"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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; xcaddy build &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --with github.com/caddy-dns/porkbun&lt;span class="err"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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;caddy:2.7.6&lt;/span&gt;&lt;span class="err"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; --from&lt;span class="o"&gt;=&lt;/span&gt;builder /usr/bin/caddy /usr/bin/caddy&lt;span class="err"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a two stage container build where we compile the Porkbun support and then use that new &lt;code&gt;caddy&lt;/code&gt; binary in the final container.&lt;/p&gt;&#10;&lt;p&gt;We&amp;rsquo;re not done yet!&lt;/p&gt;&#10;&lt;h2 id="automated-caddy-builds-with-updates"&gt;Automated Caddy builds with updates&lt;/h2&gt;&#10;&lt;p&gt;I created a &lt;a href="https://github.com/major/caddy"&gt;GitHub repository&lt;/a&gt; that builds the Caddy container for me and keeps it updated.&#10;There&amp;rsquo;s a &lt;a href="https://github.com/major/caddy/blob/main/.github/workflows/docker-publish.yml"&gt;workflow to publish a container&lt;/a&gt; to GitHub&amp;rsquo;s container repository and I can pull containers from there on my various CoreOS machines.&lt;/p&gt;&#10;&lt;p&gt;In addition, I use &lt;a href="https://github.com/apps/renovate"&gt;Renovate&lt;/a&gt; to watch for Caddy updates.&#10;New updates come through a &lt;a href="https://github.com/major/caddy/pull/10"&gt;regular pull request&lt;/a&gt; and I can apply them whenever I want.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="pr.png" alt="Renovate pull request" title="Example pull request from Renovate"&gt;&lt;/p&gt;&#10;&lt;h2 id="connecting-to-porkbun"&gt;Connecting to Porkbun&lt;/h2&gt;&#10;&lt;p&gt;We start here by getting an API key to manage the domain at Porkbun.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Log into your &lt;a href="https://porkbun.com/account/domainsSpeedy"&gt;Porkbun dashboard&lt;/a&gt;.&lt;/li&gt;&#10;&lt;li&gt;Click &lt;strong&gt;Details&lt;/strong&gt; to the right of the domain you want to manage.&lt;/li&gt;&#10;&lt;li&gt;Look for &lt;strong&gt;API Access&lt;/strong&gt; in the leftmost column and turn it on.&lt;/li&gt;&#10;&lt;li&gt;At the top right of the dashboard, click &lt;strong&gt;Account&lt;/strong&gt; and then &lt;strong&gt;API Access&lt;/strong&gt;.&lt;/li&gt;&#10;&lt;li&gt;Add a title for your new API key, such as &lt;em&gt;Caddy&lt;/em&gt;, and click &lt;strong&gt;Create API Key&lt;/strong&gt;.&lt;/li&gt;&#10;&lt;li&gt;Save the API key and secrey key that are displayed.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Open up your Caddy configuration file (the &lt;em&gt;Caddyfile&lt;/em&gt;) and add some configuration:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-caddyfile" data-lang="caddyfile"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;email&lt;/span&gt; &lt;span class="s"&gt;me@example.com&lt;/span&gt;&lt;span class="c1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # Uncomment this next line if you want to get&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # some test certificates first.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;acme_dns&lt;/span&gt; &lt;span class="s"&gt;porkbun&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;api_key&lt;/span&gt; &lt;span class="s"&gt;pk1_******&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;api_secret_key&lt;/span&gt; &lt;span class="s"&gt;sk1_******&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;example.com&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;handle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;respond&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Hello world!&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Save the Caddyfile and restart your Caddy server or container.&#10;Caddy will immediately begin requesting your TLS certificates and managing your DNS records for those certificates.&#10;This normally finishes in less than 30 seconds or so during the first run.&lt;/p&gt;&#10;&lt;p&gt;If you don&amp;rsquo;t see the HTTPS endpoint working within a minute or two, be sure to check the Caddy logs.&#10;You might have a typo in a Porkbun API key or the domain you&amp;rsquo;re trying to modify doesn&amp;rsquo;t have the &lt;strong&gt;API Access&lt;/strong&gt; switch enabled.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;[!NOTE]&#10;Remember that Porkbun requires you to enable API access for each domain.&#10;API access is disabled at Porkbun by default.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;&lt;strong&gt;That&amp;rsquo;s it!&lt;/strong&gt; 🎉&lt;/p&gt;&#10;&lt;h2 id="renewals"&gt;Renewals&lt;/h2&gt;&#10;&lt;p&gt;Caddy will keep watch over the certificates and begin the renewal process as the expiration approaches.&#10;It has a very careful retry mechanism that ensures your certificates are updated without tripping any rate limits at the certificate provider.&lt;/p&gt;&#10;&lt;h2 id="further-reading"&gt;Further reading&lt;/h2&gt;&#10;&lt;p&gt;Caddy&amp;rsquo;s detailed documentation about &lt;a href="https://caddyserver.com/docs/automatic-https"&gt;Automatic HTTPS&lt;/a&gt; and the &lt;a href="https://caddyserver.com/docs/caddyfile/directives/tls"&gt;tls configuration directive&lt;/a&gt; should answer most questions about how the process works.&lt;/p&gt;&#10;</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;&#10;&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.&#10;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;&#10;&lt;p&gt;The answer is pretty basic.&#10;&lt;strong&gt;It makes my life easier.&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m a Dad.&#10;I&amp;rsquo;m on the PTC (Parent Teacher Club) at one of my children&amp;rsquo;s schools.&#10;I volunteer as an IT person for a non-profit.&#10;I write software.&#10;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;&#10;&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;&#10;&lt;p&gt;That&amp;rsquo;s what this blog post is about!&lt;/p&gt;&#10;&lt;h1 id="updates"&gt;Updates&lt;/h1&gt;&#10;&lt;p&gt;First and foremost, I love how CoreOS does updates.&#10;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;&#10;&lt;ol&gt;&#10;&lt;li&gt;Updates are automatically retrieved and they&amp;rsquo;re loaded into a slot.&lt;/li&gt;&#10;&lt;li&gt;Your system reboots into the new update but your original OS tree remains in place.&lt;/li&gt;&#10;&lt;li&gt;Did the update boot? Awesome. You&amp;rsquo;re good to go.&lt;/li&gt;&#10;&lt;li&gt;Did something break? The system reverts back to the known good tree.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;In this way, it&amp;rsquo;s a lot like your smartphone.&lt;/p&gt;&#10;&lt;p&gt;You have full control over when a node looks for an update and how often it checks for them.&#10;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;&#10;&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.&#10;The updates come through, monitoring shuts off, the node reboots, and monitoring comes back.&#10;The nodes almost always come back before the monitoring even alerts me.&lt;/p&gt;&#10;&lt;p&gt;It also removes the reminders I would set for myself to update packages and run reboots.&#10;I know that my CoreOS nodes will do this automatically, so I don&amp;rsquo;t need to think about it.&lt;/p&gt;&#10;&lt;p&gt;Also, updates are rarely ever impactful to my workloads since all of them are running inside containers.&#10;My containers come right back up as soon as the node finishes its reboot.&lt;/p&gt;&#10;&lt;h1 id="toolbox"&gt;Toolbox&lt;/h1&gt;&#10;&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.&#10;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;&#10;&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.&#10;Do that in a toolbox container.&#10;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;&#10;&lt;p&gt;But it gets better.&lt;/p&gt;&#10;&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.&#10;Also, these containers have seamless access to anything you have in your home directory, including sockets.&#10;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.&#10;You get the best of both worlds.&lt;/p&gt;&#10;&lt;p&gt;Don&amp;rsquo;t want Fedora?&#10;You have lots of distribution options through toolbox.&#10;Read the details on &lt;a href="https://containertoolbx.org/install/"&gt;custom images&lt;/a&gt; to create your own!&lt;/p&gt;&#10;&lt;h1 id="layering"&gt;Layering&lt;/h1&gt;&#10;&lt;p&gt;Okay, there are those situations where you really want a package on CoreOS and toolbox might not be sufficient.&#10;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;&#10;&lt;p&gt;You have a couple of options here:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&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;&#10;&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!&#10;&lt;em&gt;(And it&amp;rsquo;s there after a reboot as well.)&lt;/em&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&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.&#10;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;&#10;&lt;h1 id="declarative-provisioning"&gt;Declarative provisioning&lt;/h1&gt;&#10;&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.&#10;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;&#10;&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.&#10;You use the &lt;code&gt;butane&lt;/code&gt; utility to get it into ignition format.&#10;The ignition format is highly compressed to ensure you can fit your configuration into most cloud providers&amp;rsquo; metadata fields.&lt;/p&gt;&#10;&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;&#10;&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.&#10;It&amp;rsquo;s easy to add files, configure Wireguard, set up users, and launch containers immediately on the first boot.&lt;/p&gt;&#10;&lt;h1 id="pets-and-cattle"&gt;Pets and cattle&lt;/h1&gt;&#10;&lt;p&gt;CoreOS works well for systems that I only need online for a short time.&#10;These might be situations where I need to test a few containers and throw it away.&#10;There&amp;rsquo;s no OS to mess with and no updates to worry about.&lt;/p&gt;&#10;&lt;p&gt;It also works well for systems that I keep online for a long time.&#10;I have a few physical systems at home that run CoreOS and they&amp;rsquo;ve been extremely stable.&#10;I also have cloud instances on Hetzner, VULTR, and Digital Ocean that have run CoreOS for months without issues.&lt;/p&gt;&#10;&lt;h1 id="more-questions"&gt;More questions?&lt;/h1&gt;&#10;&lt;p&gt;Feel free to &lt;a href="mailto:major+coreos@mhtx.net"&gt;send me an email&lt;/a&gt;.&#10;I&amp;rsquo;ll update this post if I get some good ones!&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&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;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</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;&#10;&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.&#10;Why do I love containers so much?&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;They start quickly&lt;/li&gt;&#10;&lt;li&gt;They make your workloads portable&lt;/li&gt;&#10;&lt;li&gt;They disconnect your application stack from the OS that runs underneath&lt;/li&gt;&#10;&lt;li&gt;You can send your application through CI as a single container image&lt;/li&gt;&#10;&lt;li&gt;You can isolate workloads on the network and limit their resource usage much like a VM&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&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;.&#10;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;&#10;&lt;p&gt;&lt;strong&gt;Yes, I think they can.&lt;/strong&gt;&lt;/p&gt;&#10;&lt;h1 id="whats-a-quadlet"&gt;What&amp;rsquo;s a quadlet?&lt;/h1&gt;&#10;&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.&#10;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.&#10;These unit files weren&amp;rsquo;t easy to edit or even parse with eyeballs.&lt;/p&gt;&#10;&lt;p&gt;Quadlets make this easier by giving you a simple ini-style file that you can easily read and edit.&#10;This blog post will include some quadlets later, but here&amp;rsquo;s an example one for Wordpress:&lt;/p&gt;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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.&#10;However, there&amp;rsquo;s something new here.&lt;/p&gt;&#10;&lt;p&gt;Check out the &lt;code&gt;AutoUpdate=registry&lt;/code&gt; line.&#10;This tells podman to keep your container updated on a regular basis with the upstream container registry.&#10;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;&#10;&lt;p&gt;Also, at the very end, you&amp;rsquo;ll see a &lt;code&gt;WantedBy&lt;/code&gt; line.&#10;This is a great place to set up container dependencies.&#10;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;&#10;&lt;h1 id="so-why-not-stick-with-docker-compose"&gt;So why not stick with docker-compose?&lt;/h1&gt;&#10;&lt;p&gt;There&amp;rsquo;s no denying that docker-compose is an awesome tool.&#10;You specify the desired outcome, tell it to bring up containers, and it gets containers into the state you specified.&#10;It handles volumes, networks, and complicated configuration without a lot of legwork.&#10;The YAML files are pretty easy to read, too.&lt;/p&gt;&#10;&lt;p&gt;However, as with watchtower, that&amp;rsquo;s another external dependency.&lt;/p&gt;&#10;&lt;p&gt;My container deployments are often done at instance boot time and I don&amp;rsquo;t make too many changes afterwards.&#10;I found myself using docker-compose for the initial deployment and then I didn&amp;rsquo;t really use it again.&lt;/p&gt;&#10;&lt;p&gt;Why not remove it entirely and use what&amp;rsquo;s built into CoreOS already?&lt;/p&gt;&#10;&lt;h1 id="quaint-quadlets-quickly"&gt;Quaint quadlets quickly!&lt;/h1&gt;&#10;&lt;p&gt;Before we start, we&amp;rsquo;re going to need a few things:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&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;&#10;&lt;li&gt;Some quadlets&lt;/li&gt;&#10;&lt;li&gt;Extra system configuration&lt;/li&gt;&#10;&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;&#10;&lt;/ul&gt;&#10;&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.&#10;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;&#10;&lt;p&gt;Let&amp;rsquo;s break it down here.&#10;First up, we add an ssh key for the default &lt;code&gt;core&lt;/code&gt; user.&lt;/p&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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&#10;&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&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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]&#10;&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&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;p&gt;We can go through each quadlet in detail.&#10;First up is MariaDB.&#10;We tell systemd that the wordpress container will want to have this one started first.&lt;/p&gt;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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.&#10;These ports will be published to the container host.&#10;Also, you&amp;rsquo;ll note that the wordpress volume is mounted here, too.&#10;This is because caddy can serve static files &lt;em&gt;much faster&lt;/em&gt; than wordpress can.&lt;/p&gt;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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&#10;&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;&#10;&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;\&#10;&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;\&#10;&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;\&#10;&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;\&#10;&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;\&#10;&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;\&#10;&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;\&#10;&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;&#10;&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.&#10;Then install vultr-cli and butane:&lt;/p&gt;&#10;&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&#10;&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;&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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;&#10;&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;&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&#9;&amp;lt;meta name=&amp;#34;viewport&amp;#34; content=&amp;#34;width=device-width&amp;#34; /&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&#9;&amp;lt;meta http-equiv=&amp;#34;Content-Type&amp;#34; content=&amp;#34;text/html; charset=utf-8&amp;#34; /&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&#9;&amp;lt;meta name=&amp;#34;robots&amp;#34; content=&amp;#34;noindex,nofollow&amp;#34; /&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&#9;&amp;lt;title&amp;gt;WordPress &amp;amp;rsaquo; Installation&amp;lt;/title&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&#9;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;h1 id="managing-containers"&gt;Managing containers&lt;/h1&gt;&#10;&lt;p&gt;Containers will automatically update on a schedule and you can check the timer:&lt;/p&gt;&#10;&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&#10;&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&#10;&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)&#10;&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&#10;&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&#10;&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&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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.&#10;&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;&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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;&#10;&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&#10;&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&#10;&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&#10;&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)&#10;&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&#10;&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&#10;&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&#10;&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)&#10;&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)&#10;&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&#10;&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&#10;&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;&#10;&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&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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 ---&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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&#10;&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&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Enjoy!&lt;/p&gt;&#10;</description></item><item><title>Deploy a containerized Ghost blog 👻</title><link>https://major.io/p/deploy-ghost/</link><pubDate>Tue, 27 Jun 2023 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/deploy-ghost/</guid><description>&lt;p&gt;There&amp;rsquo;s no shortage of options for starting a self-hosted blog.&#10;Wordpress might be chosen most often, but I stumbled upon &lt;a href="https://ghost.org/"&gt;Ghost&lt;/a&gt; recently and their &lt;a href="https://ghost.org/vs/wordpress/"&gt;performance numbers&lt;/a&gt; really got my attention.&lt;/p&gt;&#10;&lt;p&gt;I prefer deploying most things in containers these days with &lt;a href="https://fedoraproject.org/coreos/"&gt;Fedora CoreOS&lt;/a&gt;.&#10;Luckily, the Ghost stack doesn&amp;rsquo;t demand a lot of infrastructure:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Ghost itself&lt;/li&gt;&#10;&lt;li&gt;MySQL 8+ &lt;em&gt;(I went with MariaDB 11.x)&lt;/em&gt;&lt;/li&gt;&#10;&lt;li&gt;A web server out front&lt;/li&gt;&#10;&lt;li&gt;TLS certificate&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;[!NOTE]&#10;Although I chose MariaDB for the database here, &lt;strong&gt;Ghost recommends MySQL&lt;/strong&gt; and will throw a warning in the admin panel if you&amp;rsquo;re using something else.&#10;I haven&amp;rsquo;t had any issues so far, but &lt;strong&gt;you&amp;rsquo;ve been warned&lt;/strong&gt;. 💣&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;I picked Caddy for the webserver since it&amp;rsquo;s so small and the configuration is tremendously simple.&lt;/p&gt;&#10;&lt;h1 id="launch-coreos"&gt;Launch CoreOS&lt;/h1&gt;&#10;&lt;p&gt;Fedora CoreOS offers lots of &lt;a href="https://fedoraproject.org/coreos/download/?stream=stable"&gt;cloud options&lt;/a&gt; for launching it immediately.&#10;Many public clouds already have CoreOS images available, but I love Hetzner&amp;rsquo;s US locations and I already had a CoreOS image loaded up in my account.&lt;/p&gt;&#10;&lt;p&gt;🇩🇪 Want CoreOS at Hetzner?&#10;There&amp;rsquo;s a &lt;a href="https://major.io/p/deploy-fedora-coreos-in-hetzner-cloud/"&gt;blog post&lt;/a&gt; for that!&lt;/p&gt;&#10;&lt;p&gt;Once your CoreOS instance is running, connect to the instance over ssh and ensure the &lt;code&gt;docker.service&lt;/code&gt; starts on each boot:&lt;/p&gt;&#10;&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;sudo systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; --now docker.service&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This ensures that containers come up on each reboot.&#10;CoreOS has a podman socket that listens for docker-compatible connections, but that doesn&amp;rsquo;t help with reboots.&lt;/p&gt;&#10;&lt;p&gt;Perhaps I&amp;rsquo;m old fashioned, but I still enjoy using &lt;a href="https://github.com/docker/compose"&gt;docker-compose&lt;/a&gt; for container management.&#10;I like how I can declare what I want and let &lt;code&gt;docker-compose&lt;/code&gt; sort out the rest.&lt;/p&gt;&#10;&lt;p&gt;Let&amp;rsquo;s install &lt;code&gt;docker-compose&lt;/code&gt; on the CoreOS instance now:&lt;/p&gt;&#10;&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;# Check the latest version in the GitHub repo before starting!&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# https://github.com/docker/compose&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -LO https://github.com/docker/compose/releases/download/v2.19.0/docker-compose-linux-x86_64&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install docker-compose and make it executable.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chown +x /usr/local/bin/docker-compose&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Verify that &lt;code&gt;docker-compose&lt;/code&gt; is ready to go:&lt;/p&gt;&#10;&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; docker-compose --version&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Docker Compose version v2.19.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="preparing-caddy"&gt;Preparing Caddy&lt;/h1&gt;&#10;&lt;p&gt;Caddy uses a configuration file called a &lt;em&gt;Caddyfile&lt;/em&gt; and we need that in place before we deploy the other containers.&#10;Within my home directory, I created a directory called &lt;em&gt;caddy&lt;/em&gt;:&lt;/p&gt;&#10;&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;mkdir caddy&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then I added the &lt;em&gt;Caddyfile&lt;/em&gt; inside the directory:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-caddy" data-lang="caddy"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="c1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # Your email for LetsEncrypt warnings/notices.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;email&lt;/span&gt; &lt;span class="s"&gt;youremail@domain.com&lt;/span&gt;&lt;span class="c1"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # Staging LetsEncrypt server to use while testing.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # Uncomment this before going to production!&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;acme_ca&lt;/span&gt; &lt;span class="s"&gt;https://acme-staging-v02.api.letsencrypt.org/directory&lt;/span&gt;&#10;&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="c1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Basic virtual host definition to feed traffic into the&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Ghost container when it arrives.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;example.com&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;reverse_proxy&lt;/span&gt; &lt;span class="n"&gt;ghost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2368&lt;/span&gt;&#10;&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="c1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# OPTIONAL: Redirect traffic to &amp;#39;www&amp;#39; to the bare domain.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;www.example.com&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;redir&lt;/span&gt; &lt;span class="s"&gt;https://example.com&lt;/span&gt;&lt;span class="se"&gt;{uri}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This configuration sets up LetsEncrypt certificates automatically from the staging server for now.&#10;Once we know our configuration is working well, we can comment out the &lt;code&gt;acme_ca&lt;/code&gt; line above and get production TLS certificates.&lt;/p&gt;&#10;&lt;p&gt;At this point, you need a DNS record pointed to your server so you can get a certificate.&#10;You have some options:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;If the site is entirely new,&lt;/strong&gt; just point the root domain name to your CoreOS instance.&#10;Use that domain in the configuration above and later in the deployment.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;If you&amp;rsquo;re migrating from an existing site,&lt;/strong&gt; choose a subdomain off your main domain to use.&#10;If your website is &lt;em&gt;example.com&lt;/em&gt;, use something like &lt;em&gt;test.example.com&lt;/em&gt; or &lt;em&gt;new.example.com&lt;/em&gt; to get Ghost up and running.&#10;It&amp;rsquo;s really easy to change this later.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Now we&amp;rsquo;re ready for the rest of the deployment.&lt;/p&gt;&#10;&lt;h1 id="deploying-containers"&gt;Deploying containers&lt;/h1&gt;&#10;&lt;p&gt;Here&amp;rsquo;s the &lt;code&gt;docker-compose.yml&lt;/code&gt; file I&amp;rsquo;m using:&lt;/p&gt;&#10;&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;&#10;&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="s1"&gt;&amp;#39;3.8&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&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;# OPTIONAL&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;# Watchtower monitors all running containers and updates&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;# them when the upstream container repo is updated.&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;watchtower&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;docker.io/containrrr/watchtower:latest&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;watchtower&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;coreos-ghost-deployment&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;WATCHTOWER_CLEANUP=true&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;WATCHTOWER_POLL_INTERVAL=3600&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;cleanup&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;privileged&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&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;# Caddy acts as our external-facing webserver and handles&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;# getting TLS certs from LetsEncrypt.&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;caddy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;caddy:latest&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;caddy&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;ghost&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;80&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;&#10;&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;443&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;443&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;&#10;&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;./caddy/Caddyfile:/etc/caddy/Caddyfile:Z&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;ghost:/var/www/html&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;caddy_data:/data&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;caddy_config:/config&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&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 Ghost blog software itself&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;ghost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;docker.io/library/ghost:5&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;ghost&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;always&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;ghostdb&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;https://example.com&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;database__client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;mysql&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;database__connection__host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ghostdb&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;database__connection__user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ghost&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;database__connection__password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;GHOST_PASSWORD_FOR_MARIADB&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;database__connection__database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ghostdb&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;ghost:/var/lib/ghost/content&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&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;# Our MariaDB database&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;ghostdb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;docker.io/library/mariadb:11&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;ghostdb&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;always&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;A_SECURE_ROOT_PASSWORD&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;MYSQL_USER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ghost&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;GHOST_PASSWORD_FOR_MARIADB&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;MYSQL_DATABASE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ghostdb&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;ghostdb:/var/lib/mysql&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;caddy_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;caddy_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;ghost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;ghostdb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I love &lt;a href="https://major.io/p/watchtower/"&gt;watchtower&lt;/a&gt; but that step is completely optional.&#10;It does require some elevated privileges to talk to the podman socket, so &lt;strong&gt;keep that in mind if you choose to use it.&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Our &lt;code&gt;ghostdb&lt;/code&gt; container starts first, followed by &lt;code&gt;ghost&lt;/code&gt;, and then &lt;code&gt;caddy&lt;/code&gt;.&#10;That follows the &lt;code&gt;depends_on&lt;/code&gt; configuration keys shown above.&lt;/p&gt;&#10;&lt;p&gt;There are two steps to take now:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Replace &lt;code&gt;GHOST_PASSWORD_FOR_MARIADB&lt;/code&gt; and &lt;code&gt;A_SECURE_ROOT_PASSWORD&lt;/code&gt; above with better passwords. 😉&lt;/li&gt;&#10;&lt;li&gt;Also, set the &lt;code&gt;url&lt;/code&gt; parameter for the &lt;code&gt;ghost&lt;/code&gt; container to your blog&amp;rsquo;s domain name.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Once all of that is done, let&amp;rsquo;s let &lt;code&gt;docker-compose&lt;/code&gt; do the heavy lifting:&lt;/p&gt;&#10;&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&#10;&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 verify that our containers are running:&lt;/p&gt;&#10;&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 docker-compose ps&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;NAME IMAGE COMMAND SERVICE &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;caddy caddy:latest &amp;#34;caddy run --config …&amp;#34; caddy &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ghost docker.io/library/ghost:5 &amp;#34;docker-entrypoint.s…&amp;#34; ghost &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ghostdb docker.io/library/mariadb:11 &amp;#34;docker-entrypoint.s…&amp;#34; ghostdb &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;watchtower docker.io/containrrr/watchtower:latest &amp;#34;/watchtower --clean…&amp;#34; watchtower &#10;&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;&#10;&lt;h1 id="ghost-initial-setup"&gt;Ghost initial setup&lt;/h1&gt;&#10;&lt;p&gt;With all of your containers running, browse to &lt;code&gt;https://example.com/ghost/&lt;/code&gt;&#10;Just add &lt;code&gt;/ghost/&lt;/code&gt; to the end of your domain name to reach the admin panel.&#10;Create your admin account there with a good password.&lt;/p&gt;&#10;&lt;p&gt;If everything looks good, run back to your &lt;em&gt;Caddyfile&lt;/em&gt; and comment out the &lt;code&gt;acme_ca&lt;/code&gt; line:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-caddy" data-lang="caddy"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="c1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # Your email for LetsEncrypt warnings/notices.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;email&lt;/span&gt; &lt;span class="s"&gt;youremail@domain.com&lt;/span&gt;&lt;span class="c1"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # Staging LetsEncrypt server to use while testing.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # Uncomment this before going to production!&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Restart the caddy container to get a production LetsEncrypt certificate on the site:&lt;/p&gt;&#10;&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 restart caddy&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="customizing-ghost"&gt;Customizing Ghost&lt;/h1&gt;&#10;&lt;p&gt;Ghost &lt;a href="https://ghost.org/docs/config/#custom-configuration-files"&gt;looks for lots of environment variables&lt;/a&gt; to determine its configuration and you can set these in your &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&#10;Although some configuration items are easy, like &lt;code&gt;url&lt;/code&gt;, some are nested and get more complicated.&#10;For these, you can use double underscores &lt;code&gt;__&lt;/code&gt; to handle the nesting.&lt;/p&gt;&#10;&lt;p&gt;As an example, we already used &lt;code&gt;database__connection__host&lt;/code&gt; in the &lt;code&gt;docker-compose.yaml&lt;/code&gt;, and that&amp;rsquo;s the equivalent to this nested configuration:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;database&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;connection&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;host&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;re deploying in containers, it&amp;rsquo;s a good idea to configure Ghost via environment variables.&#10;This ensures that your &lt;code&gt;docker-compose.yml&lt;/code&gt; is authoritative for the Ghost deployment.&#10;You &lt;em&gt;can&lt;/em&gt; &lt;code&gt;exec&lt;/code&gt; into the container, adjust the config file on disk, and restart Ghost, but then you have to remember where you configured each item. 🥵&lt;/p&gt;&#10;&lt;h1 id="switching-to-production-domain"&gt;Switching to production domain&lt;/h1&gt;&#10;&lt;p&gt;If you used a temporary domain to get everything configured and you&amp;rsquo;re ready to use your production domain, follow these steps:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Open your &lt;em&gt;Caddyfile&lt;/em&gt; and replace all instances of the testing domain with the production domain&lt;/li&gt;&#10;&lt;li&gt;Restart caddy: &lt;code&gt;sudo docker-compose restart caddy&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Edit the &lt;code&gt;docker-compose.yml&lt;/code&gt; and change the &lt;code&gt;url&lt;/code&gt; key in the &lt;code&gt;ghost&lt;/code&gt; container to the production domain&lt;/li&gt;&#10;&lt;li&gt;Apply the configuration with &lt;code&gt;sudo docker-compose up -d&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Enjoy your new automatically-updating Ghost blog deployment! 👻&lt;/p&gt;&#10;</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;.&#10;Keeping containers update remains a constant battle, but it&amp;rsquo;s still easier than running kubernetes.&lt;/p&gt;&#10;&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.&#10;It&amp;rsquo;s a simple container that does a few important things:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;It monitors (via docker/podman socket) the running containers on the host&lt;/li&gt;&#10;&lt;li&gt;It tracks the versions/tags of each container image&lt;/li&gt;&#10;&lt;li&gt;It looks for updated versions of the container image in their upstream repositories&lt;/li&gt;&#10;&lt;li&gt;Based on a configurable schedule, it pulls a new container image and restarts the container for updates&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&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;.&#10;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;&#10;&lt;p&gt;My new deployments always need watchtower running.&#10;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;&#10;&lt;h1 id="quadlets"&gt;Quadlets&lt;/h1&gt;&#10;&lt;p&gt;So what&amp;rsquo;s a quadlet?&lt;/p&gt;&#10;&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.&#10;Here&amp;rsquo;s an example &lt;code&gt;.container&lt;/code&gt; file from the post:&lt;/p&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;h1 id="configure-a-quadlet-on-boot"&gt;Configure a quadlet on boot&lt;/h1&gt;&#10;&lt;p&gt;As I mentioned earlier, I want a watchtower container running on my CoreOS nodes at first boot.&#10;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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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]&#10;&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&#10;&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&#10;&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&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;&#10;&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]&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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&#10;&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&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;&#10;&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]&#10;&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;&#10;&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;&#10;&lt;ol&gt;&#10;&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&#10;&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;&#10;&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;&#10;&lt;/ol&gt;&#10;&lt;p&gt;The quadlet file has some important configurations:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;I added environment variables to clean up outdated container images and check for updates once an hour&lt;/li&gt;&#10;&lt;li&gt;The podman socket is mounted inside the watchtower container&lt;/li&gt;&#10;&lt;li&gt;Security labels are disabled to allow for communication with the podman socket&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;[!NOTE]&#10;&lt;strong&gt;Mounting the podman socket and disabling security labels is not an ideal security approach.&lt;/strong&gt;&#10;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.&#10;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;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;From here, we convert the butane configuration into an ignition configuration.&#10;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;&#10;&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&#10;&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;&#10;&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.&#10;Just paste in the ignition configuration into the large text box before launch.&lt;/p&gt;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install vultr-cli&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&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;&#10;&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;\&#10;&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;\&#10;&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;&#10;&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;&#10;&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&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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&#10;&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&#10;&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&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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&#10;&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&#10;&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&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;p&gt;My system rebooted for an ostree update shortly after provisioning and the container came up automatically both times.&lt;/p&gt;&#10;</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&#10;Haven&amp;rsquo;t heard about it?&#10;Don&amp;rsquo;t despair.&#10;I&amp;rsquo;ll catch you up on what you missed.&lt;/p&gt;&#10;&lt;p&gt;Fedora CoreOS offers a really fast pathway to running containers on hardware, in virtual machines, or in clouds.&#10;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;&#10;&lt;p&gt;But that&amp;rsquo;s not the best part.&lt;/p&gt;&#10;&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;.&#10;The OS underneath your containers ships as a single unit and it automatically updates itself much like your mobile phone.&#10;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;&#10;&lt;p&gt;Many people use CoreOS as the workhorse underneath kubernetes.&#10;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.&#10;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;&#10;&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?&#10;&lt;strong&gt;Absolutely!&lt;/strong&gt;&lt;/p&gt;&#10;&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;&#10;&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.&#10;The basic ideas are these:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;When you take care of cattle, you take care of them as a group.&#10;Losing one or more of them would make you sad, but you know you have many others.&lt;/li&gt;&#10;&lt;li&gt;As for pets, you spend a lot of time taking care of them and playing with them.&#10;If you lost one, it would be devastating.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;A fleet of web servers could be treated like cattle.&#10;Keep lots of them online and replace any instances that have issues.&lt;/p&gt;&#10;&lt;p&gt;On the other hand, databases or tier zero systems (everyone feels if it they went down) are like pets.&#10;You carefully build, maintain, and monitor these.&lt;/p&gt;&#10;&lt;h1 id="how-does-coreos-fit-in"&gt;How does CoreOS fit in?&lt;/h1&gt;&#10;&lt;p&gt;Many people do use CoreOS as a container hosting platform as part of a bigger system.&#10;It works really well for that.&#10;But it&amp;rsquo;s great as a regular cloud server, too.&lt;/p&gt;&#10;&lt;p&gt;You can run a single node CoreOS deployment and manage containers via the tools that you know and love.&#10;For example, &lt;a href="https://github.com/docker/compose"&gt;docker-compose&lt;/a&gt; works great on CoreOS.&#10;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;&#10;&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;&#10;&lt;h1 id="my-development-tools-are-missing"&gt;My development tools are missing!&lt;/h1&gt;&#10;&lt;p&gt;😱 No &lt;code&gt;vim&lt;/code&gt;?&#10;This is too minimal!&#10;&lt;strong&gt;What are we going to do?&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Luckily CoreOS comes with &lt;a href="https://github.com/containers/toolbox"&gt;toolbox&lt;/a&gt;. 🧰&lt;/p&gt;&#10;&lt;p&gt;Toolbox gives you the ability to run a utility container on the system with some handy benefits:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&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;&#10;&lt;/blockquote&gt;&#10;&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.&#10;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.&#10;Need a different version, such as Fedora Rawhide?&#10;Just specify the Fedora release you want on the prompt:&lt;/p&gt;&#10;&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; &#10;&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&#10;&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.&#10;&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&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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&#10;&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.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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.&#10;&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;.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&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 ~]$ &#10;&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.&#10;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;&#10;&lt;h1 id="my-tool-wont-work-in-the-toolbox"&gt;My tool won&amp;rsquo;t work in the toolbox.&lt;/h1&gt;&#10;&lt;p&gt;Some applications have issues running inside a container, even one that has elevated privileges on the system.&#10;CoreOS offers an option for layering packages on top of the underlying immutable OS.&lt;/p&gt;&#10;&lt;p&gt;Simply run &lt;code&gt;rpm-ostree install PACKAGE&lt;/code&gt; to layer a package on top of the OS.&#10;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.&#10;That means that you need to reboot before you can use the package.&lt;/p&gt;&#10;&lt;p&gt;Don&amp;rsquo;t want to reboot?&#10;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;&#10;&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.&#10;Installing a package like &lt;a href="https://github.com/traviscross/mtr"&gt;mtr&lt;/a&gt; would look like this:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;$ sudo rpm-ostree install --apply-live mtr&#10;&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;&#10;&lt;h1 id="how-do-updates-work"&gt;How do updates work?&lt;/h1&gt;&#10;&lt;p&gt;There are two main technologies at work here.&lt;/p&gt;&#10;&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.&#10;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;&#10;&lt;p&gt;Second, &lt;code&gt;rpm-ostree&lt;/code&gt; handles the OS layers and switches between them at boot time.&#10;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.&#10;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.&#10;In these situations, your downtime might be extended a bit due to two reboots.&#10;Your system will come back up with the original OS layer activated.&lt;/p&gt;&#10;&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;.&#10;Want to live a bit more on the edge?&#10;Go for &lt;em&gt;next&lt;/em&gt; or &lt;em&gt;testing&lt;/em&gt;.&#10;You&amp;rsquo;re on the &lt;em&gt;stable&lt;/em&gt; stream by default.&lt;/p&gt;&#10;&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.&#10;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;&#10;&lt;h1 id="i-have-more-questions"&gt;I have more questions.&lt;/h1&gt;&#10;&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;.&#10;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;&#10;&lt;p&gt;Love the idea of an immutable OS but you wish you had it for your desktop or laptop?&#10;Go check out &lt;a href="https://fedoraproject.org/silverblue/"&gt;Fedora Silverblue&lt;/a&gt;. 💻&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Okay, so it&amp;rsquo;s &lt;em&gt;mostly&lt;/em&gt; immutable.&#10;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.&#10;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;&#10;&lt;/li&gt;&#10;&lt;li id="fn:2"&gt;&#10;&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.&#10;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;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Automatic container updates with watchtower</title><link>https://major.io/p/watchtower/</link><pubDate>Wed, 04 Jan 2023 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/watchtower/</guid><description>&lt;p&gt;Keeping things updated quickly becomes a monotonous task.&#10;I&amp;rsquo;m surrounded by devices that demand updates on different frequencies.&#10;Phones, computers, tables, cloud instances, containers, and even my car need constant attention for updates that improve security or fix bugs.&#10;&lt;em&gt;(Sometimes the updates cause bugs, but let&amp;rsquo;s forget about those for now)&lt;/em&gt; 😉&lt;/p&gt;&#10;&lt;p&gt;My container infrastructure runs on &lt;a href="https://getfedora.org/en/coreos"&gt;Fedora CoreOS&lt;/a&gt; and it updates itself.&#10;It has an immutable layer underneath my containers that updates using &lt;a href="https://ostreedev.github.io/ostree/"&gt;ostree&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;However, keeping containers updated is a constant battle.&#10;Updating the containers themselves is fairly easy with a &lt;code&gt;podman pull&lt;/code&gt; or &lt;code&gt;docker pull&lt;/code&gt; followed by a stop and start.&#10;It&amp;rsquo;s a bit easier with &lt;code&gt;docker-compose&lt;/code&gt;, but it&amp;rsquo;s still a nuisance to remember to update.&lt;/p&gt;&#10;&lt;h1 id="enter-watchtower"&gt;Enter watchtower&lt;/h1&gt;&#10;&lt;p&gt;&lt;a href="https://containrrr.dev/watchtower/"&gt;Watchtower&lt;/a&gt; is incredibly handy and surprisingly simple to operate.&#10;At a high level, it reads the container tags from each running container and watches the upstream repositories for updates.&#10;When an updated container appears, watchtower springs into action, pulls the new container, and replaces the old container with the new one.&lt;/p&gt;&#10;&lt;p&gt;It accepts &lt;a href="https://containrrr.dev/watchtower/arguments/"&gt;arguments&lt;/a&gt; to configure all kinds of aspects of updating containers.&#10;You can exclude certain containers from updates, choose your update interval, and send notifications when updates occur.&lt;/p&gt;&#10;&lt;p&gt;Also, if manually updating containers is something you find fascinating, watchtower can notify you about updates.&#10;Then you get that great feeling of running lots of commands on your own.&#10;&lt;em&gt;(Wait, surely nobody likes that!)&lt;/em&gt;&lt;/p&gt;&#10;&lt;h1 id="deploy"&gt;Deploy&lt;/h1&gt;&#10;&lt;p&gt;There are plenty of configuration snippets in watchtower&amp;rsquo;s documentation, but you can start off with something as simple with this in your &lt;code&gt;docker-compose.yaml&lt;/code&gt;:&lt;/p&gt;&#10;&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;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;watchtower&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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/containrrr/watchtower&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;always&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;privileged&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;My configuration on my &lt;a href="https://major.io/p/self-hosted-mastodon-second-try/"&gt;mastodon deployment&lt;/a&gt; looks something like this:&lt;/p&gt;&#10;&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;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;watchtower&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;watchtower&lt;/span&gt;&lt;span class="w"&gt;&#10;&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/containrrr/watchtower&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;watchtower.tootchute.com&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;WATCHTOWER_CLEANUP=true&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;WATCHTOWER_POLL_INTERVAL=86400&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;WATCHTOWER_NOTIFICATIONS=shoutrrr&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;WATCHTOWER_NOTIFICATION_URL=discord://DISCORD_WEBHOOK_KEY@DISCORD_CHANNEL_ID&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;always&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;privileged&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This configuration sends me notifications via my Discord server whenever watchtower starts or when a container is updated.&lt;/p&gt;&#10;&lt;p&gt;The &lt;code&gt;docker.sock&lt;/code&gt; volume mount allows watchtower to interact with the container daemon underneath watchtower.&#10;This could easily be done with &lt;code&gt;docker&lt;/code&gt;, &lt;code&gt;moby-engine&lt;/code&gt;, or &lt;code&gt;podman&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;d like to remove the &lt;code&gt;privileged&lt;/code&gt; setting at some point soon but I haven&amp;rsquo;t figured out a way to allow watchtower to talk to the &lt;code&gt;docker.sock&lt;/code&gt; without it. 🤔&lt;/p&gt;&#10;&lt;h1 id="further-considerations"&gt;Further considerations&lt;/h1&gt;&#10;&lt;p&gt;If you don&amp;rsquo;t trust the upstream where you download your containers, be careful using watchtower.&#10;A malicious container could be uploaded to a particular container image repository and your system might update itself to the malicious container before the malicious container is found.&#10;&lt;em&gt;Then again, if you don&amp;rsquo;t trust the upstream where you download your containers, you should be building these containers yourself.&lt;/em&gt; 😉&lt;/p&gt;&#10;&lt;p&gt;For more complex services that might need some extra care around updates, such as database services, you may want to exclude them from automatic updates.&#10;You can run &lt;a href="https://containrrr.dev/watchtower/running-multiple-instances/"&gt;multiple instances&lt;/a&gt; of watchtower with customized configurations for different sets of containers.&lt;/p&gt;&#10;</description></item><item><title>docker-compose on Fedora CoreOS</title><link>https://major.io/p/docker-compose-on-coreos/</link><pubDate>Sat, 17 Dec 2022 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/docker-compose-on-coreos/</guid><description>&lt;p&gt;Deploying applications in containers provides lots of flexibility and compatibility benefits.&lt;/p&gt;&#10;&lt;p&gt;Once you package your application and its dependencies in a container, that container runs almost anywhere without issues.&#10;Very few of the old &amp;ldquo;it worked on my machine!&amp;rdquo; problems remain.&#10;However, the challenge of running a container and linking it up with other helpful pieces of software still remains.&lt;/p&gt;&#10;&lt;p&gt;Web applications need something to serve HTTP requests and handle TLS.&#10;They also need databases, and those databases must be online and available first.&#10;All of these need reliable storage that is easily managed.&lt;/p&gt;&#10;&lt;p&gt;In my personal infrastructure, I keep coming back to &lt;a href="https://github.com/docker/compose"&gt;docker-compose&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;🐇 &lt;strong&gt;In a hurry?&lt;/strong&gt;&#10;Skip to the last section of this post if you want to skip my reasons for using docker-compose and you just want to see the steps.&lt;/p&gt;&#10;&lt;h1 id="why-docker-compose"&gt;Why docker-compose?&lt;/h1&gt;&#10;&lt;p&gt;The tried-and-true docker-compose is one of the original &amp;ldquo;set your desired state&amp;rdquo; systems for managing containers.&#10;You specify what your container deployment should look like and docker-compose finds a way to get your containers in order.&#10;Sometimes that&amp;rsquo;s a fresh start without any existing containers.&#10;Sometimes it involves managing an existing fleet of containers and adjusting their configuration.&lt;/p&gt;&#10;&lt;p&gt;As an example, deploying your first container with docker-compose is easy.&#10;Assemble a &lt;a href="https://compose-spec.io/"&gt;basic YAML file&lt;/a&gt;, point docker-compose at it, and your container is running!&lt;/p&gt;&#10;&lt;p&gt;Need to change the configuration?&#10;Just make your changes in the YAML file, re-run docker-compose, and it knows enough to make the right changes.&#10;If containers need to be restarted, it takes care of that.&lt;/p&gt;&#10;&lt;h2 id="why-not-kubernetes"&gt;Why not kubernetes?&lt;/h2&gt;&#10;&lt;p&gt;I&amp;rsquo;ve run my own kubernetes deployment and I&amp;rsquo;ve also been a consumer of large kubernetes and OpenShift deployments.&#10;All of this experience taught me two things:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Kubernetes and OpenShift are great.&#10;Like really great.&#10;Once you learn them, they are incredibly powerful tools that make a developer&amp;rsquo;s life much easier.&lt;/li&gt;&#10;&lt;li&gt;Running my own kubernetes or OpenShift deployment on my own time (and own dime) is not enjoyable.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Deploying, maintaining, and troubleshooting kubernetes infrastructure on your own is time consuming.&#10;Shared storage and networking caused me the most headaches in the past.&lt;/p&gt;&#10;&lt;h2 id="what-about-k3s"&gt;What about k3s?&lt;/h2&gt;&#10;&lt;p&gt;I love &lt;a href="https://k3s.io/"&gt;k3s&lt;/a&gt;.&#10;However, that still means I have to figure out networking for inter-container communication and load balancing.&#10;Shared storage is also needed.&lt;/p&gt;&#10;&lt;p&gt;I could argue that running my own k3s deployment is easier than full kubernetes, but in the end, there&amp;rsquo;s more extra stuff around it that I don&amp;rsquo;t want to maintain.&lt;/p&gt;&#10;&lt;h2 id="why-dont-you-use-managed-kubernetes"&gt;Why don&amp;rsquo;t you use managed kubernetes?&lt;/h2&gt;&#10;&lt;p&gt;Great question!&#10;Several providers have some excellent kubernetes offerings out there.&#10;Smaller providers, such as &lt;a href="https://www.digitalocean.com/products/kubernetes"&gt;Digital Ocean&lt;/a&gt; and &lt;a href="https://www.vultr.com/kubernetes/"&gt;VULTR&lt;/a&gt;, have affordable offerings that are packed with features.&lt;/p&gt;&#10;&lt;p&gt;The challenge is that kubernetes deployments have overhead for the control plane, so you can&amp;rsquo;t use all of the virtual machines that you rent.&#10;For example, you may get three virtual machines in your cluster, each with 2GB RAM, but you can really only use about 1GB of RAM from each instance for your containers.&lt;/p&gt;&#10;&lt;p&gt;In addition, you will eventually need shared storage and some type of load balancer.&#10;The costs add up quickly.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s easy to start with a $50-$70 kubernetes offering and later find yourself cracking $100 per month after adding on storage and load balancers.&lt;/p&gt;&#10;&lt;h2 id="what-about-podman"&gt;What about podman?&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://podman.io/"&gt;Podman&lt;/a&gt; is a delight to use.&#10;You can toss some kubernetes YAML at it for deployments or pods and it will start them up for you.&#10;It also has a handy feature for exporting a container to a systemd unit file so you can manage it like any other systemd unit.&lt;/p&gt;&#10;&lt;p&gt;However, when I want to make quick adjustments to a container configuration, it can be frustrating to get that done.&#10;I also like to make adjustments to all of my containers in one place since some applications depend on multiple containers running in tandem.&lt;/p&gt;&#10;&lt;p&gt;The &lt;a href="https://github.com/containers/podman-compose"&gt;podman-compose&lt;/a&gt; project helps quite a bit, but it still lacks some of docker-compose&amp;rsquo;s features.&lt;/p&gt;&#10;&lt;p&gt;I use podman &lt;em&gt;constantly&lt;/em&gt; on my laptop and desktop for development, testing, and toolbox containers.&lt;/p&gt;&#10;&lt;h1 id="whats-the-big-deal-about-coreos"&gt;What&amp;rsquo;s the big deal about CoreOS?&lt;/h1&gt;&#10;&lt;p&gt;&lt;a href="https://getfedora.org/en/coreos"&gt;Fedora CoreOS&lt;/a&gt; provides the foundation for my container infrastructure.&#10;I love Fedora already, but here&amp;rsquo;s what makes CoreOS special to me:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Automatic updates arrive via &lt;a href="https://ostreedev.github.io/ostree/"&gt;ostree&lt;/a&gt; as a fully tested minimal unit&lt;/li&gt;&#10;&lt;li&gt;My system reboots automatically to apply the updates and it reverts back to the previous working update if the update fails&lt;/li&gt;&#10;&lt;li&gt;It comes with all of the container tools and configurations that I need&lt;/li&gt;&#10;&lt;li&gt;Whenever I need development or troubleshooting tools, &lt;a href="https://github.com/containers/toolbox"&gt;toolbox containers&lt;/a&gt; are one step away&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;It&amp;rsquo;s a constantly-updated OS that is designed for containers.&#10;What could be better than that?&lt;/p&gt;&#10;&lt;h1 id="adding-docker-compose-to-coreos"&gt;Adding docker-compose to CoreOS&lt;/h1&gt;&#10;&lt;p&gt;Deploy CoreOS in your favorite cloud or on your favorite piece of hardware (I have &lt;a href="https://major.io/2021/08/20/deploy-fedora-coreos-in-hetzner-cloud/"&gt;a post&lt;/a&gt; about deploying it on &lt;a href="https://www.hetzner.com/cloud"&gt;Hetzner Cloud&lt;/a&gt;).&#10;Login as the &lt;code&gt;core&lt;/code&gt; user via ssh.&lt;/p&gt;&#10;&lt;p&gt;Go to the &lt;a href="https://github.com/docker/compose/releases"&gt;releases page&lt;/a&gt; for docker-compose and get the latest release for your architecture.&#10;Move it into place once you download it:&lt;/p&gt;&#10;&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; curl -sLO https://github.com/docker/compose/releases/download/v2.14.1/docker-compose-linux-x86_64&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo chmod +x /usr/local/bin/docker-compose&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s add a really basic deployment of traefik&amp;rsquo;s &lt;a href="https://hub.docker.com/r/traefik/whoami"&gt;whoami&lt;/a&gt; container.&#10;Start a new file called &lt;code&gt;docker-compose.yaml&lt;/code&gt;:&lt;/p&gt;&#10;&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;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;whoami&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;whoami&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;docker.io/traefik/whoami&lt;/span&gt;&lt;span class="w"&gt;&#10;&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;&#10;&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;&#10;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we try to bring up our containers now, we get an error:&lt;/p&gt;&#10;&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; docker-compose up -d&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get &amp;#34;http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json?all=1&amp;amp;filters=%7B%22label%22%3A%7B%22com.docker.compose.project%3Dcore%22%3Atrue%7D%7D&amp;#34;: dial unix /var/run/docker.sock: connect: permission denied&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;core&lt;/code&gt; user is not in the &lt;code&gt;docker&lt;/code&gt; group and does not have permissions to talk to the docker socket.&lt;/p&gt;&#10;&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; id&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;uid=1000(core) gid=1000(core) groups=1000(core),4(adm),10(wheel),16(sudo),190(systemd-journal) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add the &lt;code&gt;docker&lt;/code&gt; group to the &lt;code&gt;core&lt;/code&gt; user as a supplementary group:&lt;/p&gt;&#10;&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 usermod -a -G docker core&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Log out of your ssh session and log in again to pick up the new group.&#10;Start the containers once more:&lt;/p&gt;&#10;&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; id&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;uid=1000(core) gid=1000(core) groups=1000(core),4(adm),10(wheel),16(sudo),190(systemd-journal),982(docker) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023&#10;&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&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;[+] Running 4/4&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; ⠿ whoami Pulled&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; ⠿ 029cd1bf7e7c Pull complete&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; ⠿ e73b694ead4f Pull complete&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; ⠿ 99df6e9e9886 Pull complete&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;[+] Running 2/2&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; ⠿ Network core_default Created&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; ⠿ Container whoami Started&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Success!&lt;/p&gt;&#10;&lt;p&gt;🤔 &lt;strong&gt;BUT WAIT!&lt;/strong&gt;&#10;If we reboot our system, the containers won&amp;rsquo;t start up at boot time!&lt;/p&gt;&#10;&lt;p&gt;On CoreOS, there&amp;rsquo;s a &lt;code&gt;docker.socket&lt;/code&gt; file that starts the actual docker service (which is actually &lt;a href="https://mobyproject.org/"&gt;moby-engine&lt;/a&gt; on Fedora and not full-fledged docker).&#10;As soon as something touches the socket, systemd starts the docker service.&#10;That keeps it out of the way until something actually asks to use it.&lt;/p&gt;&#10;&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 status docker.socket&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;● docker.socket - Docker Socket for the API&#10;&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/system/docker.socket; enabled; preset: enabled)&#10;&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 Sat 2022-12-17 22:48:42 UTC; 4min 47s ago&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Until: Sat 2022-12-17 22:48:42 UTC; 4min 47s ago&#10;&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: ● docker.service&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Listen: /run/docker.sock (Stream)&#10;&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: 0 (limit: 2207)&#10;&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: 0B&#10;&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: 596us&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; CGroup: /system.slice/docker.socket&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After a reboot, nothing pokes the socket and the associated service never starts.&#10;You can try it yourself!&#10;Reboot and the whoami container will be down.&#10;Run &lt;code&gt;docker-compose ps&lt;/code&gt; one time and suddenly your containers are running!&lt;/p&gt;&#10;&lt;p&gt;Let&amp;rsquo;s fix this so containers come up on boot without any extra work (or socket poking):&lt;/p&gt;&#10;&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 systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; --now docker.service&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The socket will still be handled by systemd, but now the docker service itself will always start at boot whether someone touched it or not.&lt;/p&gt;&#10;&lt;p&gt;Enjoy your new lightweight container infrastructure. ✨&lt;/p&gt;&#10;</description></item><item><title>Deploy Fedora CoreOS in Hetzner cloud</title><link>https://major.io/p/deploy-fedora-coreos-in-hetzner-cloud/</link><pubDate>Fri, 20 Aug 2021 00:00:00 +0000</pubDate><author>major@mhtx.net (Major Hayden)</author><guid>https://major.io/p/deploy-fedora-coreos-in-hetzner-cloud/</guid><description>&lt;p&gt;&lt;a href="https://www.hetzner.com/"&gt;Hetzner&lt;/a&gt; has always been a reliable and cost-effective hosting company for me&#10;for several years. I&amp;rsquo;ve run &lt;a href="https://icanhazip.com"&gt;icanhazip.com&lt;/a&gt; on their dedicated servers and I run&#10;several small applications in &lt;a href="https://www.hetzner.com/cloud"&gt;their cloud&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;When I run containers, I love using &lt;a href="https://getfedora.org/en/coreos?stream=stable"&gt;Fedora CoreOS&lt;/a&gt; for its easy updates and&#10;very small server footprint. Almost everything you need for hosting containers&#10;is provided right out of the box, but you can add extra packages via&#10;&lt;a href="https://coreos.github.io/rpm-ostree/"&gt;rpm-ostree&lt;/a&gt; layers and reboot to use them.&lt;/p&gt;&#10;&lt;p&gt;This post shows you how to deploy Fedora CoreOS in Hetzner&amp;rsquo;s cloud as an&#10;inexpensive and efficient method for hosting your container workloads.&lt;/p&gt;&#10;&lt;h2 id="roadblocks"&gt;Roadblocks&lt;/h2&gt;&#10;&lt;p&gt;First off, Hetzner does not offer Fedora CoreOS as one of its cloud base images.&#10;That normally wouldn&amp;rsquo;t be a problem, but they don&amp;rsquo;t allow you to upload&#10;snapshots or base images, either. Don&amp;rsquo;t worry! We can think creatively and&#10;deploy the image via the cloud rescue environment.&lt;/p&gt;&#10;&lt;p&gt;Another challenge is that &lt;a href="https://github.com/coreos/ignition"&gt;ignition&lt;/a&gt;, the CoreOS first boot configuration tool,&#10;does not support Hetzner&amp;rsquo;s metadata service at this time. I&amp;rsquo;m working on a &lt;a href="https://github.com/coreos/ignition/pull/1262"&gt;pull&#10;request&lt;/a&gt; to add this support. We can get creative here and embed our ignition&#10;configuration inside the instance itself. That&amp;rsquo;s a bit annoying since all of&#10;your instances will get the same configuration, but that works out fine for my&#10;needs since I mainly care about ssh keys being present.&lt;/p&gt;&#10;&lt;h2 id="generating-ignition-configuration"&gt;Generating ignition configuration&lt;/h2&gt;&#10;&lt;p&gt;The ignition configuration allows us to specify ssh keys (and many other&#10;possible configurations) for the first boot. First, we start with a really basic&#10;&lt;a href="https://coreos.github.io/butane/examples/"&gt;butane configuration&lt;/a&gt;:&lt;/p&gt;&#10;&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="c"&gt;# hetzner-coreos.butane&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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;&#10;&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-rsa AAAAB3NzaC1y...&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can use any username here that you prefer and add your public ssh key.&#10;Compile the configuration into an ignition file with butane:&lt;/p&gt;&#10;&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;butane hetzner-coreos.butane &amp;gt; config.ign&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Keep this &lt;code&gt;config.ign&lt;/code&gt; file handy because we need it before we snapshot our&#10;Fedora CoreOS image later.&lt;/p&gt;&#10;&lt;h2 id="prepare-the-coreos-snapshot"&gt;Prepare the CoreOS snapshot&lt;/h2&gt;&#10;&lt;p&gt;Although Hetzner doesn&amp;rsquo;t allow for uploading your own snapshot, you can replace&#10;the entire root disk of an instance from the rescue system. From there, we can&#10;snapshot the root disk storage and use that as our new base image.&lt;/p&gt;&#10;&lt;p&gt;Download and install the &lt;a href="https://github.com/hetznercloud/cli"&gt;hcloud&lt;/a&gt; tool first. Follow the installation&#10;instructions to generate an API key and store it on your system.&lt;/p&gt;&#10;&lt;p&gt;Start by building a basic Fedora 34 instance where we can deploy the Fedora&#10;CoreOS image:&lt;/p&gt;&#10;&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; hcloud server create --datacenter nbg1-dc3 --image fedora-34 &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; --type cpx11 --name coreos-deployer&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Activate the rescue system for the instance you just created and reboot into the&#10;rescue environment:&lt;/p&gt;&#10;&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; hcloud server enable-rescue coreos-deployer&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;1.148s [=================================] 100.00%&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Rescue enabled for server 14168423 with root password: xxxxx&#10;&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; hcloud server reboot coreos-deployer&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;656ms [==================================] 100.00%&#10;&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 14168423 rebooted&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Use ssh to log into the rescue environment (user: &lt;code&gt;root&lt;/code&gt;, password provided in&#10;the &lt;code&gt;enable-rescue&lt;/code&gt; step). The rescue environment is in a ramdisk and we don&amp;rsquo;t&#10;have enough space to build the &lt;a href="https://coreos.github.io/coreos-installer/"&gt;coreos-installer&lt;/a&gt; or download a raw disk, but we&#10;can get creative and stream the filesystem directly from a Fedora CoreOS image&#10;download.&lt;/p&gt;&#10;&lt;p&gt;I prefer to live dangerously and I run the testing release, but there are &lt;a href="https://getfedora.org/en/coreos/download?tab=cloud_launchable&amp;amp;stream=stable"&gt;three&#10;releases available for download&lt;/a&gt;. Download, decompress, and write the image to&#10;the root disk all at once:&lt;/p&gt;&#10;&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="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;COREOS_DISK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://builds.coreos.fedoraproject.org/prod/streams/testing/builds/34.20210821.2.0/x86_64/fedora-coreos-34.20210821.2.0-metal.x86_64.raw.xz&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sL &lt;span class="nv"&gt;$COREOS_DISK&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xz -d &lt;span class="p"&gt;|&lt;/span&gt; dd &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sda &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This process should take one or two minutes to complete.&lt;/p&gt;&#10;&lt;h2 id="embed-the-ignition-configuration"&gt;Embed the ignition configuration&lt;/h2&gt;&#10;&lt;p&gt;We could hop out of rescue now and reboot right into Fedora CoreOS, but we need&#10;to provide SSH keys for our instance. The &lt;code&gt;config.ign&lt;/code&gt; file we generated in the&#10;first section of this post will be deployed as &lt;code&gt;/ignition/config.ign&lt;/code&gt; in the&#10;image.&lt;/p&gt;&#10;&lt;p&gt;Let&amp;rsquo;s find the boot partition:&lt;/p&gt;&#10;&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; fdisk -l /dev/sda&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Disk /dev/sda: 38.2 GiB, 40961572864 bytes, 80003072 sectors&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Disk model: QEMU HARDDISK&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Units: sectors of 1 * 512 = 512 bytes&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Sector size (logical/physical): 512 bytes / 512 bytes&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;I/O size (minimum/optimal): 512 bytes / 512 bytes&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Disklabel type: gpt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Disk identifier: 00000000-0000-4000-A000-000000000001&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Device Start End Sectors Size Type&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;/dev/sda1 2048 4095 2048 1M BIOS boot&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;/dev/sda2 4096 264191 260096 127M EFI System&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;/dev/sda3 264192 1050623 786432 384M Linux filesystem « boot&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;/dev/sda4 1050624 5003230 3952607 1.9G Linux filesystem « root&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mount the boot partition and deploy the &lt;code&gt;config.ign&lt;/code&gt;:&lt;/p&gt;&#10;&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;mount /dev/sda3 /mnt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;mkdir /mnt/ignition&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;vi /mnt/ignition/config.ign « copy/paste your config.ign from earlier&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;umount /mnt&#10;&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 power off the instance to avoid booting it. We want all subsequent boots&#10;to be as clean as possible.&lt;/p&gt;&#10;&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;root@rescue ~ # poweroff&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="make-the-snapshot"&gt;Make the snapshot&lt;/h2&gt;&#10;&lt;p&gt;Create a snapshot from our powered off server:&lt;/p&gt;&#10;&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;hcloud server create-image --description fedora-34-coreos \&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; --type snapshot coreos-deployer&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This usually takes 1-2 minutes. Let&amp;rsquo;s get our image ID:&lt;/p&gt;&#10;&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; hcloud image list &lt;span class="p"&gt;|&lt;/span&gt; grep fedora-34-coreos&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;46874212 snapshot - fedora-34-coreos 0.96 GB 40 GB Wed Sep 1 10:07:04 CDT 2021 -&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="boot-the-instance"&gt;Boot the instance&lt;/h2&gt;&#10;&lt;p&gt;Now that we have a snapshot with our ignition configuration embedded in it,&#10;let&amp;rsquo;s make a new instance!&lt;/p&gt;&#10;&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;hcloud server create --datacenter nbg1-dc3 --image 46874212 --type cpx11 \&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; --ssh-key personal_servers --name first-coreos-instance&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hetzner normally boots cloud images &lt;strong&gt;really quickly&lt;/strong&gt;, but it takes a bit&#10;longer when booting from snapshots. I assume that they have the common base&#10;images cached on most hypervisors so they can provision them really quickly. The&#10;delay isn&amp;rsquo;t too bad here: the instances usually take about 90 seconds to boot.&lt;/p&gt;&#10;&lt;p&gt;Once it boots, you should see notes from ignition on bootup about your&#10;configuration:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="2021-09-01-fedora-coreos-console.png" alt="2021-09-01-fedora-coreos-console.png" title="Fedora CoreOS console"&gt;&lt;/p&gt;&#10;&lt;p&gt;Use ssh to login as the &lt;code&gt;core&lt;/code&gt; user:&lt;/p&gt;&#10;&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 core@INSTANCE_IP_ADDRESS&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Fedora CoreOS 34.20210821.2.0&#10;&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&#10;&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/c/server/coreos/&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;[core@localhost ~]$&lt;/span&gt; podman --version&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;podman version 3.3.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;&#10;&lt;p&gt;Now that you have a snapshot made, you can delete your original instance (I&#10;called it &lt;code&gt;coreos-deployer&lt;/code&gt; above) and just build off that snapshot whenever you&#10;need to. That should save you a few Euros per month. 💸&lt;/p&gt;&#10;&lt;p&gt;Once ignition has support for Hetzner&amp;rsquo;s metadata service, the extra step of&#10;embedding your configuration won&amp;rsquo;t be needed.&lt;/p&gt;&#10;&lt;p&gt;Also, if anyone from Hetzner is reading this post, I&amp;rsquo;d love to get Fedora CoreOS&#10;as one of the options for base images in your cloud! 🤗&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Photo credit: &lt;a href="https://unsplash.com/photos/K3fQgNfKUoc"&gt;Daniel Seßler on Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&#10;</description></item></channel></rss>