<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Caius&#x27; Experiments</title>
      <link>https://caius.dev/</link>
      <description></description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://caius.dev/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Sat, 28 Mar 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Why I Self Host</title>
          <pubDate>Sat, 28 Mar 2026 00:00:00 +0000</pubDate>
          <author>Caius Brindescu</author>
          <link>https://caius.dev/blog/why-i-self-host/</link>
          <guid>https://caius.dev/blog/why-i-self-host/</guid>
          <description xml:base="https://caius.dev/blog/why-i-self-host/">&lt;p&gt;I have been running my own servers, on and off since college.
They have been a great experimentation platform, and given me resources to tap into for my professional life.
It wasn&#x27;t always smooth sailing, but here&#x27;s some of my takes, and why it&#x27;s always been worth the hassle, at least for me.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;a-short-history&quot;&gt;A short history&lt;a class=&quot;post-anchor&quot; href=&quot;#a-short-history&quot; aria-label=&quot;Anchor link for: a-short-history&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;the-early-days&quot;&gt;The early days&lt;a class=&quot;post-anchor&quot; href=&quot;#the-early-days&quot; aria-label=&quot;Anchor link for: the-early-days&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;The first  &quot;server&quot; I built was during college when I took an old desktop, put Linux on it, and used it mostly as a network file store.
No redundancy, but it was fun.&lt;&#x2F;p&gt;
&lt;p&gt;Later on during grad school, I had to setup the research group server&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.
Using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;account.dyn.com&#x2F;&quot;&gt;DynDNS&lt;&#x2F;a&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, it was publicly accessible.
That worked fine for a while, until a vulnerable Apache module turned it into a spam bot.
I only found out when I got an annoyed email from a sysadmin in Bucharest, Romania, that it was spamming their network.
Took it down for the day, cleaned it up, and made a note to apply patches more often in the future.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;today&quot;&gt;Today&lt;a class=&quot;post-anchor&quot; href=&quot;#today&quot; aria-label=&quot;Anchor link for: today&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Currently, I am running a Pi Cluster with 4 Raspberry Pi 4 Nodes, with 4 GB each, running &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;k3s.io&#x2F;&quot;&gt;K3s&lt;&#x2F;a&gt;.
While not a very powerful cluster, and only using SD Card for storage, it&#x27;s enough to serve a static website, grafana, prometheus, and any random experiments.
I also have a 1 node Framework Desktop that will run more &quot;critical&quot; systems, like a Postgres database for services that need it.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;why&quot;&gt;Why?&lt;a class=&quot;post-anchor&quot; href=&quot;#why&quot; aria-label=&quot;Anchor link for: why&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;p&gt;A lot of the services I run (or plan to) are usually available for free (or a reasonable subscription) else where.
You can host a website cheaply on AWS with S3 and Cloudfront (assuming low traffic).
You can host your 3D prints on Printables and the like, and I do.
But there are a few advantages to having control of where your data ends up.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;learning&quot;&gt;Learning&lt;a class=&quot;post-anchor&quot; href=&quot;#learning&quot; aria-label=&quot;Anchor link for: learning&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;This has been the primary reason for this. The Pi Cluster in particular was a great learning experience. Testing a repeatable Ansible set of playbooks, getting K3S to work and deploying a few services using both plain Kubernetes templates and Helm is not something I get to do in my day job.&lt;&#x2F;p&gt;
&lt;p&gt;I find that I learn best when working on something practical. This is true from programming languages to infrastructure and networking. Having a &quot;disposable&quot; setup makes this a lot easier when learning.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enshittification&quot;&gt;Enshittification&lt;a class=&quot;post-anchor&quot; href=&quot;#enshittification&quot; aria-label=&quot;Anchor link for: enshittification&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s an unfortunate reality of today&#x27;s world.
Free services get more restrictive, open source alternatives get yanked&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, or the prices get constantly increased, with &quot;AI&quot; as the excuse&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#4&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Having your own services protects from this reality, and you don&#x27;t need to worry as much about increased costs, lost access, etc.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;private-access&quot;&gt;Private access&lt;a class=&quot;post-anchor&quot; href=&quot;#private-access&quot; aria-label=&quot;Anchor link for: private-access&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;From the grad school days, I&#x27;ve learned to be wary when exposing anything over the internet.
To this end, the cluster is currently private and I access it from anywhere using Tailscale.
The free tier&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#5&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; is enough for me and my partner, and we can access the services from anywhere.
Of course, locally we can always use the local network.&lt;&#x2F;p&gt;
&lt;p&gt;Using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cert-manager.io&#x2F;&quot;&gt;Cert Manager&lt;&#x2F;a&gt;,
I have publicly valid certificates and DNS that points to the Tailscale IP address of a node in the cluster.
This works great, and we don&#x27;t get any annoying browser warnings.
Making everything public is only a matter of updating the DNS records.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;your-data-your-rules&quot;&gt;Your data, your rules&lt;a class=&quot;post-anchor&quot; href=&quot;#your-data-your-rules&quot; aria-label=&quot;Anchor link for: your-data-your-rules&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Keeping the data private means it&#x27;s not going to be used to train AI models, and target ads at you.
I&#x27;d rather have my data truly private, not private with an asterisk and a disclaimer, as the service can still read it but promises to play nice.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-downsides&quot;&gt;The downsides&lt;a class=&quot;post-anchor&quot; href=&quot;#the-downsides&quot; aria-label=&quot;Anchor link for: the-downsides&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;reliability&quot;&gt;Reliability&lt;a class=&quot;post-anchor&quot; href=&quot;#reliability&quot; aria-label=&quot;Anchor link for: reliability&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;My setup is only as reliable as my internet and power are.
A UPS helps with the power, but no backup to the internet connection.
If Comcast is being its usual self, we&#x27;ll get a few periods of downtime a week.
For now this is not an issue, as the total downtime is less than 10 minutes a week, and mostly at night.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;data-safety&quot;&gt;Data safety&lt;a class=&quot;post-anchor&quot; href=&quot;#data-safety&quot; aria-label=&quot;Anchor link for: data-safety&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;If anything were to happen to the hardware, well, the data is gone.
That is solved by having automatic backups to S3&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#6&quot;&gt;6&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, and having a good &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.backblaze.com&#x2F;blog&#x2F;the-3-2-1-backup-strategy&#x2F;&quot;&gt;3-2-1 backup policy&lt;&#x2F;a&gt; is critical.&lt;&#x2F;p&gt;
&lt;p&gt;Also, test your backups regularly.
You don&#x27;t want to find that your backups are broken when you really need them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;cost&quot;&gt;Cost&lt;a class=&quot;post-anchor&quot; href=&quot;#cost&quot; aria-label=&quot;Anchor link for: cost&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;In particular, the upfront costs of getting the hardware.
This is only getting more expensive these days&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#7&quot;&gt;7&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.
However, if you&#x27;ve got some old hardware lying around, or go the used option, it&#x27;s going to be less of a concern.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;security&quot;&gt;Security&lt;a class=&quot;post-anchor&quot; href=&quot;#security&quot; aria-label=&quot;Anchor link for: security&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;If the spambot incident taught me anything, it&#x27;s that if it&#x27;s open to the internet it&#x27;s a target.
Data leaks could be more serious if it has private data, and you&#x27;re the only one in charge and responsible for securing it.
Keeping up to date with patches becomes even more critical the more sensitive the stored data.
A solution like Tailscale sidesteps the issue by keeping everything private.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;post-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;p&gt;Is this for everyone?
No. But if you want to learn, or care about where data ends up living, this is something worth looking into.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;footnotes&quot;&gt;Footnotes&lt;a class=&quot;post-anchor&quot; href=&quot;#footnotes&quot; aria-label=&quot;Anchor link for: footnotes&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;In our living room, because the university policies for a &quot;hard-wired&quot; server were beyond draconian, and made the hardware useless for the research we were doing&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;2&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;It was free in those days&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;3&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;selfhosted&#x2F;comments&#x2F;1r3zkg7&#x2F;minio_github_repository_officially_archived&#x2F;&quot;&gt;Minio archiving their open source repo&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;4&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=44225782&quot;&gt;Google Workspace price increase due to new AI features&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;5&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;5&lt;&#x2F;sup&gt;
&lt;p&gt;I am aware that this could enshittify in the future. However, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;headscale.net&#x2F;stable&#x2F;&quot;&gt;Headscale&lt;&#x2F;a&gt; is an option, so I&#x27;m confident there&#x27;s an offramp if I need it&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;6&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;6&lt;&#x2F;sup&gt;
&lt;p&gt;For data durability, I haven&#x27;t found anything that beats S3, or other key store offerings by a large cloud vendor.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;7&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;7&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theverge.com&#x2F;news&#x2F;839353&#x2F;pc-ram-shortage-pricing-spike-news&quot;&gt;RAM price hikes&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Self Hosted Postgres in Kubernetes with PITR Recovery</title>
          <pubDate>Wed, 31 Dec 2025 00:00:00 +0000</pubDate>
          <author>Caius Brindescu</author>
          <link>https://caius.dev/blog/self-hosted-postgres/</link>
          <guid>https://caius.dev/blog/self-hosted-postgres/</guid>
          <description xml:base="https://caius.dev/blog/self-hosted-postgres/">&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;a class=&quot;post-anchor&quot; href=&quot;#introduction&quot; aria-label=&quot;Anchor link for: introduction&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;p&gt;I would like to selfhost a few services, and they require persistence, in the form of a PostgreSQL database.
Since my Kubernetes cluster runs on 4 Raspberry Pi 4, each with an SD card for disk, it&#x27;s only a matter of time until one of them gets corrupted.
To avoid the innevitable and predictable data loss, I&#x27;ll need automated backups, idealy with Point-In-Time Recovery (PITR).&lt;&#x2F;p&gt;
&lt;p&gt;If I&#x27;m going to reinvent the wheel, why not go for the whole wheel?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;solution&quot;&gt;Solution&lt;a class=&quot;post-anchor&quot; href=&quot;#solution&quot; aria-label=&quot;Anchor link for: solution&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cloudnative-pg.io&#x2F;documentation&#x2F;current&#x2F;&quot;&gt;CloudNativePG&lt;&#x2F;a&gt; seems to be most mature and feature complete out there.
It provides an operator that does a lot of the heavy lifting.
Backups are handled by the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cloudnative-pg.io&#x2F;plugin-barman-cloud&#x2F;&quot;&gt;Barman Cloud&lt;&#x2F;a&gt; plugin.
It handles aspects like WAL Log archiving, taking regular snapshots and uploading them a cloud object storage, like S3&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.
Seems to meet all the needs I need for my project.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;setup&quot;&gt;Setup&lt;a class=&quot;post-anchor&quot; href=&quot;#setup&quot; aria-label=&quot;Anchor link for: setup&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;setting-up-a-cloud-native-pg-database&quot;&gt;Setting up a Cloud Native PG database&lt;a class=&quot;post-anchor&quot; href=&quot;#setting-up-a-cloud-native-pg-database&quot; aria-label=&quot;Anchor link for: setting-up-a-cloud-native-pg-database&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Using the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cloudnative-pg.io&#x2F;docs&#x2F;1.28&#x2F;installation_upgrade&quot;&gt;instructions on their website&lt;&#x2F;a&gt;, we&#x27;ll need to install the operator.
We can do it with the following command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; kubectl apply&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; --server-side -f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;cloudnative-pg&#x2F;cloudnative-pg&#x2F;release-1.28&#x2F;releases&#x2F;cnpg-1.28.0.yaml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can now create our database.
The Barman Cloud Plugin we&#x27;ll be using later assumes that the DB is in the &lt;code&gt;cnpg-system&lt;&#x2F;code&gt; namespace.
To simply this experiment, we&#x27;ll work with that assumption, and we&#x27;ll start by creating the namespace:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; kubectl create namespace cnpg-system&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then we can define our cluster configuration, with one user so we can connect to it later:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;apiVersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; v1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; kubernetes.io&#x2F;basic-auth&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; Secret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; postgres-secret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  namespace&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; cnpg-system&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;stringData&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  username&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; user1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  password&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; supersecretpassword&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;---&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;apiVersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; postgresql.cnpg.io&#x2F;v1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; Cluster&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; test&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  namespace&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; cpgn-system&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  instances&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  storage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    size&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; 1Gi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And when we apply this, we have a new cluster with 1 instance.
We can forward the port, and connect to it like we normally would to a Postgres instance running locally.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; kubectl port-forward&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; cnpg-system service&#x2F;test-restore-rw 5432:5432&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;backups&quot;&gt;Backups&lt;a class=&quot;post-anchor&quot; href=&quot;#backups&quot; aria-label=&quot;Anchor link for: backups&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;installing&quot;&gt;Installing&lt;a class=&quot;post-anchor&quot; href=&quot;#installing&quot; aria-label=&quot;Anchor link for: installing&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Now, we can configure backups for our new Postgres cluster.&lt;&#x2F;p&gt;
&lt;p&gt;First, let&#x27;s install the prerequisites that the Barman Cloud plugin requires.
We will need to install Certificate Manager&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; kubectl apply&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; https:&#x2F;&#x2F;github.com&#x2F;cert-manager&#x2F;cert-manager&#x2F;releases&#x2F;download&#x2F;v1.19.2&#x2F;cert-manager.yaml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then, we can install the plugin:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; kubectl apply&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; https:&#x2F;&#x2F;github.com&#x2F;cloudnative-pg&#x2F;plugin-barman-cloud&#x2F;releases&#x2F;download&#x2F;v0.9.0&#x2F;manifest.yaml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;configuration&quot;&gt;Configuration&lt;a class=&quot;post-anchor&quot; href=&quot;#configuration&quot; aria-label=&quot;Anchor link for: configuration&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;We can now define the backup configuration.
For this example, I&#x27;ll use S3 as the storage target for the backups.&lt;&#x2F;p&gt;
&lt;p&gt;First, we&#x27;ll need to store a set AWS credentials with access to the right bucket&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.
We&#x27;ll use an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;configuration&#x2F;secret&#x2F;#secret-types&quot;&gt;Opaque Secret&lt;&#x2F;a&gt; to store the access and secret keys:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;apiVersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; v1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; Secret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; s3-credentials&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  namespace&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; cnpg-system&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; Opaque&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;stringData&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  access-key-id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;lt;redacted&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  secret-key-id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;lt;redacted&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next, we&#x27;ll define the storage configuration to our S3 Bucket:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;apiVersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; barmancloud.cnpg.io&#x2F;v1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; ObjectStore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; s3-store&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  namespace&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; cnpg-system&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  configuration&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    destinationPath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; s3:&#x2F;&#x2F;&amp;lt;bucket&amp;gt;&#x2F;postgres&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    s3Credentials&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      accessKeyId&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; s3-credentials&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        key&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; access-key-id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      secretAccessKey&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; s3-credentials&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        key&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; secret-key-id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    wal&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      compression&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; gzip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, we&#x27;ll need to tell our Postgres cluster to use this configuration, and enable WAL archiving.
We&#x27;ll add the following to the Cluster &lt;code&gt;spec&lt;&#x2F;code&gt; field:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;plugins&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; barman-cloud.cloudnative-pg.io&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    isWALArchiver&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    parameters&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      barmanObjectName&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; s3-store&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The final piece of the puzzle is setup regular &quot;base&quot; backups.
These will backup the entire dataset, and give us a &quot;base&quot; for the WAL logs to be applied to in order to get our Point-In-Time Restore.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;apiVersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; postgresql.cnpg.io&#x2F;v1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; ScheduledBackup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; pg-backup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  namespace&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; cnpg-system&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  cluster&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; test&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  schedule&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;0 0 * * * *&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt; # hourly&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  backupOwnerReference&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; self&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  method&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; plugin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  pluginConfiguration&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; barman-cloud.cloudnative-pg.io&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;results&quot;&gt;Results&lt;a class=&quot;post-anchor&quot; href=&quot;#results&quot; aria-label=&quot;Anchor link for: results&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Now that everything is configured, we can list our S3 bucket, and sure enough, we have backups.
The top level structure has our base and WAL logs:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ aws s3 ls s3:&#x2F;&#x2F;&amp;lt;redacted&amp;gt;&#x2F;postgres&#x2F;test&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                           PRE base&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                           PRE wals&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Digging in deeper, we have our base backups, nicely named by timestamp, one every hour, as we&#x27;d expect:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ aws s3 ls s3:&#x2F;&#x2F;&amp;lt;redacted&amp;gt;&#x2F;postgres&#x2F;test&#x2F;base&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                           PRE 20251227T030000&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                           PRE 20251227T040000&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                           PRE 20251227T050000&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                           PRE 20251227T060000&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                           PRE 20251227T070000&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                           ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And finally, we have our WAL logs archived:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ aws s3 ls s3:&#x2F;&#x2F;&amp;lt;redacted&amp;gt;&#x2F;postgres&#x2F;test&#x2F;wals&#x2F;0000000100000000&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2025-12-26 21:00:02      16944 000000010000000000000013.gz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2025-12-26 21:00:04        210 000000010000000000000014.00000028.backup.gz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2025-12-26 21:00:03      16513 000000010000000000000014.gz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2025-12-26 21:05:03      17139 000000010000000000000015.gz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2025-12-26 21:30:03      17276 000000010000000000000016.gz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2025-12-26 22:00:02      16416 000000010000000000000017.gz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2025-12-26 22:00:05        209 000000010000000000000018.00000028.backup.gz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2025-12-26 22:00:04      16510 000000010000000000000018.gz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So we should have all the pieces needed to perform a PITR.
We&#x27;ll tacke this in the next section.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;restoring&quot;&gt;Restoring&lt;a class=&quot;post-anchor&quot; href=&quot;#restoring&quot; aria-label=&quot;Anchor link for: restoring&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;For testing the restore, I&#x27;ve created a simple table, with timestamps for easy reasoning:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;CREATE TABLE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  created_at &lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;timestamp&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And we inserted different values, and this is the end state of the table:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;test=&amp;gt; select * from test;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; id |         created_at         &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;----+----------------------------&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 16 | 2025-12-27 03:00:17.601392&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 17 | 2025-12-27 03:00:20.438822&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 18 | 2025-12-27 03:28:06.64914&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 19 | 2025-12-27 20:42:25.563207&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 20 | 2025-12-27 20:42:32.992066&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 21 | 2025-12-27 20:45:58.634257&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(6 rows)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ll restore the table to 2025-12-27, at 20:43:00 UTC.
For this, we&#x27;ll create a new cluster, and we&#x27;ll point it at the backups we have.
We&#x27;ll also need to give it the target time we want the cluster restored to.
We arrive at this configuration:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;apiVersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; postgresql.cnpg.io&#x2F;v1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; Cluster&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; test-restore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  namespace&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; cnpg-system&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  instances&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  imagePullPolicy&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; IfNotPresent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  bootstrap&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    recovery&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      source&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; source&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  externalClusters&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; source&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    plugin&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; barman-cloud.cloudnative-pg.io&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      parameters&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        barmanObjectName&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; s3-store&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        serverName&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; test&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        targetTime&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;2025-12-27T20:43:00Z&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  storage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    size&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; 1Gi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once the restore is done (it was pretty much instant for this example), we con connect and check our test table:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;test=&amp;gt; select * from test;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; id |         created_at         &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;----+----------------------------&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 16 | 2025-12-27 03:00:17.601392&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 17 | 2025-12-27 03:00:20.438822&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 18 | 2025-12-27 03:28:06.64914&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 19 | 2025-12-27 20:42:25.563207&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 20 | 2025-12-27 20:42:32.992066&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(5 rows)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As we&#x27;d expect, we&#x27;re missing row with &lt;code&gt;id&lt;&#x2F;code&gt; 21, as it&#x27;s &lt;em&gt;after&lt;&#x2F;em&gt; the target restore time.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusions-and-final-remarks&quot;&gt;Conclusions and Final Remarks&lt;a class=&quot;post-anchor&quot; href=&quot;#conclusions-and-final-remarks&quot; aria-label=&quot;Anchor link for: conclusions-and-final-remarks&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;p&gt;All in all, this was an easy setup, and it works fine for at least the basic use case.
The true test is once this sees some &quot;production&quot; loads, and testing with an actual live data base.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-note-on-s3-cots&quot;&gt;A note on S3 cots&lt;a class=&quot;post-anchor&quot; href=&quot;#a-note-on-s3-cots&quot; aria-label=&quot;Anchor link for: a-note-on-s3-cots&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;For this example, I used S3 as the backup target destination.
The amount of data stored is small, however, WAL archiving could end up writing a lot of objects.
This could incur significant S3 API charges, so it&#x27;s something I&#x27;m keeping an eye on.
With hourly base backups, I didn&#x27;t see any cost increases for my AWS account.
But inserting 21 records, and deleting a few is not exactly a representative use case, but at least the &quot;baseline&quot; cost is not absurd.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;retention-policies&quot;&gt;Retention policies&lt;a class=&quot;post-anchor&quot; href=&quot;#retention-policies&quot; aria-label=&quot;Anchor link for: retention-policies&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Barman has &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cloudnative-pg.io&#x2F;plugin-barman-cloud&#x2F;docs&#x2F;retention&#x2F;&quot;&gt;the option of specifying retention policies&lt;&#x2F;a&gt;.
However, for this experiment, I&#x27;ve gone with specifying a lifecycle policy on the S3 bucket.
Everything under &lt;code&gt;postgres&#x2F;&lt;&#x2F;code&gt; will be deleted after 7 days.
This will give me a one week recovery window.&lt;&#x2F;p&gt;
&lt;p&gt;Using the Barman retention policy will cause the plugin to list S3, and then delete the objects.
This also will incur some S3 API charges, and I think that using the S3 lifecycle rule is probably good enough.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;footnotes&quot;&gt;Footnotes&lt;a class=&quot;post-anchor&quot; href=&quot;#footnotes&quot; aria-label=&quot;Anchor link for: footnotes&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;The plugin also &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cloudnative-pg.io&#x2F;plugin-barman-cloud&#x2F;docs&#x2F;object_stores&#x2F;&quot;&gt;supports Google Cloud Storage, or Azure Blobs or some other services that implementation a compatible API&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;2&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;You can skip this step if your cluster already has it installed on the cluster. My test cluster did not.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;3&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;For reference, I granted the user full access to the S3 bucket where the backups are stored.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Setting up the DX Commander Expedition Antenna</title>
          <pubDate>Mon, 21 Apr 2025 00:00:00 +0000</pubDate>
          <author>Caius Brindescu</author>
          <link>https://caius.dev/blog/dx-commander-expedition/</link>
          <guid>https://caius.dev/blog/dx-commander-expedition/</guid>
          <description xml:base="https://caius.dev/blog/dx-commander-expedition/">&lt;p&gt;Recently I got a new &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dxcommander.com&#x2F;product&#x2F;dx-commander-expedition-kit&#x2F;&quot;&gt;DXCommander Expedition Antenna&lt;&#x2F;a&gt; for Parks on the Air (POTA) use.
After some experimentations, this week I decided to finish the tuning, and make a few contacts with it&lt;&#x2F;p&gt;
&lt;h2 id=&quot;initial-thoughts&quot;&gt;Initial thoughts&lt;a class=&quot;post-anchor&quot; href=&quot;#initial-thoughts&quot; aria-label=&quot;Anchor link for: initial-thoughts&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s a large antenna!
See in context with a large pine tree.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;antenna.012761669250aaa4.jpg&quot; alt=&quot;DX Commander Expedion Antenna&quot;
     width=&quot;960&quot; height=&quot;1280&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;antenna.5101a32058764802.jpg 640w,
             https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;antenna.60997bb223dfa8d7.jpg 784w,
             https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;antenna.e1a51cba35fce9f3.jpg 1280w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;It took a while to set up, with a couple of saw horses it&#x27;s not too bad.
Having the antenna at a reasonble height avoids all be bending over to cut and measure things.
Once it was clear where everything went, and had the clamps trimmed to size, the assembly went smooth.&lt;&#x2F;p&gt;
&lt;p&gt;I also tried tryting to extend the antenna while it&#x27;s vertical.
I don&#x27;t recomment this approach.
It doesn&#x27;t work and it will come crashing down and &quot;colapse&quot; quite fast.&lt;&#x2F;p&gt;
&lt;p&gt;The best option for deploying in the field is to lay it down, assemble everything, then walk it up.
It&#x27;s light enough that it&#x27;s easy to do with a single person.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-good&quot;&gt;The good&lt;a class=&quot;post-anchor&quot; href=&quot;#the-good&quot; aria-label=&quot;Anchor link for: the-good&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;The tuning! 40 and 20 meters are spot on, with barely any noticeable movement of the SWR meter while transmitting.&lt;&#x2F;p&gt;
&lt;p&gt;20 meters is well below 2.0 SWR across the entire band.
&lt;img src=&quot;https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;20m.d0ca9e0cd1179d11.png&quot; alt=&quot;20 meter tunning&quot;
     width=&quot;1031&quot; height=&quot;1279&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;20m.3df5bdbe485a607a.png 640w,
             https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;20m.9f1c05e82fb6ec9f.png 784w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;For 40 meters, the story is the same.
&lt;img src=&quot;https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;40m.2842b8ff69f588d9.png&quot; alt=&quot;40 meter tunning&quot;
     width=&quot;1031&quot; height=&quot;1279&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;40m.cb2c589b013a6f01.png 640w,
             https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;40m.709bed10188e2c1b.png 784w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;The 40 meters element ended up a lot longer than I expected.
So long, in fact, that I don&#x27;t need any shockcord at the top.
It reaches the top of the pole, and I use the little clear platic tubing to hold the element in place.
A loop of shockcork keeps me from loosing the little plastic tube.&lt;&#x2F;p&gt;
&lt;p&gt;I opted for a &quot;perfect&quot; 40 meter tune, and this probably lead to the long element, and the not so great 15 meter performance (more on that later).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-ok&quot;&gt;The OK&lt;a class=&quot;post-anchor&quot; href=&quot;#the-ok&quot; aria-label=&quot;Anchor link for: the-ok&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Tuning the 10 meter band was a bit more finicky than I liked.
I was able to get it under 3.0 across the whole band, and I was able to use it fine without using the internal ATU of the radio.&lt;&#x2F;p&gt;
&lt;p&gt;(Sorry, no image of the 10 meter turning.
I closed the software and didn&#x27;t save the image, and I only realized this after the antenna was down.
I&#x27;m not going to stand it back up, in the rain, for one image.
You&#x27;ll have to trust me on this one)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-meh&quot;&gt;The meh&lt;a class=&quot;post-anchor&quot; href=&quot;#the-meh&quot; aria-label=&quot;Anchor link for: the-meh&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;15 meters.
It&#x27;s tuning is not great, see below.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;15m.a8f544f9fde907b1.png&quot; alt=&quot;15 meter tunning&quot;
     width=&quot;1031&quot; height=&quot;1279&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;15m.421ca08d39725446.png 640w,
             https:&#x2F;&#x2F;caius.dev&#x2F;processed_images&#x2F;15m.db281ada9a76f3fa.png 784w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;In practice, I get under 2.0 with the Icom 7300 (very brief testing).
It&#x27;s usable without an ATU, and well within ATU limits if I&#x27;m using a radio that has one built in.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s possible the &quot;overtunning&quot; the 40 meter band got me worse performance here.
But it&#x27;s a compromise I made on purpose.
I use 40 meters a lot more often than 15, and I&#x27;m happy with the overall result.&lt;&#x2F;p&gt;
&lt;p&gt;Also, the elements are a bit loose than I&#x27;d like.
The tensioning loops ended up very close to the spreader plates.
This might be my error in assembly, but for future reference, having the loops a few inches down is OK, as there&#x27;s pleny of shockcord to accomodate this.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;a class=&quot;post-anchor&quot; href=&quot;#conclusions&quot; aria-label=&quot;Anchor link for: conclusions&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s a great antenna!&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s larger than I expected.
But it did deliver the performance I was expecting.
It&#x27;s now in the back of the truck, and I&#x27;m looking forward to trying out in the field on the next POTA activation!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Decoding Meshtastic Channel Links</title>
          <pubDate>Sat, 25 Jan 2025 00:00:00 +0000</pubDate>
          <author>Caius Brindescu</author>
          <link>https://caius.dev/blog/decoding-meshtastic-links/</link>
          <guid>https://caius.dev/blog/decoding-meshtastic-links/</guid>
          <description xml:base="https://caius.dev/blog/decoding-meshtastic-links/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;a class=&quot;post-anchor&quot; href=&quot;#introduction&quot; aria-label=&quot;Anchor link for: introduction&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;If you don&#x27;t know what Meshtastic is, this &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=N3FXej9fqIk&quot;&gt;video has a great introduction&lt;&#x2F;a&gt;.
The TL;DR: is that it&#x27;s a communication protocol using 900 MHz (in the US) &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;LoRa&quot;&gt;LORA Radios&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Communication is done either via direct messages (DMs), or via channels.
The channels are encrypted, so for 2 parties to communicate, they need to know the encryption key (pre-shared key or PSK, for short) for that channel.
Sharing the chaneel name, key etc. is done via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;meshtastic.org&#x2F;e&#x2F;&quot;&gt;QR codes or URLs&lt;&#x2F;a&gt;.
In this blog post, we&#x27;ll look at what information is encoded when sharing channel settings, and we can decode it using a &quot;simple&quot; Python script.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-channel-link&quot;&gt;The channel link&lt;a class=&quot;post-anchor&quot; href=&quot;#the-channel-link&quot; aria-label=&quot;Anchor link for: the-channel-link&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ll use the following link as an example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;https:&#x2F;&#x2F;meshtastic.org&#x2F;e&#x2F;#CjQSIOsfCkgIpGY_8iW02ad-4QPaCSBISJqzIVoZKHdqKXd8GgtCbG9nQ2hhbm5lbCUEAAAAEg4IATgBQANIAVAeWBRoAQ&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The part that we are interested in is the fragment:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;CjQSIOsfCkgIpGY_8iW02ad-4QPaCSBISJqzIVoZKHdqKXd8GgtCbG9nQ2hhbm5lbCUEAAAAEg4IATgBQANIAVAeWBRoAQ&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Base64&quot;&gt;URL safe base64&lt;&#x2F;a&gt; encoded string that has the information we are looking for.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;structure&quot;&gt;Structure&lt;a class=&quot;post-anchor&quot; href=&quot;#structure&quot; aria-label=&quot;Anchor link for: structure&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;The message is encoded using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;protobuf.dev&#x2F;&quot;&gt;protobuf&lt;&#x2F;a&gt;.
The structure we are interested in is defined &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;meshtastic&#x2F;protobufs&#x2F;blob&#x2F;2cffaf53e3faf1b6e41a8b8f05312f2f893be413&#x2F;meshtastic&#x2F;channel.proto&quot;&gt;in this file&lt;&#x2F;a&gt;.
It&#x27;s reasonably well documented, so I will not get into all the details here.
But the main properties we are interested in are the channel name, and the PSK.
They are encoded as the &lt;code&gt;psk&lt;&#x2F;code&gt; and &lt;code&gt;name&lt;&#x2F;code&gt; properties in the top level &lt;code&gt;ChannelSettings&lt;&#x2F;code&gt; message.&lt;&#x2F;p&gt;
&lt;p&gt;For the rest of this post, we&#x27;ll ignore the remainder of the properties, but they can be extracted using the same approach.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;protobuf-and-python&quot;&gt;Protobuf and Python&lt;a class=&quot;post-anchor&quot; href=&quot;#protobuf-and-python&quot; aria-label=&quot;Anchor link for: protobuf-and-python&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;generating-python-code-from-the-protobuf-structure&quot;&gt;Generating Python code from the protobuf structure&lt;a class=&quot;post-anchor&quot; href=&quot;#generating-python-code-from-the-protobuf-structure&quot; aria-label=&quot;Anchor link for: generating-python-code-from-the-protobuf-structure&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;The first setup is installing all the prerequisites.
There are 2 parts: the protobuf compiler and the Python protobuf library.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; apt install protobuf-compiler&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;pip&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; install protobuf==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;3.20.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: I recommend using a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;venv.html&quot;&gt;virtual enviroment&lt;&#x2F;a&gt; for your python setup.
Here&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;packaging.python.org&#x2F;en&#x2F;latest&#x2F;guides&#x2F;installing-using-pip-and-virtual-environments&#x2F;#create-and-use-virtual-environments&quot;&gt;a good resource for setting it up&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Next, we&#x27;ll need to get the protobuf definition &quot;compiled&quot; into python code, so we can the deserialize the configuration from the URL.
The protobuf definition &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;meshtastic&#x2F;protobufs&quot;&gt;lives in GitHub&lt;&#x2F;a&gt;, so we&#x27;ll need to clone it.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; clone git@github.com:meshtastic&#x2F;protobufs.git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ll need to compile a total of four proto files to get this working.
First, let&#x27;s create a new folder for our generated files to live in:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; meshtastic&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then, &lt;code&gt;cd&lt;&#x2F;code&gt; into the &lt;code&gt;protobuf&lt;&#x2F;code&gt; folder, and run the following commands.
(You can compile all of them if you want to, but it&#x27;s not needed for this use case.)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;protoc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; --python_out=..&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; meshtastic&#x2F;apponly.proto&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;protoc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; --python_out=..&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; meshtastic&#x2F;channel.proto&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;protoc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; --python_out=..&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; meshtastic&#x2F;device_ui.proto&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;protoc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; --python_out=..&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; meshtastic&#x2F;config.proto&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, we need to make the &lt;code&gt;meshtastic&lt;&#x2F;code&gt; folder a python &quot;module&quot;, we we&#x27;ll create an empty &lt;code&gt;__init__.py&lt;&#x2F;code&gt; file inside:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;touch&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; meshtastic&#x2F;__init__.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;decoding&quot;&gt;Decoding&lt;a class=&quot;post-anchor&quot; href=&quot;#decoding&quot; aria-label=&quot;Anchor link for: decoding&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;How that have all the prerequisites, let&#x27;s get decoding!&lt;&#x2F;p&gt;
&lt;p&gt;To start with we&#x27;ll need to import the required pacakges:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; meshtastic.apponly_pb2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; ChannelSet&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; base64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ll see the string we want to decode as a constant (for now):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;TO_DECODE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;CjESIMC70tNI5vkpQpHZGeg0WV7y6KqEoD0_t74fM1_jCaMkGghCbG9nVGVzdCUEAAAAEg4IATgBQANIAVAeWBRoAQ&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next, we&#x27;ll create a new, empty object to populate with the deserialized data:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;channelSet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ChannelSet()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, let&#x27;s decode and display the channel name and PSK:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;channelSet.ParseFromString(base64.urlsafe_b64decode(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;TO_DECODE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;==&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; s&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; channelSet.settings:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt;    print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Channel name: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; s.name)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt;    print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Psk: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; base64.b64encode(s.psk).decode())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A few notes on the implementation.
The settings are encoded in a &quot;URL Safe&quot; base64 encoding.
However, Python still expects the padding to be there, so we&#x27;re adding the maximum of 2 padding characters to get around this.
If that&#x27;s too many, any extras will be ignored by the decoder.
&quot;Proper&quot; URL Safe encoding explictly omits padding, but the Python library still requires it, so here we are.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;results&quot;&gt;Results&lt;a class=&quot;post-anchor&quot; href=&quot;#results&quot; aria-label=&quot;Anchor link for: results&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Running the above code, we&#x27;ll get the settings we expect:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ python decode.py &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Channel name: BlogTest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Psk: wLvS00jm+SlCkdkZ6DRZXvLoqoSgPT+3vh8zX+MJoyQ=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can extract other properties if we want to, using the same approach.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-end&quot;&gt;The End&lt;a class=&quot;post-anchor&quot; href=&quot;#the-end&quot; aria-label=&quot;Anchor link for: the-end&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
</description>
      </item>
    </channel>
</rss>
