<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Strafo</title>
      <link>https://strafo.net</link>
      <description>Personal blog of a cat disguised as a human</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://strafo.net/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Thu, 23 Apr 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Database internals (part 1): notes on storage engines and PostgreSQL</title>
          <pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate>
          <author>Andrea Straforini</author>
          <link>https://strafo.net/blog/postgresql-internals/</link>
          <guid>https://strafo.net/blog/postgresql-internals/</guid>
          <description xml:base="https://strafo.net/blog/postgresql-internals/">&lt;p&gt;At work we need to improve the ingestion of a Timeseries database. We tried PostgreSQL + TimescaleDB but unfortunately we&#x27;re only achieving ingestion from ~700 IoT devices in our benchmarks.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s time then to become somewhat of an &lt;em&gt;expert&lt;&#x2F;em&gt; of PostgreSQL and TimescaleDB; for doing good optimization I need to know the internal architecture and details of the software.&lt;&#x2F;p&gt;
&lt;p&gt;These are notes I&#x27;ve gathered from different sources. Writing them out helps me learn better, and they&#x27;re handy to look back on.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;a class=&quot;post-anchor&quot; href=&quot;#disclaimer&quot; aria-label=&quot;Anchor link for: disclaimer&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 article is a collection of educational notes and summaries &lt;strong&gt;based on the copyrighted sources listed below&lt;&#x2F;strong&gt;.
For complete and authoritative information, please refer to the original works.
Images and substantial passages are derived from the cited sources under fair use for educational purposes.&lt;&#x2F;p&gt;
&lt;p&gt;This compilation and original commentary are offered for personal learning and reference.
For any complaints, please contact me.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;a class=&quot;post-anchor&quot; href=&quot;#resources&quot; aria-label=&quot;Anchor link for: resources&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 following works served as the primary sources for this article:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.interdb.jp&#x2F;pg&#x2F;index.html&quot;&gt;The Internals of PostgreSQL - Hironobu SUZUKI&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;postgrespro.com&#x2F;blog&#x2F;pgsql&#x2F;3994098&quot;&gt;Designing Data-Intensive Applications - Martin Kleppmann&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Complementary data and analysis were generated or enhanced using Claude models.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;theory&quot;&gt;Theory&lt;a class=&quot;post-anchor&quot; href=&quot;#theory&quot; aria-label=&quot;Anchor link for: theory&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;indexes&quot;&gt;Indexes&lt;a class=&quot;post-anchor&quot; href=&quot;#indexes&quot; aria-label=&quot;Anchor link for: indexes&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;Indexes are auxiliary data structures that speed up data retrieval by avoiding full table scans; the choice of index type determines the trade-offs between read performance, write overhead, and supported query patterns.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hash-indexes&quot;&gt;Hash Indexes&lt;a class=&quot;post-anchor&quot; href=&quot;#hash-indexes&quot; aria-label=&quot;Anchor link for: hash-indexes&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;Keep an in-memory hash map that maps each record key to the byte offset of its value in the on-disk log file.&lt;&#x2F;p&gt;
&lt;p&gt;The new writes are always appended. In order to avoid running out of disk space we break the log into segments files.
When the segment reaches a certain size or retention then a compaction procedure is run where we keep only the latest value for each key.&lt;&#x2F;p&gt;
&lt;p&gt;In order to avoid blocking the read&#x2F;writes we can merge old segments with a background thread which will write to a new segment.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;hash_index.c775de7f3d774760.png&quot; alt=&quot;Hash Index&quot;
     width=&quot;1280&quot; height=&quot;731&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;hash_index.a93b31fd9690a9b1.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;hash_index.a107bc46b5155dbd.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;hash_index.7bf6897ee3dafccd.png 1280w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;Some characteristics of this approach:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;the key cardinality should not exceed ram size&lt;&#x2F;li&gt;
&lt;li&gt;read&#x2F;writes can be really fast thanks to Caching. If the page is in cache then virtually no I&#x2F;O operation is needed.&lt;&#x2F;li&gt;
&lt;li&gt;usually values are encoded with a binary format into the file&lt;&#x2F;li&gt;
&lt;li&gt;tombstone records are used to delete records.&lt;&#x2F;li&gt;
&lt;li&gt;crash recovery is handled by replaying the segments and using checksum for partial written records or some types of optimizations starting from there&lt;&#x2F;li&gt;
&lt;li&gt;write is strictly sequential so usually a single thread is used while reading can be concurrent.&lt;&#x2F;li&gt;
&lt;li&gt;range queries are not efficient. you have to scan the entire hash index&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;sstables&quot;&gt;SSTables&lt;a class=&quot;post-anchor&quot; href=&quot;#sstables&quot; aria-label=&quot;Anchor link for: sstables&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;Rather than appending data and maintaining a complete Hash Index, Sorted String Tables keep segments ordered by key and use a sparse index.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;sstable.d6a21b529bcd85f9.png&quot; alt=&quot;SSTable&quot;
     width=&quot;780&quot; height=&quot;340&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;sstable.b37bbdc9b0856456.png 640w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;This gives us the following properties:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;When merging segments we can use mergesort&lt;&#x2F;li&gt;
&lt;li&gt;We don&#x27;t have O(1) read access but we have O(n&#x2F;m) where n is the number of elements in the segment and m the cardinality of the sparse index for the segments; or O(log(n&#x2F;m)) when the subsegment has key+value with a fixed size&lt;&#x2F;li&gt;
&lt;li&gt;Each subsegment can be compressed to limit I&#x2F;O&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;lsm-tree&quot;&gt;LSM-tree&lt;a class=&quot;post-anchor&quot; href=&quot;#lsm-tree&quot; aria-label=&quot;Anchor link for: lsm-tree&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 do you get data sorted in the first place?
Two ways:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Structure on disk&lt;&#x2F;li&gt;
&lt;li&gt;In memory data structure&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For LSM-trees we use an in-memory data structure, in particular balanced tree data structures like AVL-trees or red-black-trees.&lt;&#x2F;p&gt;
&lt;p&gt;So the flow is like this:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;When a write come in, add it to the balanced tree (also called &lt;strong&gt;memtable&lt;&#x2F;strong&gt;). This operation has a complexity of O(log(n))&lt;&#x2F;li&gt;
&lt;li&gt;When a retention policy (size or time) is reached we write to disk the memtable as an SSTable. Given the already ordered balanced tree this operation can be done efficiently and in background.&lt;&#x2F;li&gt;
&lt;li&gt;When serving a read request, first we try to find it in the memtable and then we iterate over each SSTable in the disk. This operation is very expensive when the entry is not present. An optimization is to maintain a bloom filter to end the read request in O(1) when the key is not present.&lt;&#x2F;li&gt;
&lt;li&gt;Run compaction background threads from time to time&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The main problem of LSM-tree is that in case of crash we lose the balanced tree data structure in memory.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;b-trees&quot;&gt;B-Trees&lt;a class=&quot;post-anchor&quot; href=&quot;#b-trees&quot; aria-label=&quot;Anchor link for: b-trees&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;B-Trees keep key-value pairs sorted by key but it does it directly on disk.&lt;&#x2F;p&gt;
&lt;p&gt;B-trees break the database down into fixed-size blocks or pages to align better with the underlying file-system and hardware.&lt;&#x2F;p&gt;
&lt;p&gt;Each page can be identified using an address or location which is used to construct a tree of pages.
The number of references to child pages in one page is called &lt;strong&gt;branching factor&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The B-tree with n keys is always balanced and has always a depth of log(n) meaning that a search for a value is O(log(n)).
Usually the depth is 3&#x2F;4 levels. (A four-level tree of 4KB pages with a branching factor of 500 can store up to 250TB)
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;btree_lookup_no_legend.84d1b1cdfb9b2ec7.png&quot; alt=&quot;B-Tree&quot;
     width=&quot;680&quot; height=&quot;270&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;btree_lookup_no_legend.7aef8327b3b7b009.png 640w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Overwriting values in place on disk means that the operation is not immune to crashes.
In order to make the database reliable to crashes, it&#x27;s common to include an additional data structure on disk: a &lt;strong&gt;write-ahead log&lt;&#x2F;strong&gt; (WAL or redo log). This is an append only file to be written before each operation on the B-tree.
The log is used for restoring after the crash to a consistent state.&lt;&#x2F;p&gt;
&lt;p&gt;Additional care must be taken when multiple threads are going to access the B-tree at the same time. This is typically done by protecting the tree&#x27;s data structure with latches (lightweight locks).&lt;&#x2F;p&gt;
&lt;p&gt;Log-structured approaches are simpler in this regard, because they do all the merging in the background without interfering with incoming queries and atomically swap old segments for new segments from time to time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;comparing-lsm-trees-and-b-trees&quot;&gt;Comparing LSM-trees and B-trees&lt;a class=&quot;post-anchor&quot; href=&quot;#comparing-lsm-trees-and-b-trees&quot; aria-label=&quot;Anchor link for: comparing-lsm-trees-and-b-trees&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;As a rule of thumb LSM-trees are faster for writes, whereas B-trees are thought to be faster for reads.&lt;&#x2F;p&gt;
&lt;p&gt;The main advantages of LSM-trees is the lower write amplification and less fragmentation.&lt;&#x2F;p&gt;
&lt;p&gt;The main downside is that you have to carefully configure the compaction operations. If not set up carefully, the operation might not keep up with the rate of incoming data.&lt;&#x2F;p&gt;
&lt;p&gt;B-trees also are attractive in databases that want to offer strong transactional semantics thanks to locks directly attached to the tree.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;other-indexing-structures&quot;&gt;Other Indexing Structures&lt;a class=&quot;post-anchor&quot; href=&quot;#other-indexing-structures&quot; aria-label=&quot;Anchor link for: other-indexing-structures&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;Since secondary indexes permit duplicate keys while B-tree leaves enforce uniqueness, a reconciliation strategy is needed.
Two solutions exist:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Appending record number to the key: &lt;code&gt;key&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;new_key=(key,row_id)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Each value in the index is a list of row ids: &lt;code&gt;key&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;list[row_id]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Secondary indexes, of course, are part of the picture too.
This raises a key question: how do we store them without duplicating data?&lt;&#x2F;p&gt;
&lt;p&gt;When using B-trees the leaf nodes point to heap file.
A &lt;strong&gt;Heap file&lt;&#x2F;strong&gt; is an unordered collection of pages, where each page holds a bunch of records.
So with B-trees the heap file corresponds to the record in the key range.&lt;&#x2F;p&gt;
&lt;p&gt;The Heap file approach is common because it avoids duplicating data when multiple secondary indexes are present: each index references a location in the heap file and the actual data is kept in one place.&lt;&#x2F;p&gt;
&lt;p&gt;The update in place of values without changing a key is efficient provided the new value is not larger than the old one.
For the latter case we need to update all the references to the new position or use a forwarding pointer to the new heap location.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;btree_heap_file.1f7be4a207dab9cc.png&quot; alt=&quot;B-Tree with Heap files&quot;
     width=&quot;691&quot; height=&quot;459&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;btree_heap_file.0166d2728d678f55.png 640w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;In some situations, the extra hop from the index to the heap file is too much of a penalty so it can be desirable to store the indexed row directly within an index.&lt;&#x2F;p&gt;
&lt;p&gt;This is known as &lt;strong&gt;clustered index&lt;&#x2F;strong&gt;.
Then secondary indexes can refer to the primary key which makes the read operation O(2log(n))~=O(log(n))&lt;&#x2F;p&gt;
&lt;p&gt;A compromise between a clustered index and a nonclustered index is known as &lt;strong&gt;covering index&lt;&#x2F;strong&gt;, which stores some of a table&#x27;s columns within the index. Some queries then can be optimized to use only the &lt;em&gt;covered&lt;&#x2F;em&gt; columns.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;column-oriented-storage&quot;&gt;Column Oriented Storage&lt;a class=&quot;post-anchor&quot; href=&quot;#column-oriented-storage&quot; aria-label=&quot;Anchor link for: column-oriented-storage&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;If you have to store petabytes of data storing them and querying them becomes difficult.
Most of the time, queries only access 4 or 5 columns.
We can execute the query effiently by using &lt;strong&gt;column oriented storage&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The idea behind it is simple: don&#x27;t store all the values from one row together, but store all the values from each column together instead.&lt;&#x2F;p&gt;
&lt;p&gt;If each column is stored in a separate file, a query only needs to read and parse those columns that are used in that query, limiting I&#x2F;O.&lt;&#x2F;p&gt;
&lt;p&gt;Also compression can reduce the I&#x2F;O bandwidth further, and using compression on column data with a low entropy can save more disk space. Different compression techniques are used, such as:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;bitmap encoding (useful when the cardinality of the values is low)&lt;&#x2F;li&gt;
&lt;li&gt;run-length encoding, dictionary encoding, and other compression techniques&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;column_oriented.b7a099634dbb9484.png&quot; alt=&quot;Column oriented storage&quot;
     width=&quot;708&quot; height=&quot;500&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;column_oriented.bffe887105e4af20.png 640w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;h3 id=&quot;replication&quot;&gt;Replication&lt;a class=&quot;post-anchor&quot; href=&quot;#replication&quot; aria-label=&quot;Anchor link for: replication&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 difficulty handling replication is caused by the changes on the replicated data.&lt;&#x2F;p&gt;
&lt;p&gt;There are three popular algorithms for replicating changes between nodes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Single-leader&lt;&#x2F;li&gt;
&lt;li&gt;Multi-leader&lt;&#x2F;li&gt;
&lt;li&gt;Leaderless replication&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Replication exists in two flavours:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Synchronous&lt;&#x2F;li&gt;
&lt;li&gt;Asynchronous&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Simply put, when replication is synchronous the master node responds &quot;&lt;em&gt;okay&lt;&#x2F;em&gt;&quot; to the user&#x2F;process only after receiving the &quot;&lt;em&gt;okay&lt;&#x2F;em&gt;&quot; from the slave node, while in asynchronous replication the master&#x27;s acknowledge is sent immediately after having written the change to its persistence.&lt;&#x2F;p&gt;
&lt;p&gt;Different replication methods exists:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Statement-based replication&lt;&#x2F;strong&gt;: the leader logs every write request and sends those to its followers. This means that every follower parses and executes those SQL statements.&lt;&#x2F;p&gt;
&lt;p&gt;It has problems with autoincrementing and nondeterministic queries. It&#x27;s not used a lot.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Write-ahead log shipping&lt;&#x2F;strong&gt;: in the case of a log-structured engine (SSTables or LSM-Trees), this log is the main place for storage. In the case of a B-Tree then the log in question is the write-ahead log.
In either case, the log is sent to the followers which build the exact same data structure found on the leader.&lt;&#x2F;p&gt;
&lt;p&gt;The main disadvantage is that the log describes the data on a very low level: a WAL contains details of which bytes were changed in which disk blocks. This makes replication closely coupled to the storage engine. This can be a problem when upgrading nodes because it might not be possible to run different versions of the database software.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Logical (row-based) log replication&lt;&#x2F;strong&gt;: the idea is to use a &lt;strong&gt;logical log&lt;&#x2F;strong&gt; which is a sequence of records describing writes to database tables at the granularity of a row. A transaction that modifies several rows generates several such log records, followed by a record indicating that the transaction was committed. This technique is also called &lt;strong&gt;change data capture&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;transactions&quot;&gt;Transactions&lt;a class=&quot;post-anchor&quot; href=&quot;#transactions&quot; aria-label=&quot;Anchor link for: transactions&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 safety guarantees provided by transactions are described by the acronym &lt;strong&gt;ACID&lt;&#x2F;strong&gt;, which stands for &lt;em&gt;Atomicity, Consistency, Isolation and Durability.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In practice, one database&#x27;s implementation of ACID does not equal another&#x27;s implementation. For example, there&#x27;s a lot of ambiguity around the meaning of &lt;em&gt;Isolation&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Atomicity&lt;&#x2F;strong&gt; means that a transaction composed of multiple writes has two possible outcomes: every write is completed and the transaction is committed or if anything goes wrong the transaction is aborted and no modification has happened on the database.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Consistency&lt;&#x2F;strong&gt; idea is that you have certain statement about your data (&lt;strong&gt;invariants&lt;&#x2F;strong&gt;) that must always be true. If a transaction starts with a database that is valid according to these invariants, and any writes during the transaction preserve the validity, then you can be sure that the invariants are always satisfied. However, this idea depends on application&#x27;s notion of invariants, and it&#x27;s the application&#x27;s responsibility to define its transactions correctly so that they preserve consistency.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Isolation&lt;&#x2F;strong&gt; means that concurrently executing transactions are isolated from each other: they cannot step on each other&#x27;s toes. There are various levels of isolation. The strongest one is &lt;strong&gt;Serializable&lt;&#x2F;strong&gt; that is the equivalent of the transaction running serially even if they run concurrently. More on isolation later.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Durability&lt;&#x2F;strong&gt; in single-node DBs means that the data has been written to nonvolatile storage, while for replicated DBs it means that the data has also been copied to some number of nodes.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;isolation-levels&quot;&gt;Isolation Levels&lt;a class=&quot;post-anchor&quot; href=&quot;#isolation-levels&quot; aria-label=&quot;Anchor link for: isolation-levels&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;Databases have long tried to hide concurrency issues from application developers by providing transaction isolation.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Serializable&lt;&#x2F;strong&gt; isolation means that the database guarantees that the transaction have the same effect as if they ran serially.
In practice, Serializable isolation has a performance cost, and many databases don&#x27;t want to pay that price. It&#x27;s therefore common for systems to use weaker levels of isolation, which protect against some issues, but not all.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;first-level-read-committed&quot;&gt;First Level: Read Committed&lt;a class=&quot;post-anchor&quot; href=&quot;#first-level-read-committed&quot; aria-label=&quot;Anchor link for: first-level-read-committed&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;The first level is &lt;strong&gt;Read Committed&lt;&#x2F;strong&gt; which makes two guarantees:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;No dirty reads: when reading you will see only committed transactions.&lt;&#x2F;li&gt;
&lt;li&gt;No dirty writes: it&#x27;s possible to overwrite only data that has been committed.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This level does not prevent concurrency problems but guarantees the basics for having transactions.&lt;&#x2F;p&gt;
&lt;p&gt;Most commonly, databases prevent dirty writes by using row-level locks while for dirty reads, old values are kept during the uncommitted transaction and are returned as result for the read-query.&lt;&#x2F;p&gt;
&lt;p&gt;This level suffers from the &lt;strong&gt;read skew&lt;&#x2F;strong&gt; problem which occurs when a transaction reads data at different points in time, seeing some values before and some after another concurrent transaction commits, resulting in an inconsistent view of the database.&lt;&#x2F;p&gt;
&lt;p&gt;For example, imagine a transaction that is generating a report by summing up inventory across multiple warehouses. It reads warehouse A first (100 units), but before it reads warehouse B, a concurrent transaction moves 50 units from B to A and commits. The report transaction now reads warehouse B&#x27;s already-updated value (50 units fewer), while having read warehouse A&#x27;s old value, missing those 50 units entirely and producing an incorrect total.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;second-level-snapshot-isolation&quot;&gt;Second Level: Snapshot Isolation&lt;a class=&quot;post-anchor&quot; href=&quot;#second-level-snapshot-isolation&quot; aria-label=&quot;Anchor link for: second-level-snapshot-isolation&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;To fix the read-skew problem we need &lt;strong&gt;repeatable reads&lt;&#x2F;strong&gt;; &lt;strong&gt;Snapshot isolation&lt;&#x2F;strong&gt; is the most common solution to this problem.&lt;&#x2F;p&gt;
&lt;p&gt;The idea is that each transaction reads from a consistent snapshot of the database, which means that the transaction sees all the data that was committed in the database at the start of the transaction. Even if the data is subsequently changed by another transaction, each transaction sees only the old data from that particular point in time.&lt;&#x2F;p&gt;
&lt;p&gt;Snapshot isolation is useful for backups, analytics and integrity checks and is supported by PostgreSQL, MySQL and others.&lt;&#x2F;p&gt;
&lt;p&gt;To implement snapshot isolation it&#x27;s usually used a generalization of the mechanism explained before to prevent dirty-reads.&lt;&#x2F;p&gt;
&lt;p&gt;The database must potentially keep several different committed versions of an object, because various in-progress transactions may need to see the state of the database at different points in time. Because it maintains several versions of an object side by side, this technique is known as &lt;strong&gt;multi-version concurrency control&lt;&#x2F;strong&gt; (MVCC).&lt;&#x2F;p&gt;
&lt;p&gt;In PostgreSQL, for example, when a transaction is started, it is given a unique, always-increasing transaction ID. Whenever a transaction writes anything to the database, the data it writes is tagged with the transaction ID of the writer. Each row in a table has a &lt;code&gt;created_by&lt;&#x2F;code&gt; field, containing the ID of the transaction that inserted this row into the table and a &lt;code&gt;deleted_by&lt;&#x2F;code&gt; field (initially empty) which contains the ID of the transaction which deleted the record. An update is instead treated as a deleted + create.&lt;&#x2F;p&gt;
&lt;p&gt;How do indexes work in MVCC DBs? An option is to have the index point to all the versions of an object and delegate the responsibility to the index query to filter out object versions not visible to the transaction. Later, a garbage collector can remove old versions.
In practice, many implementation details determine the performance of MVCC.&lt;&#x2F;p&gt;
&lt;p&gt;For example, PostgreSQL has optimization for avoiding index updates if different versions of the same object can fit on the same page.&lt;&#x2F;p&gt;
&lt;p&gt;Many databases call &lt;em&gt;snapshot isolation&lt;&#x2F;em&gt; by different names: Oracle calls it &quot;serializable&quot;, PostgreSQL and MySQL call it &quot;repeatable read&quot;. And to top it off, IBM DB2 uses &quot;repeatable read&quot; to refer to &lt;em&gt;serializability&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;em&gt;snapshot isolation&lt;&#x2F;em&gt; also needs to handle the &lt;em&gt;lost update&lt;&#x2F;em&gt; problem (the classic two parallel counter increments).
Databases provide different solutions to this problem but most of these solutions require careful application-level discipline.
The main solution are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Atomic write operations&lt;&#x2F;strong&gt;: usually implemented by taking an exclusive lock on the object when it&#x27;s read so that no other transaction can write it (or take a competing lock) until the update has been applied.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, object-relational mapping frameworks make it easy to accidentally write code that performs unsafe read-modify-write cycles instead of using atomic operations.&lt;&#x2F;p&gt;
&lt;p&gt;Example query which is concurrency-safe in most databases:&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;UPDATE&lt;&#x2F;span&gt;&lt;span&gt; counters &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;SET&lt;&#x2F;span&gt;&lt;span&gt; val &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; val &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; WHERE key =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;foo&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Explicit locking&lt;&#x2F;strong&gt;: simply lock objects that are going to be updated. Then the application can perform the read-modify-write cycle.&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;BEGIN TRANSACTION&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;SELECT * FROM&lt;&#x2F;span&gt;&lt;span&gt; inventory &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span&gt; order_id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1234&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; FOR UPDATE&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: #88846F;&quot;&gt;-- The FOR UPDATE clause indicates that the database should take a lock on all rows returned by this query.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;UPDATE&lt;&#x2F;span&gt;&lt;span&gt; inventory &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;SET&lt;&#x2F;span&gt;&lt;span&gt; stock &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; stock &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; WHERE&lt;&#x2F;span&gt;&lt;span&gt; order_id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1234&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; AND key =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;foo&amp;#39;&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;UPDATE&lt;&#x2F;span&gt;&lt;span&gt; inventory &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;SET&lt;&#x2F;span&gt;&lt;span&gt; stock &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; stock &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; WHERE&lt;&#x2F;span&gt;&lt;span&gt; order_id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1234&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; AND key =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;bar&amp;#39;&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;COMMIT&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Compare and Set&lt;&#x2F;strong&gt;: in databases that do not provide transactions, compare-and-set operations are sometimes available. The idea is simple: only perform the update if the value has not changed since you last read it. If it has, the update fails and you must retry.&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: #88846F;&quot;&gt;-- You read the current value first&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;SELECT&lt;&#x2F;span&gt;&lt;span&gt; stock &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; inventory &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span&gt; product_id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 3&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: #88846F;&quot;&gt;-- stock = 10&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;-- Then update only if the value is still what you read&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;UPDATE&lt;&#x2F;span&gt;&lt;span&gt; inventory &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;SET&lt;&#x2F;span&gt;&lt;span&gt; stock &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; stock &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; WHERE&lt;&#x2F;span&gt;&lt;span&gt; product_id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; AND&lt;&#x2F;span&gt;&lt;span&gt; stock &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Automatically detecting lost updates&lt;&#x2F;strong&gt;: the idea is to let the updates run in parallel and, if the transaction manager detects a lost update, abort the transaction and force it to retry its read-modify-write cycle. PostgreSQL, Oracle and SQL server snapshot isolation levels automatically detect when a lost update has occurred and abort the offending transaction. MySQL InnoDB, by contrast, does not detect them.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are other kinds of subtler race conditions that can occur when different transactions concurrently try to write to the same objects like &lt;code&gt;Write Skew&lt;&#x2F;code&gt; and &lt;code&gt;Phantoms&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Write Skew is like a generalization of the lost update problem but sneakier. Instead of two transactions writing to the same object, they read the same data, make a decision based on it, and then write to different objects. No dirty read, no lost update yet the result violates a business constraint.&lt;&#x2F;p&gt;
&lt;p&gt;Classic example: Doctors on-call&lt;&#x2F;p&gt;
&lt;p&gt;Rule: at least 1 doctor must be on call at all times.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;write_skew_doctors_oncall.fc96f3725ffeebab.png&quot; alt=&quot;Write Skew Problem&quot;
     width=&quot;680&quot; height=&quot;520&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;write_skew_doctors_oncall.dc1f6eee37528acf.png 640w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;Here the problem could be solved using some techniques like the &lt;code&gt;FOR UPDATE&lt;&#x2F;code&gt; clause.&lt;&#x2F;p&gt;
&lt;p&gt;What makes this case particularly tricky is that it slips past snapshot isolation undetected. Because &lt;strong&gt;each transaction modifies a different row&lt;&#x2F;strong&gt;, one for Alice and one for Bob, there is no write conflict to speak of. Snapshot isolation is only concerned with two transactions touching the same object; since that never happens here, both writes are accepted cleanly.&lt;&#x2F;p&gt;
&lt;p&gt;The pattern always follows this shape:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;SELECT&lt;&#x2F;code&gt; reads some shared state&lt;&#x2F;li&gt;
&lt;li&gt;Application code makes a decision based on that read&lt;&#x2F;li&gt;
&lt;li&gt;An &lt;code&gt;UPDATE&lt;&#x2F;code&gt; changes a different object based on that decision&lt;&#x2F;li&gt;
&lt;li&gt;The invariant (at least 1 doctor on call) is now broken&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Instead, a Phantom occurs when a transaction&#x27;s query would return different rows if re-executed, because another transaction inserted or deleted rows in between.&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: #88846F;&quot;&gt;-- Transaction A checks: is there any room in the meeting?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;SELECT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; count&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; bookings &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span&gt; room_id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; AND&lt;&#x2F;span&gt;&lt;span&gt; slot &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;14:00&amp;#39;&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: #88846F;&quot;&gt;-- returns 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;-- Transaction B does the exact same check concurrently → also 0&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: #88846F;&quot;&gt;-- Both decide it&amp;#39;s safe to book:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; bookings (room_id, slot, user_id) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;VALUES&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;14:00&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;alice&amp;#39;&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;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; bookings (room_id, slot, user_id) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;VALUES&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;14:00&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;bob&amp;#39;&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: #88846F;&quot;&gt;-- Room is now double-booked ✗&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The tricky part here is that &lt;code&gt;SELECT FOR UPDATE&lt;&#x2F;code&gt; doesn&#x27;t help: there are no existing rows to lock when both transactions check. The row that causes the conflict doesn&#x27;t exist yet at the time of the check.&lt;&#x2F;p&gt;
&lt;p&gt;The solutions are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Serializable isolation&lt;&#x2F;strong&gt;: the only level that fully prevents those problems&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Materializing the conflict&lt;&#x2F;strong&gt;: artificially inserting a lock row before it exists (e.g. pre-populate a slots table so there is always a row to lock with FOR UPDATE)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Application-level constraints&lt;&#x2F;strong&gt;: like a UNIQUE index on (room_id, slot) that causes one of the inserts to fail&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;third-level-serializable&quot;&gt;Third Level: Serializable&lt;a class=&quot;post-anchor&quot; href=&quot;#third-level-serializable&quot; aria-label=&quot;Anchor link for: third-level-serializable&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;&lt;strong&gt;Serializable&lt;&#x2F;strong&gt; is the strongest isolation level. It guarantees that even though transactions may execute concurrently, the end result is identical to some serial execution.
It fully prevents dirty reads, non-repeatable reads, phantoms, and write skew.&lt;&#x2F;p&gt;
&lt;p&gt;There are three main approaches:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Actual Serial Execution&lt;&#x2F;strong&gt;: it works in practice when transactions are very short and the dataset fits in memory (no slow disk I&#x2F;O). Redis and VoltDB use this approach. The idea was that modern CPUs are fast enough that a single-threaded loop can outperform a multi-threaded system drowning in lock contention. (&lt;em&gt;Did you say KISS?&lt;&#x2F;em&gt;)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Two-Phase Locking (2PL)&lt;&#x2F;strong&gt;: is the traditional approach. Several transactions are allowed to concurrently read the same object as long as nobody is writing to it. But when a transaction needs to write an object, exclusive access is required.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Serializable Snapshot Isolation (SSI)&lt;&#x2F;strong&gt;: the modern approach. The best of both worlds: optimistic concurrency with full serializability. The idea is to let transactions run freely under snapshot isolation, but track dependencies between them. If the database detects that the combined outcome could not have happened in any serial order, it aborts one of the transactions.&lt;&#x2F;p&gt;
&lt;p&gt;The huge advantage over 2PL is that readers never block writers and writers never block readers: conflicts are only detected at commit time, not during execution. This gives much better throughput under high concurrency.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;postgresql-internals&quot;&gt;PostgreSQL Internals&lt;a class=&quot;post-anchor&quot; href=&quot;#postgresql-internals&quot; aria-label=&quot;Anchor link for: postgresql-internals&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 PostgreSQL, tables, indexes, sequences, views and functions are all database objects that are logically separated from each other and belong to their respective databases.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_db_cluster.870f5b9a559a8e4e.png&quot; alt=&quot;Database cluster Postgres&quot;
     width=&quot;1280&quot; height=&quot;400&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_db_cluster.5381493d0b2989a9.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_db_cluster.e1810f2eadbc8c93.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_db_cluster.acfd341d24e1f47f.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_db_cluster.249568eaaffe110b.png 1920w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_db_cluster.f50ed172b0d66ba7.png 2560w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;All DB objects are managed via &lt;strong&gt;object identifiers&lt;&#x2F;strong&gt; (OIDs) which are uint (4 bytes).&lt;&#x2F;p&gt;
&lt;p&gt;The relationship between objects is stored in &lt;strong&gt;system catalogs&lt;&#x2F;strong&gt;.
System catalogs are the place where a relational database management system stores schema metadata, such as information about tables and columns, and internal bookkeeping information. PostgreSQL&#x27;s system catalogs are regular tables.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;physical-structure-of-dbcluster&quot;&gt;Physical Structure of DBCluster&lt;a class=&quot;post-anchor&quot; href=&quot;#physical-structure-of-dbcluster&quot; aria-label=&quot;Anchor link for: physical-structure-of-dbcluster&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;Postgres saves all configs, databases, etc. under $PGDATA as files; it also supports &lt;strong&gt;tablespaces&lt;&#x2F;strong&gt; but their meaning is different from other RDBMSs.&lt;&#x2F;p&gt;
&lt;p&gt;A tablespace in PostgreSQL is a single directory that contains some data outside of the base directory.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_db_fs.9ac353e6297399f6.png&quot; alt=&quot;Database cluster Postgres Physical Files&quot;
     width=&quot;1280&quot; height=&quot;678&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_db_fs.70163f7aaeb1c59a.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_db_fs.d530fa2cf2e93b7a.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_db_fs.53a8c0ce748a4953.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_db_fs.6be5ec44f83007cb.png 1920w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;Tablespace is useful in two cases: when you want to spread data across different physical drives for performance (e.g. indexes on NVMe, cold data on slow disks), or when your main filesystem is full and you need to overflow onto another mount point.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;table_space.0abd790e38cc7fab.png&quot; alt=&quot;Database cluster Postgres Physical Files&quot;
     width=&quot;1280&quot; height=&quot;701&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;table_space.f350bce8953a95e6.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;table_space.e066a1143355035f.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;table_space.0562808400ad0b84.png 1280w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;The most important subdirectories are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;base: Subdirectory containing per-database subdirectories.&lt;&#x2F;li&gt;
&lt;li&gt;pg_wal: Subdirectory containing WAL (Write Ahead Logging) segment files.&lt;&#x2F;li&gt;
&lt;li&gt;pg_xact: Subdirectory containing transaction commit state data.&lt;&#x2F;li&gt;
&lt;li&gt;pg_multixact: Subdirectory containing multitransaction status data. (used for shared row locks)&lt;&#x2F;li&gt;
&lt;li&gt;pg_commit_ts: Subdirectory containing transaction commit timestamp data.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;layout-of-files-associated-with-tables-and-indexes&quot;&gt;Layout of Files Associated with Tables and Indexes&lt;a class=&quot;post-anchor&quot; href=&quot;#layout-of-files-associated-with-tables-and-indexes&quot; aria-label=&quot;Anchor link for: layout-of-files-associated-with-tables-and-indexes&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;Each table or index whose size is less than 1GB is stored in a single file under its database directory.&lt;&#x2F;p&gt;
&lt;p&gt;Tables and indexes are internally managed by OIDs, while their data files are managed by &lt;code&gt;relfilenode&lt;&#x2F;code&gt;. The &lt;code&gt;relfilenode&lt;&#x2F;code&gt; values usually but not always match the respective OIDs.&lt;&#x2F;p&gt;
&lt;p&gt;When the file size of tables and indexes exceeds 1GB, PostgreSQL creates new files named like &lt;code&gt;relfilenode.1&lt;&#x2F;code&gt; (..n) and so on.
Each table has two associated files, suffixed with &lt;code&gt;_fsm&lt;&#x2F;code&gt; and &lt;code&gt;_vm&lt;&#x2F;code&gt;. These are referred to as the free space map and visibility map, respectively.&lt;&#x2F;p&gt;
&lt;p&gt;The free space map stores information about the free space capacity on each page within the table file, and the visibility map stores information about the visibility of each page within the table file.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_disk_files.f3773b86f6f2d798.png&quot; alt=&quot;Disk files&quot;
     width=&quot;706&quot; height=&quot;582&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_disk_files.e4d33f3257fbefc5.png 640w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;PostgreSQL&#x27;s default index is a B-tree (technically a B+ tree variant), but it&#x27;s just one of several index types it supports.
Every file, whether a heap or an index, is split into 8KB pages, and the page format differs depending on what&#x27;s inside.&lt;&#x2F;p&gt;
&lt;p&gt;Every file is just an array of 8KB pages. Whether it&#x27;s a heap file (18751) or a B-tree index (18752), the physical format is identical at the outer level, a sequence of fixed-size pages. What differs is the payload inside each page.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;_fsm&lt;&#x2F;code&gt; fork is how INSERT finds space fast. Without it, Postgres would have to scan pages looking for a free slot. The FSM is a compact tree of free-space fractions; it lets Postgres jump directly to a page with enough room for the new tuple.&lt;&#x2F;p&gt;
&lt;p&gt;The B-tree leaf node stores a &lt;strong&gt;Tuple ID&lt;&#x2F;strong&gt; (TID), not the actual row. Each index entry is (key, TID) where TID = (block number, slot offset).&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s why after an index lookup you still need a second I&#x2F;O to fetch the actual tuple from the heap.&lt;&#x2F;p&gt;
&lt;p&gt;PostgreSQL supports covering indexes.&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: #88846F;&quot;&gt;-- multi-column: all three columns are part of the key (multi-column index)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;CREATE INDEX&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; ON&lt;&#x2F;span&gt;&lt;span&gt; employees (department_id, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;, salary);&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: #88846F;&quot;&gt;-- covering with INCLUDE: only department_id is the key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;CREATE INDEX&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; ON&lt;&#x2F;span&gt;&lt;span&gt; employees (department_id) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;INCLUDE&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;, salary);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With &lt;code&gt;INCLUDE&lt;&#x2F;code&gt;, the non-key columns don&#x27;t bloat the internal B-tree nodes, they only exist in the leaf nodes.&lt;&#x2F;p&gt;
&lt;p&gt;You can confirm an index-only scan is happening with EXPLAIN:&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&gt;EXPLAIN &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;SELECT name&lt;&#x2F;span&gt;&lt;span&gt;, salary &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; employees &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span&gt; department_id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 5&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: #88846F;&quot;&gt;-- you want to see:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;Index&lt;&#x2F;span&gt;&lt;span&gt; Only Scan &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; idx_salary_covering &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt; employees&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  Index&lt;&#x2F;span&gt;&lt;span&gt; Cond: (department_id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 5&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;  Heap Fetches: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;   ← &lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt; means the VM saved all heap trips&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;Heap Fetches: 0&lt;&#x2F;code&gt; is the ideal because the query never touched the heap file at all.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;file-handling&quot;&gt;File Handling&lt;a class=&quot;post-anchor&quot; href=&quot;#file-handling&quot; aria-label=&quot;Anchor link for: file-handling&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;Postgres has a dedicated layer called the &lt;strong&gt;Storage Manager&lt;&#x2F;strong&gt; (smgr) that handles all file I&#x2F;O, and it talks directly to the OS via low-level syscalls: no stdio and no buffering layer in between.&lt;&#x2F;p&gt;
&lt;p&gt;But the really important thing is that Postgres almost never goes straight to I&#x2F;O. There&#x27;s a whole stack in between:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;asciidoc&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; Query executor&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; Buffer Manager         ← the main actor: 8KB slots in shared memory&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; Storage Manager        ← smgr, translates page requests to file offsets&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; OS kernel page cache   ← kernel may still buffer writes here&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; Physical disk&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Postgres allocates a large region of shared memory at startup (controlled by &lt;code&gt;shared_buffers&lt;&#x2F;code&gt;, default 128MB) and carves it into 8KB slots with one slot per page.
When a backend needs a page it asks the buffer manager first.
If the page is already in a slot (a &quot;buffer hit&quot;) no I&#x2F;O happens.&lt;&#x2F;p&gt;
&lt;p&gt;PostgreSQL uses the following syscalls to perform I&#x2F;O:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;&#x2F;* modern Postgres (single page) *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;pread&lt;&#x2F;span&gt;&lt;span&gt;(fd, buffer,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 8192&lt;&#x2F;span&gt;&lt;span&gt;, block_num &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 8192&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;   &#x2F;* read *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;pwrite&lt;&#x2F;span&gt;&lt;span&gt;(fd, buffer,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 8192&lt;&#x2F;span&gt;&lt;span&gt;, block_num &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 8192&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;* write *&#x2F;&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: #88846F;&quot;&gt;&#x2F;* modern Postgres (multiple pages, vectored) *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;preadv&lt;&#x2F;span&gt;&lt;span&gt;(fd, iov, nblocks, offset);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;pwritev&lt;&#x2F;span&gt;&lt;span&gt;(fd, iov, nblocks, offset);&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: #88846F;&quot;&gt;&#x2F;* fallback for old platforms only *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;lseek&lt;&#x2F;span&gt;&lt;span&gt;(fd, block_num &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 8192&lt;&#x2F;span&gt;&lt;span&gt;, SEEK_SET);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;read&lt;&#x2F;span&gt;&lt;span&gt;(fd, buffer,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 8192&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The other critical syscall is &lt;code&gt;fsync()&lt;&#x2F;code&gt;. When Postgres writes a page it doesn&#x27;t immediately fsync because that would be slow. Instead it marks the buffer as &quot;dirty&quot; and a background process called the &lt;strong&gt;bgwriter&lt;&#x2F;strong&gt; periodically flushes dirty buffers to disk.&lt;&#x2F;p&gt;
&lt;p&gt;fsync is what forces the OS to flush all the way to durable storage, and Postgres calls it at checkpoint time to guarantee crash recovery works correctly.&lt;&#x2F;p&gt;
&lt;p&gt;This is also why &lt;strong&gt;WAL&lt;&#x2F;strong&gt; (Write-Ahead Log) exists, dirty writes without fsync are not durable, so Postgres records every change to the WAL first and fsyncs that before touching the heap pages. If the machine crashes, Postgres can replay the WAL to reconstruct any heap pages that didn&#x27;t make it to disk in time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;internal-layout-of-a-heap-table-file&quot;&gt;Internal layout of a Heap table file&lt;a class=&quot;post-anchor&quot; href=&quot;#internal-layout-of-a-heap-table-file&quot; aria-label=&quot;Anchor link for: internal-layout-of-a-heap-table-file&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;PostgreSQL uses &lt;strong&gt;slotted pages&lt;&#x2F;strong&gt; and &lt;strong&gt;slot arrays&lt;&#x2F;strong&gt; to organize data inside the file.&lt;&#x2F;p&gt;
&lt;p&gt;Every data file is in fact divided into pages (or &lt;em&gt;blocks&lt;&#x2F;em&gt;) of fixed length (8KB) and each block is numbered sequentially from 0 (&lt;strong&gt;block number&lt;&#x2F;strong&gt;).
When the file is full, PostgreSQL adds a new empty page to the end of the file to increase its size.&lt;&#x2F;p&gt;
&lt;p&gt;Here it&#x27;s described the layout of the table file.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;table_file_structure.5147049637d18f65.png&quot; alt=&quot;Table file layout&quot;
     width=&quot;1280&quot; height=&quot;467&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;table_file_structure.b821a9ce00ae7af8.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;table_file_structure.b0e4236615becc6d.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;table_file_structure.6709b1acf3e80897.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;table_file_structure.8dd9d15ddc09cbcc.png 1920w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;table_file_structure.144a33a5906ddca8.png 2560w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;A page within a table contains three kinds of data:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;heap tuple(s) – A heap tuple is a record data itself. Heap tuples are stacked in order from the bottom of the page.&lt;&#x2F;li&gt;
&lt;li&gt;line pointer(s) – A line pointer is a pointer to each heap tuple. Line pointers form a simple array that plays the role of an index to the tuples. When a tuple is added to the page, a new line pointer is pushed onto the array to point to the new tuple.&lt;&#x2F;li&gt;
&lt;li&gt;header data – A header data  (struct &lt;code&gt;PageHeaderData&lt;&#x2F;code&gt;) is allocated in the beginning of the page. It contains information about the page.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The &lt;code&gt;PageHeaderData&lt;&#x2F;code&gt; is defined like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;typedef&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; PageHeaderData&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: #88846F;&quot;&gt; &#x2F;* XXX LSN is member of *any* block, not only page-organized ones *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; PageXLogRecPtr pd_lsn;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;* LSN: next byte after last byte of xlog&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;         * record for last change to this page *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; uint16  pd_checksum;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt; &#x2F;* checksum *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; uint16  pd_flags;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;* flag bits, see below *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; LocationIndex pd_lower;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;* offset to start of free space *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; LocationIndex pd_upper;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;* offset to end of free space *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; LocationIndex pd_special;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt; &#x2F;* offset to start of special space *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; uint16  pd_pagesize_version;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; TransactionId pd_prune_xid;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt; &#x2F;* oldest prunable XID, or zero if none *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; ItemIdData pd_linp[FLEXIBLE_ARRAY_MEMBER];&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt; &#x2F;* line pointer array *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;} PageHeaderData;&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;typedef&lt;&#x2F;span&gt;&lt;span&gt; PageHeaderData &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;PageHeader;&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;typedef&lt;&#x2F;span&gt;&lt;span&gt; uint64 XLogRecPtr;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For now the most important are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pd_checksum&lt;&#x2F;code&gt; - checksum of the page&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pd_lower&lt;&#x2F;code&gt;, &lt;code&gt;pd_upper&lt;&#x2F;code&gt; - points to the end&#x2F;start of the free heap space&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pd_special&lt;&#x2F;code&gt; – This variable is for indexes. In the page within tables, it points to the end of the page. (In the page within indexes, it points to the beginning of special space, which is the data area held only by indexes and contains the particular data according to the kind of index types such as B-tree, GiST, GiN, etc.)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To identify a tuple within the table, a tuple identifier (TID) is used internally. A TID comprises a pair of values: the block number of the page that contains the tuple, and the offset number of the line pointer that points to the tuple.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;reading-heap-tuples&quot;&gt;Reading Heap Tuples&lt;a class=&quot;post-anchor&quot; href=&quot;#reading-heap-tuples&quot; aria-label=&quot;Anchor link for: reading-heap-tuples&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;Two typical access methods, sequential scan and B-tree index scan, are outlined here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Sequential scan – It reads all tuples in all pages sequentially by scanning all line pointers in each page.&lt;&#x2F;li&gt;
&lt;li&gt;B-tree index scan – It reads an index file that contains index tuples. If the index tuple with the key that you are looking for has been found, PostgreSQL reads the desired heap tuple using the obtained TID value.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_reading.a894a62e44dbe679.png&quot; alt=&quot;PostgreSQL reading sequence&quot;
     width=&quot;1280&quot; height=&quot;853&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_reading.566abed124076b66.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_reading.ec31eb3751326f3b.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_reading.433ef41d6bfdf5b9.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_reading.eeda98fabc6f5672.png 1920w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;PostgreSQL has a component called &lt;strong&gt;planner&lt;&#x2F;strong&gt; (or &lt;strong&gt;query optimizer&lt;&#x2F;strong&gt;) that takes a parsed SQL query and figures out the most efficient way to execute it.
The costs of sequential scans and index scans are estimated by &lt;code&gt;cost_seqscan()&lt;&#x2F;code&gt; and &lt;code&gt;cost_index()&lt;&#x2F;code&gt; respectively.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;seq_page_cost&lt;&#x2F;code&gt; vs &lt;code&gt;random_page_cost&lt;&#x2F;code&gt; ratio is the key lever. The default values of &lt;code&gt;seq_page_cost&lt;&#x2F;code&gt; and &lt;code&gt;random_page_cost&lt;&#x2F;code&gt; are 1.0 and 4.0 respectively which means PostgreSQL assumes random access is four times slower than sequential. This &lt;em&gt;default is based on HDDs&lt;&#x2F;em&gt;. When using SSDs, &lt;code&gt;random_page_cost&lt;&#x2F;code&gt; should be lowered to around 1.0, otherwise the planner may select ineffective plans.&lt;&#x2F;p&gt;
&lt;p&gt;Sequential scan is typically chosen for cases when the table being scanned is small or the percentage of rows returned outweighs using an index. If a query returns ~10% of rows, a sequential scan is probably faster.&lt;&#x2F;p&gt;
&lt;p&gt;Bitmap scans act as a middle ground. If an index scan or a sequential scan aren&#x27;t the perfect option, Postgres can use a bitmap index scan as a hybrid approach. It is typically chosen when a query matches too many rows for a regular index scan, but not so many that a sequential scan would be the best option.&lt;&#x2F;p&gt;
&lt;p&gt;It works in two phases: first builds an in-memory bitmap of matching pages from the index, then fetches only those pages from the heap; this provides better random I&#x2F;O behaviour than a pure index scan.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;index-only-scans-and-visibility-maps&quot;&gt;Index-Only Scans and Visibility Maps&lt;a class=&quot;post-anchor&quot; href=&quot;#index-only-scans-and-visibility-maps&quot; aria-label=&quot;Anchor link for: index-only-scans-and-visibility-maps&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;PostgreSQL uses a performance optimization called &lt;strong&gt;Index-Only Scan&lt;&#x2F;strong&gt; that avoids accessing heap pages when possible. The challenge is that index tuples lack transaction metadata (&lt;code&gt;t_xmin&lt;&#x2F;code&gt;, &lt;code&gt;t_xmax&lt;&#x2F;code&gt;), which is needed for visibility checks in concurrent transactions.&lt;&#x2F;p&gt;
&lt;p&gt;The solution is the &lt;strong&gt;Visibility Map (VM)&lt;&#x2F;strong&gt;: when a heap page is marked as fully visible in the VM, all tuples on that page are known to be visible to all transactions. For these pages, PostgreSQL can retrieve all necessary data directly from the index without accessing the heap at all.&lt;&#x2F;p&gt;
&lt;p&gt;For pages with uncertain visibility, heap access is still required to verify which tuple versions are visible to the current transaction.&lt;&#x2F;p&gt;
&lt;p&gt;This optimization significantly reduces I&#x2F;O costs for queries that can be satisfied by index data alone, especially for large tables where heap pages may be scattered across disk.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;index-only-scan.d06dfeaff459fec6.png&quot; alt=&quot;Index Only Scan&quot;
     width=&quot;1280&quot; height=&quot;1023&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;index-only-scan.9a347b1d83ffc3a2.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;index-only-scan.b942c41cb7235464.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;index-only-scan.6027710ca70056f8.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;index-only-scan.c10895404b43914e.png 1920w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;h3 id=&quot;writing-heap-tuples&quot;&gt;Writing Heap Tuples&lt;a class=&quot;post-anchor&quot; href=&quot;#writing-heap-tuples&quot; aria-label=&quot;Anchor link for: writing-heap-tuples&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;To understand how PostgreSQL writes tuples into a page, let&#x27;s consider a simple table with a single page that initially holds one tuple.&lt;&#x2F;p&gt;
&lt;p&gt;At that point, &lt;code&gt;pd_lower&lt;&#x2F;code&gt; points to the first line pointer, and that line pointer, along with &lt;code&gt;pd_upper&lt;&#x2F;code&gt;, both reference the same first tuple in the page.&lt;&#x2F;p&gt;
&lt;p&gt;When a second tuple is inserted, PostgreSQL places it right after the first one and appends a new line pointer to the array.
The new line pointer references the newly written tuple, while &lt;code&gt;pd_lower&lt;&#x2F;code&gt; advances to point at this second line pointer and &lt;code&gt;pd_upper&lt;&#x2F;code&gt; shifts down to point at the second tuple.
Along with these pointer updates, other metadata fields in the page header are also updated accordingly.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;write_heap_tuple.dca54d06975cce6c.png&quot; alt=&quot;Heap Tuple Insertion&quot;
     width=&quot;1280&quot; height=&quot;269&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;write_heap_tuple.046e928e1d3d50d2.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;write_heap_tuple.55f8794d2c77921f.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;write_heap_tuple.6c328d24412f9921.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;write_heap_tuple.e75b14ae649258e2.png 1920w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;write_heap_tuple.04bc01c647e25edf.png 2560w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;h4 id=&quot;heap-only-tuple-optimization&quot;&gt;Heap Only Tuple Optimization&lt;a class=&quot;post-anchor&quot; href=&quot;#heap-only-tuple-optimization&quot; aria-label=&quot;Anchor link for: heap-only-tuple-optimization&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;The &lt;strong&gt;Heap Only Tuple&lt;&#x2F;strong&gt; (HOT) mechanism is an optimization that kicks in when an updated row can be stored on the same page as its predecessor.
It conserves space in both index and table pages, and reduces the amount of work VACUUM needs to do.&lt;&#x2F;p&gt;
&lt;p&gt;Consider a table &quot;tbl&quot; with columns id (primary key) and data. The table has 1,000 rows; the last one (id = 1000) lives on page 5 of the table heap, referenced by an index entry pointing to position (5, 1).
When you run:&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;UPDATE&lt;&#x2F;span&gt;&lt;span&gt; tbl &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;SET data =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;B&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; WHERE&lt;&#x2F;span&gt;&lt;span&gt; id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1000&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Without HOT, PostgreSQL writes a new tuple to the heap and a new entry into the index page. Every update therefore bloats the index, and cleaning up those stale index entries is expensive, both in write cost and in VACUUM overhead.&lt;&#x2F;p&gt;
&lt;p&gt;HOT avoids the extra index write under two conditions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The new tuple fits on the same page as the old one. If the row migrates to a different page, a new index entry must be written.&lt;&#x2F;li&gt;
&lt;li&gt;The indexed column(s) are not modified. If the update changes a value that is part of an index key, a new index entry is required regardless of page placement.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;When these conditions are met, PostgreSQL skips inserting a new index tuple.
Instead, it marks the old tuple with the &lt;code&gt;HEAP_HOT_UPDATED&lt;&#x2F;code&gt; flag and the new tuple with the &lt;code&gt;HEAP_ONLY_TUPLE&lt;&#x2F;code&gt; flag. The existing index entry continues to point to the old tuple&#x27;s line pointer, no index page is touched.
Reading a HOT-updated row via index scan then works like this:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Follow the index entry to line pointer [1].&lt;&#x2F;li&gt;
&lt;li&gt;Read the old tuple (Tuple_1), which is flagged &lt;code&gt;HEAP_HOT_UPDATED&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Follow Tuple_1&#x27;s &lt;code&gt;t_ctid&lt;&#x2F;code&gt; to reach the new tuple (Tuple_2).&lt;&#x2F;li&gt;
&lt;li&gt;Apply the visibility rules to decide which version is the one to return.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;A problem arises once Tuple_1 becomes dead and gets removed: the index still points to its line pointer, but the tuple is gone, making Tuple_2 unreachable.&lt;&#x2F;p&gt;
&lt;p&gt;PostgreSQL solves this through pruning: before Tuple_1 is physically removed, its line pointer is redirected to point at Tuple_2&#x27;s line pointer.
The index entry therefore still leads to the correct live tuple through an extra indirection hop, without any change to the index itself.&lt;&#x2F;p&gt;
&lt;p&gt;Pruning can happen opportunistically during any SQL command whenever PostgreSQL judges it appropriate.&lt;&#x2F;p&gt;
&lt;p&gt;Alongside pruning, PostgreSQL also performs defragmentation: it reclaims the space occupied by dead HOT tuples on the page.
Crucially, this does not touch the index at all, so it is significantly cheaper than a full VACUUM pass.&lt;&#x2F;p&gt;
&lt;p&gt;Complete description can be found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.interdb.jp&#x2F;pg&#x2F;pgsql07&#x2F;01.html&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;process-and-memory-architecture&quot;&gt;Process and Memory Architecture&lt;a class=&quot;post-anchor&quot; href=&quot;#process-and-memory-architecture&quot; aria-label=&quot;Anchor link for: process-and-memory-architecture&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;A collection of multiple processes that cooperatively manage a database cluster is usually referred to as a ‘PostgreSQL server’. It contains the following types of processes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Postgres server process: is the parent of all processes within a PostgreSQL server. It allocates a shared memory area, initiates various background processes, starts replication-related processes and background worker processes as needed, and waits for connection requests from clients (by listening on port 5432). When a connection request is received, it spawns a backend process to handle the client session.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Backend process: handles all queries issued by one connected client. It communicates with the client using a single TCP connection and terminates when the client disconnects. PostgreSQL supports concurrent connections from multiple clients, with the maximum number controlled by the &lt;code&gt;max_connections&lt;&#x2F;code&gt; parameter (default: 100).&lt;&#x2F;p&gt;
&lt;p&gt;If clients frequently connect and disconnect from a PostgreSQL server, it can increase the cost of establishing connections and creating backend processes. To deal with such a case, a connection pooling middleware such as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.pgbouncer.org&#x2F;&quot;&gt;pgbouncer&lt;&#x2F;a&gt; can be used.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Background processes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Background writer: writes dirty pages on the shared buffer pool to a persistent storage.&lt;&#x2F;li&gt;
&lt;li&gt;Checkpointer: this process performs the checkpoint process.&lt;&#x2F;li&gt;
&lt;li&gt;Autovacuum launcher: it requests the postgres server to create the autovacuum workers.&lt;&#x2F;li&gt;
&lt;li&gt;WAL writer: writes and flushes the WAL data on the WAL buffer to persistent storage periodically.&lt;&#x2F;li&gt;
&lt;li&gt;IO worker: handle read operations asynchronously, offloading I&#x2F;O tasks from regular backend processes.&lt;&#x2F;li&gt;
&lt;li&gt;Others: WAL summarizer, statistics collector, archiver and logging collector.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Replication-associated processes&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_procs.673641e9932f90cf.png&quot; alt=&quot;PostgreSQL process architecture&quot;
     width=&quot;1280&quot; height=&quot;582&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_procs.80295e54ec4586b4.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_procs.468e7afbfd3b1598.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_procs.0ef3aa8aff74b6a7.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg_procs.99a2e3c625505a9e.png 1920w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;Memory architecture in PostgreSQL can be classified into three broad categories:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Local memory area&lt;&#x2F;li&gt;
&lt;li&gt;Dynamic Shared Memory&lt;&#x2F;li&gt;
&lt;li&gt;Shared memory area&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each backend process allocates a local memory area for query processing.
The three major areas are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;work_mem&lt;&#x2F;code&gt;: used by the executor for sorting, distinct and joining operations.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;maintenance_work_mem&lt;&#x2F;code&gt;: various maintenance operations like VACUUM use this mem area.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;temp_buffers&lt;&#x2F;code&gt;: executor uses this area for storing temporary tables.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Dynamic Shared Memory it&#x27;s an on-demand mem space used by parallel query for inter-process communications between postgres processes.&lt;&#x2F;p&gt;
&lt;p&gt;The shared memory area is allocated at startup.
There are three major areas:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Shared buffer pool: tables and indexes pages are loaded from persistent storage here to be operated on directly&lt;&#x2F;li&gt;
&lt;li&gt;WAL buffer: is a buffering are where WAL data is stored before writing it to persistent storage&lt;&#x2F;li&gt;
&lt;li&gt;Commit log (CLOG): keeps the state of all transactions (in_progress,committed, aborted) for the concurrency control mechanism.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;query-processing&quot;&gt;Query Processing&lt;a class=&quot;post-anchor&quot; href=&quot;#query-processing&quot; aria-label=&quot;Anchor link for: query-processing&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;Although the parallel query implemented in version 9.6 uses multiple background worker processes, a backend process basically handles all queries issued by the connected client. This backend consists of five subsystems:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Parser&lt;&#x2F;li&gt;
&lt;li&gt;Analyzer&lt;&#x2F;li&gt;
&lt;li&gt;Rewriter&lt;&#x2F;li&gt;
&lt;li&gt;Planner&lt;&#x2F;li&gt;
&lt;li&gt;Executor&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;asciidoc&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          SQL Statement&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;┌─────────────────────────────────┐&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│     Backend Process             │&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;│  ┌──────────────────────────┐   │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  │       Parser             │   │&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;│             │ (parse tree)      │&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;│  ┌──────────────────────────┐   │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  │  Analyzer&#x2F;Analyser       │   │&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;│             │ (query tree)      │&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;│  ┌──────────────────────────┐   │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  │      Rewriter            │   │&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;│             │ (query tree)      │&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;│  ┌──────────────────────────┐   │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  │      Planner             │   │&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;│             │ (plan tree)       │&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;│  ┌──────────────────────────┐   │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  │      Executor            │   │&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;│             │                   │&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;              ↓&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            Result&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;            Client&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The parser generates a parse tree from sql plain text and only checks the syntax of an input when generating a parse tree. Therefore, it only returns an error if there is a syntax error in the query.
For 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;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;testdb&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; SELECT&lt;&#x2F;span&gt;&lt;span&gt; id, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;data FROM&lt;&#x2F;span&gt;&lt;span&gt; tbl_a &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span&gt; id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 300&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; ORDER BY data&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;becomes:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;parsetree.aa47f471fc5b3891.png&quot; alt=&quot;Parse tree from sql statement&quot;
     width=&quot;1280&quot; height=&quot;565&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;parsetree.d75fdbc0ad4a9690.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;parsetree.7b27f41726e750c5.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;parsetree.1b559fbafe91817b.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;parsetree.03d9a8d7600835d3.png 1920w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;parsetree.ce02804da4071ec1.png 2560w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;The analyzer runs a semantic analysis of the parse tree and generates a query tree.
The root is the Query structure containing the command type, metadata, and leaves structured as lists or trees for each clause.&lt;&#x2F;p&gt;
&lt;p&gt;The Query structure has four key components. The &lt;code&gt;targetlist&lt;&#x2F;code&gt; contains the result columns, and if the input uses an asterisk, the analyzer expands it to all columns. The range table holds the relations and tables used in the query, including their OID and name. The join tree stores the &lt;code&gt;FROM&lt;&#x2F;code&gt; and &lt;code&gt;WHERE&lt;&#x2F;code&gt; clauses. Finally, the sort clause contains a list of &lt;code&gt;SortGroupClause&lt;&#x2F;code&gt; elements.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;analyzer.6a18efe9ddfa9b13.png&quot; alt=&quot;Query tree from analyzer&quot;
     width=&quot;1280&quot; height=&quot;563&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;analyzer.61b6b5933b8c9ec5.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;analyzer.e93f265e952421a3.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;analyzer.7aa99194d618b409.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;analyzer.da6ddbdca6a450a2.png 1920w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;analyzer.3145ed0ce2ecfdf1.png 2560w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;The rewriter is the system that realizes the rule system. It transforms a query tree according to the rules stored in the &lt;code&gt;pg_rules&lt;&#x2F;code&gt; system catalog, if necessary.
For example views in PostgreSQL are implemented by using the rule system.&lt;&#x2F;p&gt;
&lt;p&gt;The planner receives a query tree from the rewriter and generates a (query) plan tree that can be processed by the executor most effectively.
Its objective is to do cost optimization.&lt;&#x2F;p&gt;
&lt;p&gt;When using the &lt;code&gt;EXPLAIN&lt;&#x2F;code&gt; command we are effectively showing the query plan tree.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;explain.cdb55b9311cd2f06.png&quot; alt=&quot;Plan tree and explain command&quot;
     width=&quot;1280&quot; height=&quot;654&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;explain.8c18e644e2457449.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;explain.a14b87d8055fa3b8.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;explain.906f0972d0499c41.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;explain.4c7a39cd92e267d5.png 1920w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;explain.819596baa77f1551.png 2560w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;The plan tree is composed of nodes, those nodes contains information necessary by the executor for processing.&lt;&#x2F;p&gt;
&lt;p&gt;The executor operates on tables and indexes via the buffer manager. When processing a query, the executor uses some memory areas, such as &lt;code&gt;temp_buffers&lt;&#x2F;code&gt; and &lt;code&gt;work_mem&lt;&#x2F;code&gt;, allocated in advance and creates temporary files if necessary.&lt;&#x2F;p&gt;
&lt;p&gt;In the output of the explain node each line is a &lt;strong&gt;plan node&lt;&#x2F;strong&gt;. The tree is read bottom-up, the executor starts at the deepest indented node and works toward the root:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Seq Scan&lt;&#x2F;code&gt; runs first → scans the heap, applies &lt;code&gt;id &amp;lt; 300&lt;&#x2F;code&gt; filter&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Sort&lt;&#x2F;code&gt; runs second → takes those rows and sorts by &lt;code&gt;data&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The cost number are divided in the following  way:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;startup cost&lt;&#x2F;strong&gt;  is the cost before the first heap tuple is fetched. For Seq Scan it&#x27;s 0.00 because it can start emitting tuples immediately. Instead, for an index scan, it must traverse the B-tree before. Only after reaching the leaf node it will emit the first TID.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;strong&gt;run cost&lt;&#x2F;strong&gt; depends on the type of node and is calculated by a sum of various constants (cpu cost, i&#x2F;o cost per page and others).&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;strong&gt;total cost&lt;&#x2F;strong&gt; is the sum of the costs of both start-up and run costs.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;strong&gt;rows&lt;&#x2F;strong&gt; is the estimated number of rows this node will return. Comes from &lt;code&gt;pg_statistic&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;ANALYZE&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;strong&gt;width&lt;&#x2F;strong&gt; is the average size of one row in bytes. Used to estimate memory consumption.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These costs are in arbitrary dimensionless units, they only matter relative to each other. &lt;code&gt;seq_page_cost&lt;&#x2F;code&gt; = 1.0 is the baseline everything else is measured against.&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;(cost=182.34..183.09 rows=300 width=8)&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;  startup    total    estimated  avg row&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   cost      cost      rows     size (bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Plain &lt;code&gt;EXPLAIN&lt;&#x2F;code&gt; only shows estimates; &lt;code&gt;EXPLAIN ANALYZE&lt;&#x2F;code&gt; actually executes the query and shows real numbers alongside:&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&gt;EXPLAIN ANALYZE &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;SELECT * FROM&lt;&#x2F;span&gt;&lt;span&gt; tbl_a &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span&gt; id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 300&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; ORDER BY data&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&gt; Sort  (cost&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;182&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;34&lt;&#x2F;span&gt;&lt;span&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;183&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;09&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; rows=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;300&lt;&#x2F;span&gt;&lt;span&gt; width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;8&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;       (actual &lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;time&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;350&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; rows=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;287&lt;&#x2F;span&gt;&lt;span&gt; loops&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;1&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;   Sort &lt;&#x2F;span&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: #F92672;&quot;&gt;data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   Sort Method: quicksort  Memory: 36kB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;   -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;  Seq Scan &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt; tbl_a  (cost&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;00&lt;&#x2F;span&gt;&lt;span&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;170&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;00&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; rows=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;300&lt;&#x2F;span&gt;&lt;span&gt; width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;8&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;                           (actual &lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;time&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;021&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;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;812&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; rows=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;287&lt;&#x2F;span&gt;&lt;span&gt; loops&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;1&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;         Filter&lt;&#x2F;span&gt;&lt;span&gt;: (id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 300&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;         Rows&lt;&#x2F;span&gt;&lt;span&gt; Removed &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;by Filter&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;713&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; Planning &lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;Time&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt; ms&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; Execution &lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;Time&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;4&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; ms&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;actual time&lt;&#x2F;strong&gt; measures pure executor time inside the node in milliseconds, not considering the time spent converting output or transmission to the client.&lt;&#x2F;p&gt;
&lt;p&gt;The new fields are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;loops: how many times this node was executed. Important for joins, an inner node in a nested loop runs once per outer row.&lt;&#x2F;li&gt;
&lt;li&gt;sort method: tells you which sort alg. was applied and the quantity of memory used. If it says external &lt;code&gt;merge Disk: Xkb&lt;&#x2F;code&gt;, the sort spilled to disk because work_mem was too small.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Finally we have &lt;code&gt;EXPLAIN (ANALYZE, BUFFERS)&lt;&#x2F;code&gt; which also show I&#x2F;O stats.&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&gt;EXPLAIN (ANALYZE, BUFFERS) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;SELECT * FROM&lt;&#x2F;span&gt;&lt;span&gt; tbl_a &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span&gt; id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 300&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; ORDER BY data&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&gt; Sort  (cost&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;182&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;34&lt;&#x2F;span&gt;&lt;span&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;183&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;09&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; rows=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;300&lt;&#x2F;span&gt;&lt;span&gt; width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;8&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;       (actual &lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;time&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;350&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; rows=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;287&lt;&#x2F;span&gt;&lt;span&gt; loops&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;1&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;   Buffers: shared hit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;48&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;   -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;  Seq Scan &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt; tbl_a ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         Buffers: shared hit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;45&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; read=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The new important fields are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;shared hit: number of pages served from the buffer manager (shared memory) so no disk I&#x2F;O.&lt;&#x2F;li&gt;
&lt;li&gt;read:  number of pages fetched from disk (or OS cache) via &lt;code&gt;pread()&lt;&#x2F;code&gt;, the expensive path.&lt;&#x2F;li&gt;
&lt;li&gt;written: number of dirty pages written back to disk during query execution.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For a complete explanation of formulas, math and the limitations behind cost estimation see: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.interdb.jp&#x2F;pg&#x2F;pgsql03&#x2F;02.html&quot;&gt;Cost Estimation in Single-Table Query&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;planner-processing-in-detail&quot;&gt;Planner Processing in detail&lt;a class=&quot;post-anchor&quot; href=&quot;#planner-processing-in-detail&quot; aria-label=&quot;Anchor link for: planner-processing-in-detail&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;The planner performs three steps:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Carry out preprocessing&lt;&#x2F;strong&gt;: some examples are: simplifying target lists, limit clauses, eval constant expressions, flattening and&#x2F;or expressions.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Get the cheapest access path&lt;&#x2F;strong&gt; by estimating the costs of all possible access paths&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Create the plan tree&lt;&#x2F;strong&gt; from the cheapest path.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;To plan the cheapest access path, the planner creates a &lt;code&gt;RelOptInfo&lt;&#x2F;code&gt; structure which contains the access paths and their corresponding costs.
The structure is stored in the &lt;code&gt;simple_rel_array&lt;&#x2F;code&gt; of the &lt;code&gt;PlannerInfo&lt;&#x2F;code&gt; structure.&lt;&#x2F;p&gt;
&lt;p&gt;In its initial state, the &lt;code&gt;RelOptInfo&lt;&#x2F;code&gt; holds the &lt;code&gt;baserestrictinfo&lt;&#x2F;code&gt; and the &lt;code&gt;indexlist&lt;&#x2F;code&gt; if related indexes exist. The &lt;code&gt;baserestrictinfo&lt;&#x2F;code&gt; stores the WHERE clauses of the query, and the &lt;code&gt;indexlist&lt;&#x2F;code&gt; stores the related indexes of the target table.&lt;&#x2F;p&gt;
&lt;p&gt;After the initialization of the &lt;code&gt;RelOptInfo&lt;&#x2F;code&gt; struct the planner estimates the costs of all possible access paths, and add them to the struct.
Details of this processing:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;A path is created, the cost of the sequential scan is estimated, and the estimated costs are written to the path. Then, the path is added to the &lt;code&gt;pathlist&lt;&#x2F;code&gt; of the &lt;code&gt;RelOptInfo&lt;&#x2F;code&gt; structure.&lt;&#x2F;li&gt;
&lt;li&gt;If indexes related to the target table exist, index access paths are created, all index scan costs are estimated, and the estimated costs are written to the path. Then, the index paths are added to the &lt;code&gt;pathlist&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;If the bitmap scan can be done, bitmap scan paths are created, all bitmap scan costs are estimated, and the estimated costs are written to the path. Then, the bitmap scan paths are added to the &lt;code&gt;pathlist&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;After that, the planner gets the cheapest access path in the pathlist of the structure and estimates limit, order_by if necessary.&lt;&#x2F;p&gt;
&lt;p&gt;Here is an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.interdb.jp&#x2F;pg&#x2F;pgsql03&#x2F;03.html#3322-example-2&quot;&gt;example&lt;&#x2F;a&gt; from InterDB.&lt;&#x2F;p&gt;
&lt;p&gt;Given this 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;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;testdb&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;# \d tbl_2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;     Table&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;public.tbl_2&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; Column |  &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;Type&lt;&#x2F;span&gt;&lt;span&gt;   | Modifiers&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&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;integer&lt;&#x2F;span&gt;&lt;span&gt; | &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;not null&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;   | &lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;integer&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;Indexes:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    &amp;quot;tbl_2_pkey&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; PRIMARY KEY&lt;&#x2F;span&gt;&lt;span&gt;, btree (id)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    &amp;quot;tbl_2_data_idx&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; btree (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;data&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&gt;testdb&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; SELECT * FROM&lt;&#x2F;span&gt;&lt;span&gt; tbl_2 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span&gt; id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 240&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The steps are:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;RelOptInfo&lt;&#x2F;code&gt; struct&lt;&#x2F;li&gt;
&lt;li&gt;Add the WHERE clause to the &lt;code&gt;baserestrictinfo&lt;&#x2F;code&gt;, and add the indexes of the target table to the &lt;code&gt;indexlist&lt;&#x2F;code&gt;. In this example, a WHERE clause &lt;code&gt;id&amp;lt;240&lt;&#x2F;code&gt; is added to the &lt;code&gt;baserestrictinfo&lt;&#x2F;code&gt;, and two indexes, &lt;code&gt;tbl_2_pkey&lt;&#x2F;code&gt; and &lt;code&gt;tbl_2_data_idx&lt;&#x2F;code&gt;, are added to the &lt;code&gt;indexlist&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Estimate the cost of the sequential scan, and add the path to the &lt;code&gt;pathlist&lt;&#x2F;code&gt; of the &lt;code&gt;RelOptInfo&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Estimate the cost of the index scans and add them to the &lt;code&gt;pathlist&lt;&#x2F;code&gt;. Note that when adding access path they&#x27;re inserted in sort order of the total cost.&lt;&#x2F;li&gt;
&lt;li&gt;Create a new &lt;code&gt;RelOptInfo&lt;&#x2F;code&gt; structure with the cheapest path.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;example_planner_access_path.ffc4435d5db6c00e.png&quot; alt=&quot;Example access path tree&quot;
     width=&quot;1280&quot; height=&quot;394&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;example_planner_access_path.b89f6e9121a2b4e5.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;example_planner_access_path.83bbcf66297a51af.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;example_planner_access_path.4c9e16e2341a6745.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;example_planner_access_path.7f2b950f1ffc6713.png 1920w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;After that the planner generates a plan tree from the cheapest path.&lt;&#x2F;p&gt;
&lt;p&gt;The plan tree is composed of different nodes like:
&lt;code&gt;PlannedStmt&lt;&#x2F;code&gt; which is the root of the tree and contains information such as:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;commandType: stores type of operation, such as SELECT and UPDATE.&lt;&#x2F;li&gt;
&lt;li&gt;rtable: stores range table entries.&lt;&#x2F;li&gt;
&lt;li&gt;relationOids: stores oids of the related tables.&lt;&#x2F;li&gt;
&lt;li&gt;plantree: stores a plan tree that is composed of plan nodes.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The plan trees is composed of various plan nodes.
The &lt;code&gt;PlanNode&lt;&#x2F;code&gt; structure is the base node and other nodes (&lt;code&gt;SeqScanNode&lt;&#x2F;code&gt;,&lt;code&gt;IndexScanNode&lt;&#x2F;code&gt;) always contain it. A &lt;code&gt;PlanNode&lt;&#x2F;code&gt; contains fields such as:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;start-up cost and total_cost.&lt;&#x2F;li&gt;
&lt;li&gt;rows is the number of rows to be scanned, which is estimated by the planner.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;targetlist&lt;&#x2F;code&gt; stores the target list items contained in the query tree.&lt;&#x2F;li&gt;
&lt;li&gt;qual is a list that stores qual conditions.&lt;&#x2F;li&gt;
&lt;li&gt;lefttree and righttree are the nodes for adding the children nodes.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;plan_tree_example.89f54df883f23d5c.png&quot; alt=&quot;Example planner tree&quot;
     width=&quot;740&quot; height=&quot;401&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;plan_tree_example.272c193f5d9c5c6d.png 640w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;Before we described the process for creating a plan tree of a single-table query, in the following is described the same process for multiple-table queries.&lt;&#x2F;p&gt;
&lt;p&gt;The preprocessing phase is similar, there are only some added operations like converting subqueries to join if possible and transforming &lt;em&gt;outer joins&lt;&#x2F;em&gt; to &lt;em&gt;inner joins&lt;&#x2F;em&gt; if possible.&lt;&#x2F;p&gt;
&lt;p&gt;Then based on the number of tables in the query and based on the parameter &lt;code&gt;geqo_threshold&lt;&#x2F;code&gt; (default 12) one of the two method is applied:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;if n_tables &amp;gt; geqo_threshold&lt;&#x2F;code&gt; then a &lt;em&gt;genetic algorithm&lt;&#x2F;em&gt; is used. The problem is in fact subjected to combinatorial explosion and for this reason a fast but sub-optimal solution is choosed instead of a optimal but slow solution.&lt;&#x2F;li&gt;
&lt;li&gt;otherwise a dynamic programming algorithm is used.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The pseudocode for the dynamic programming algorithm is the following:&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;function find_cheapest_plan(tables[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;..n]):&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: #88846F;&quot;&gt;    # Step 1: solve all singles&lt;&#x2F;span&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; each table T:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        best_plan({T}) = cheapest way to read T&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: #88846F;&quot;&gt;    # Steps 2..n: solve increasingly larger groups&lt;&#x2F;span&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; size = &lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt; to n:&lt;&#x2F;span&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; each subset S of tables where&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span&gt;S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; = size:&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&gt;            best = infinity&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: #88846F;&quot;&gt;            # try every way to split S into two non-empty groups&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;            # where left_size &amp;lt;= right_size (to avoid duplicates)&lt;&#x2F;span&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; left_size = &lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt; to size&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 2&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;                for&lt;&#x2F;span&gt;&lt;span&gt; each subset L of S where&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span&gt;L&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; = left_size:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    R = S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; L&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: #88846F;&quot;&gt;                    # skip duplicate: when both halves are same size,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;                    # only consider L &amp;lt; R to avoid trying the same split twice&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                    if&lt;&#x2F;span&gt;&lt;span&gt; left_size = size&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; left_size&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; and&lt;&#x2F;span&gt;&lt;span&gt; L&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; R:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                        skip&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: #88846F;&quot;&gt;                    # both L and R were solved in earlier steps&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    left_plan  = best_plan(L)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    right_plan = best_plan(R)&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: #88846F;&quot;&gt;                    # try every join method in both directions&lt;&#x2F;span&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; each method&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; [nested_loop, merge_join, hash_join]:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                        cost1 = estimate(method,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; outer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;left_plan,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;  inner&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;right_plan)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                        cost2 = estimate(method,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; outer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;right_plan,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; inner&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;left_plan)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                        best = &lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt;min&lt;&#x2F;span&gt;&lt;span&gt;(best, cost1, cost2)&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&gt;            best_plan(S) = best&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: #88846F;&quot;&gt;    # the answer is the best plan for the full set&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; best_plan(tables[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;..n])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Given n tables {T₁, T₂, ..., Tₙ}, build solutions bottom-up by group size k:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;k = 1: For each table Tᵢ, find its cheapest access path (sequential scan, index scan, etc.). Store it as best_plan({Tᵢ}).&lt;&#x2F;li&gt;
&lt;li&gt;k = 2: For each pair {Tᵢ, Tⱼ}, the only split is {Tᵢ} + {Tⱼ}. Try all join methods in both directions. Store the cheapest as best_plan({Tᵢ, Tⱼ}).&lt;&#x2F;li&gt;
&lt;li&gt;k = 3: For each triple, try all 1+2 splits. Example: {A,B,C} → A+{B,C}, B+{A,C}, C+{A,B}. Both sides already solved. Keep the cheapest.&lt;&#x2F;li&gt;
&lt;li&gt;k = 4: Now two kinds of splits appear: 1+3 and 2+2. Example: {A,B,C,D} → A+{B,C,D}, ..., D+{A,B,C}, {A,B}+{C,D}, {A,C}+{B,D}, {A,D}+{B,C}. All sub-groups already solved.&lt;&#x2F;li&gt;
&lt;li&gt;k = 5..n: Same pattern. Split sizes grow: 1+(k-1), 2+(k-2), ..., up to ⌊k&#x2F;2⌋+(k-⌊k&#x2F;2⌋). Every sub-group was solved at a smaller k.&lt;&#x2F;li&gt;
&lt;li&gt;k = n: One group remains — all tables. The cheapest plan found for it is the final answer.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The key properties of this algorithm are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Every sub-problem is solved exactly once and reused many times.&lt;&#x2F;li&gt;
&lt;li&gt;All join methods and directions are tried at every split.&lt;&#x2F;li&gt;
&lt;li&gt;Total sub-problems = 2ⁿ, which explodes past n ≈ 12, hence PostgreSQL&#x27;s switch to a genetic algorithm.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This algorithm is known in the literature as &lt;strong&gt;DPsize&lt;&#x2F;strong&gt; (Dynamic Programming by size), originally introduced by Selinger et al. It produces bushy join trees, meaning it considers not only left-deep plans (where one new table is added at each step) but also plans where two independently computed sub-joins are combined (e.g., (A ⋈ B) ⋈ (C ⋈ D)).&lt;&#x2F;p&gt;
&lt;p&gt;Bushy trees can be significantly cheaper than left-deep trees in certain scenarios like when small tables can be paired together independently, keeping intermediate results small throughout.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;mutiple_table_plan.7bcf449b48de5b99.png&quot; alt=&quot;Multiple Tables Plan&quot;
     width=&quot;1280&quot; height=&quot;483&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;mutiple_table_plan.67119fa7819f65b5.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;mutiple_table_plan.6513610d00fdf7d7.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;mutiple_table_plan.1ade8812ce92b4e0.png 1280w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;h4 id=&quot;join-operations&quot;&gt;Join Operations&lt;a class=&quot;post-anchor&quot; href=&quot;#join-operations&quot; aria-label=&quot;Anchor link for: join-operations&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;PostgreSQL supports three join operations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Nested loop join&lt;&#x2F;li&gt;
&lt;li&gt;Merge join&lt;&#x2F;li&gt;
&lt;li&gt;Hash join&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The nested loop join and the merge join in PostgreSQL have several variations.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Nested Loop Join&lt;&#x2F;strong&gt; scans the inner table once for every row in the outer table, checking the join condition each time. It&#x27;s simple and works with any join condition, but can be slow for large tables.
The start-up cost is 0 while the run cost is defined primarily by the product of the sizes of the outer and inner tables.&lt;&#x2F;p&gt;
&lt;p&gt;The cost of this join is always estimated, but this join operation is rarely used because more efficient variations are usually used.&lt;&#x2F;p&gt;
&lt;p&gt;PostgreSQL has a variation called &lt;strong&gt;materialized nested loop join&lt;&#x2F;strong&gt; which reduces the total scanning cost of the inner table.&lt;&#x2F;p&gt;
&lt;p&gt;Before running a nested loop join, the executor writes the inner table tuples to &lt;code&gt;work_mem&lt;&#x2F;code&gt; or a temporary file by scanning the inner table once.
This has the potential to process the inner table tuples more efficiently than using the buffer manager, especially if at least all the tuples are written to &lt;code&gt;work_mem&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s another variation called &lt;strong&gt;indexed nested loop join&lt;&#x2F;strong&gt; which uses the index on the inner table to look up the tuples satisfying the join condition for each tuple of the outer table.&lt;&#x2F;p&gt;
&lt;p&gt;Unlike the nested loop join, the &lt;strong&gt;merge join&lt;&#x2F;strong&gt; can only be used in natural joins and equi-joins.&lt;&#x2F;p&gt;
&lt;p&gt;The basic merge join sorts both tables on the join key, then walks through them in parallel to find matches.&lt;&#x2F;p&gt;
&lt;p&gt;The startup cost comes from sorting both sides, O(N log N) each, while the actual merging is only O(N_outer + N_inner). It only works for equi-joins.&lt;&#x2F;p&gt;
&lt;p&gt;When the inner table benefits from being cached for efficient rescanning, PostgreSQL adds a Materialize node on top of the sorted inner result, producing a &lt;strong&gt;materialized merge join&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If the outer table already has an index on the join key, PostgreSQL can skip sorting it entirely and use an index scan instead, while still sorting (or materializing) the inner table. In the best case, when both tables have suitable indexes, PostgreSQL uses index scans on both sides, eliminating sorting costs altogether; this is the indexed merge join and typically the cheapest variant.&lt;&#x2F;p&gt;
&lt;p&gt;Similar to the merge join, the &lt;strong&gt;hash join&lt;&#x2F;strong&gt; can be only used in natural joins and equi-joins.&lt;&#x2F;p&gt;
&lt;p&gt;The behavior changes depending on whether the inner table fits in memory.&lt;&#x2F;p&gt;
&lt;p&gt;When the inner table is small enough (25% or less of &lt;code&gt;work_mem&lt;&#x2F;code&gt;), PostgreSQL uses a simple in-memory hash join.&lt;&#x2F;p&gt;
&lt;p&gt;It works in two phases:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The &quot;build&quot; phase scans the entire inner table and inserts every tuple into a hash table (called a &quot;batch&quot;) organized into buckets whose count is always a power of 2.&lt;&#x2F;li&gt;
&lt;li&gt;The &quot;probe&quot; phase scans the outer table, hashes each tuple&#x27;s join key, looks up the matching bucket, and joins any tuples that satisfy the condition. The overall cost is roughly O(N_outer + N_inner).&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;When the inner table is too large for a single in-memory batch, PostgreSQL switches to a &lt;strong&gt;hybrid hash join with skew&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It creates multiple batches, one in memory and the rest as temporary files on disk. Each tuple is routed to a batch based on a portion of its hash key bits.&lt;&#x2F;p&gt;
&lt;p&gt;The build and probe phases then repeat for each batch in successive rounds.&lt;&#x2F;p&gt;
&lt;p&gt;The first round processes the in-memory batch directly, while later rounds require reading and writing temp files, which is more expensive. To mitigate this, PostgreSQL adds a special &quot;skew&quot; batch that holds inner tuples expected to match the most common values (MCVs) of the outer table&#x27;s join attribute. This way, if the data distribution is skewed, say 10% of customers account for 70% of purchases, most outer tuples get processed in the cheap first round via the skew batch, avoiding costly disk I&#x2F;O in later rounds.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, the hash join can also incorporate index scans. During the probe phase, if the outer table has a usable index for a &lt;code&gt;WHERE&lt;&#x2F;code&gt; clause filter, PostgreSQL will use an index scan instead of a sequential scan to feed tuples into the probe side.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;parallel-query&quot;&gt;Parallel Query&lt;a class=&quot;post-anchor&quot; href=&quot;#parallel-query&quot; aria-label=&quot;Anchor link for: parallel-query&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;Parallel Query is a feature that processes a single query using multiple processes (Workers).&lt;&#x2F;p&gt;
&lt;p&gt;The PostgreSQL process that executes the query becomes the Leader and starts up multiple Worker processes.
Each Worker process then performs scan processing, and returns the results sequentially to the Leader process.
The Leader process aggregates the results.&lt;&#x2F;p&gt;
&lt;p&gt;The configuration parameter &lt;code&gt;parallel_leader_participation&lt;&#x2F;code&gt;, allows the Leader process to also process queries while waiting for responses from Workers. The default is on.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg-parallell-overview.355f050133a7255d.png&quot; alt=&quot;Parallel Query Overview&quot;
     width=&quot;1280&quot; height=&quot;1091&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg-parallell-overview.cee3213a0342e59b.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg-parallell-overview.82594eff1ef423fa.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;pg-parallell-overview.7fc3839825edead8.png 1280w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;ol&gt;
&lt;li&gt;Leader Creates Plan: The optimizer creates a plan that can be executed in parallel. It&#x27;s done by adding the following nodes to the plan: &lt;code&gt;GatherNode&lt;&#x2F;code&gt;, &lt;code&gt;GatherMerge&lt;&#x2F;code&gt;, &lt;code&gt;Append&lt;&#x2F;code&gt; and &lt;code&gt;AppendMerge&lt;&#x2F;code&gt;, &lt;code&gt;Finalize&#x2F;Partial Aggregate&lt;&#x2F;code&gt;. The nodes executed by Workers are generally the subplan tree below those nodes, and the parallel_safe value is set to True.&lt;&#x2F;li&gt;
&lt;li&gt;Leader Stores Shared information: To execute the plan on both the Leader and Worker nodes, the Leader stores necessary information in its Dynamic Shared Memory (DSM) area.&lt;&#x2F;li&gt;
&lt;li&gt;Leader Creates Workers.&lt;&#x2F;li&gt;
&lt;li&gt;Worker Sets up State: Each worker reads the stored shared information to sets up its state, ensuring a consistent execution environment with the Leader.&lt;&#x2F;li&gt;
&lt;li&gt;Worker Scans rows and returns results.&lt;&#x2F;li&gt;
&lt;li&gt;Leader Gathers results.&lt;&#x2F;li&gt;
&lt;li&gt;After the query finishes, the Workers are terminated, and the Leader releases the DSM area.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;concurrency-control&quot;&gt;Concurrency Control&lt;a class=&quot;post-anchor&quot; href=&quot;#concurrency-control&quot; aria-label=&quot;Anchor link for: concurrency-control&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;Above Read Committed, PostgreSQL offers two stronger isolation modes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Snapshot Isolation (Repeatable Read)&lt;&#x2F;li&gt;
&lt;li&gt;Serializable Snapshot Isolation (Serializable).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In particular it uses MVCC for SI and Optimistic Concurrency control for DML (Data manipulation Language, e.g. SELECT, UPDATE, INSERT, DELETE) while it uses
two-phase locking (2PL) for DDL (Data Definition Language, e.g. CREATE TABLE, etc).&lt;&#x2F;p&gt;
&lt;p&gt;At the start of a transaction, the transaction manager assigns a unique identifier known as a transaction ID (txid).
There are three special txid:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0&lt;&#x2F;code&gt; means &lt;code&gt;Invalid&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;1&lt;&#x2F;code&gt; means &lt;code&gt;Bootstrap&lt;&#x2F;code&gt;, which is used in the initialization of the db cluster.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;2&lt;&#x2F;code&gt; means &lt;code&gt;Frozen&lt;&#x2F;code&gt;, which is a mechanism used to handle the integer overflow of the transaction id.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Txids can be compared with each other to understand the &quot;time position&quot; of the transaction and therefore the visibility of the other transactions for the current one.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;transaction_id_wraparound.469dab64ef3f9253.png&quot; alt=&quot;Transaction Id&quot;
     width=&quot;1280&quot; height=&quot;606&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;transaction_id_wraparound.b95d825f0dd7de11.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;transaction_id_wraparound.7e486ec321fc93ae.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;transaction_id_wraparound.5f320d4c74c2ce50.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;transaction_id_wraparound.25567841363115d7.png 1920w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;transaction_id_wraparound.db4f7eb00ddf9dc6.png 2560w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;Since the txid space is insufficient in practical systems, PostgreSQL treats the txid space as a circle.&lt;&#x2F;p&gt;
&lt;p&gt;Heap tuples in table pages are of two types: usual data tuples and TOAST tuples.&lt;&#x2F;p&gt;
&lt;p&gt;In the usual data tuples, the &lt;code&gt;HeapTupleHeaderData&lt;&#x2F;code&gt; structure contains different info:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;t_xmin&lt;&#x2F;strong&gt; holds the txid of the transaction that inserted this tuple.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;t_xmax&lt;&#x2F;strong&gt; holds the txid of the transaction that deleted or updated this tuple. If this tuple has not been deleted or updated, &lt;code&gt;t_xmax&lt;&#x2F;code&gt; is set to 0, which means INVALID as seen before.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;t_cid&lt;&#x2F;strong&gt; stores the command id (cid): a zero-based counter of how many SQL commands have already run in the current transaction before the one that produced this tuple. In BEGIN; INSERT; INSERT; INSERT; COMMIT;, the three inserted tuples get &lt;code&gt;t_cid&lt;&#x2F;code&gt; values of 0, 1, and 2 respectively.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;t_ctid&lt;&#x2F;strong&gt; holds a tid that locates a tuple within the table. If the tuple has been updated, its &lt;code&gt;t_ctid&lt;&#x2F;code&gt; points to the new version; otherwise it points to itself.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;PostgreSQL does not overwrite data in place by using &quot;slotted page&quot; as heap page: line pointers grow downward from the header, and tuple data stacks upward from the bottom, with free space in between. Each tuple carries MVCC metadata described before where &lt;code&gt;t_ctid&lt;&#x2F;code&gt; act as pointer to the next version of the same row, or to itself if it&#x27;s the latest.&lt;&#x2F;p&gt;
&lt;p&gt;When multiple transactions insert rows, each new tuple simply gets appended on top of the latest tuple and a new line pointer is allocated at the top. The tuples from different transactions coexist in the same page, visibility is determined later by checking each tuple&#x27;s &lt;code&gt;t_xmin&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;t_xmax&lt;&#x2F;code&gt; against the reader&#x27;s snapshot, not by physical separation.&lt;&#x2F;p&gt;
&lt;p&gt;When a row is updated, PostgreSQL logically deletes the old tuple (writing the updating transaction&#x27;s ID into &lt;code&gt;t_xmax&lt;&#x2F;code&gt;) and inserts a brand-new tuple with the new data.
The old tuple&#x27;s &lt;code&gt;t_ctid&lt;&#x2F;code&gt; is rewritten to point to the new one, forming a version chain. This is why updates cause table bloat, every update leaves a dead tuple behind.&lt;&#x2F;p&gt;
&lt;p&gt;When a row is deleted, only &lt;code&gt;t_xmax&lt;&#x2F;code&gt; is set. The tuple remains physically in the page as a dead tuple until VACUUM reclaims the space, resetting those line pointers to &quot;unused&quot; and freeing the bytes for future inserts. The Free Space Map (FSM) then knows this page has room again.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;commit-log-clog&quot;&gt;Commit Log (CLOG)&lt;a class=&quot;post-anchor&quot; href=&quot;#commit-log-clog&quot; aria-label=&quot;Anchor link for: commit-log-clog&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;PostgreSQL holds the statuses of transactions in the Commit Log (CLOG).
It&#x27;s allocated to shared memory and is used throughout transaction processing.&lt;&#x2F;p&gt;
&lt;p&gt;There are four transaction states: IN_PROGRESS, COMMITTED, ABORTED, and SUB_COMMITTED.&lt;&#x2F;p&gt;
&lt;p&gt;The clog comprises one or more 8 KB pages in shared memory.
It logically forms an array, where the indices of the array correspond to the respective transaction ids, and each item in the array holds the status of the corresponding transaction id.&lt;&#x2F;p&gt;
&lt;p&gt;When PostgreSQL shuts down or whenever the checkpoint process runs, the data of the clog are written into files stored in the &lt;code&gt;pg_xact&lt;&#x2F;code&gt; subdirectory. (Note that &lt;code&gt;pg_xact&lt;&#x2F;code&gt; was called &lt;code&gt;pg_clog&lt;&#x2F;code&gt; in versions 9.6 or earlier.) These files are named ‘0000’, ‘0001’, and so on.&lt;&#x2F;p&gt;
&lt;p&gt;PostgreSQL flushes its transaction status data (the &#x27;clog&#x27;) to the &lt;code&gt;pg_xact&lt;&#x2F;code&gt; folder whenever a checkpoint occurs or the system shuts down.&lt;&#x2F;p&gt;
&lt;p&gt;New pages are appended to the clog as it fills, causing it to expand over time.
However, to keep the system efficient, the Vacuum process regularly prunes unnecessary old data from these pages and files.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;transaction-snapshot&quot;&gt;Transaction Snapshot&lt;a class=&quot;post-anchor&quot; href=&quot;#transaction-snapshot&quot; aria-label=&quot;Anchor link for: transaction-snapshot&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;A snapshot is a data set that records which transactions are active (either in progress or not yet started) at a specific moment. PostgreSQL uses this to determine which versions of data (tuples) a transaction is allowed to see.&lt;&#x2F;p&gt;
&lt;p&gt;PostgreSQL represents snapshots using three main components:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;xmin&lt;&#x2F;strong&gt;: The earliest transaction ID (txid) that is still active. All transactions with a lower ID are already committed or aborted.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;xmax&lt;&#x2F;strong&gt;: Any transaction with this ID or higher is considered &quot;not yet started&quot; and is invisible.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;xip_list&lt;&#x2F;strong&gt;: A list of specific active txids that fall between xmin and xmax.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;transaction_snapshot.0584ca73e391986a.png&quot; alt=&quot;Transaction Snapshot&quot;
     width=&quot;1280&quot; height=&quot;344&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;transaction_snapshot.a3f9a32e96f696ab.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;transaction_snapshot.7814e8195fda617c.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;transaction_snapshot.ad78f9d6bd88dc19.png 1280w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;The timing of when a snapshot is taken depends on the transaction&#x27;s isolation level:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;SI: A new snapshot is obtained at the start of every SQL command. This allows the transaction to see changes committed by others between commands.&lt;&#x2F;li&gt;
&lt;li&gt;SSI: A single snapshot is obtained when the first SQL command is executed and is reused for the entire transaction. This ensures the data stays consistent throughout the session.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;ssi-implementation&quot;&gt;SSI Implementation&lt;a class=&quot;post-anchor&quot; href=&quot;#ssi-implementation&quot; aria-label=&quot;Anchor link for: ssi-implementation&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;Conceptually, there are three types of conflicts: wr-conflicts (Dirty Reads), ww-conflicts (Lost Updates), and rw-conflicts.&lt;&#x2F;p&gt;
&lt;p&gt;However, wr- and ww-conflicts do not need to be considered because PostgreSQL prevents such conflicts, as shown in the previous sections.
Thus, SSI implementation in PostgreSQL only needs to consider rw-conflicts.&lt;&#x2F;p&gt;
&lt;p&gt;PostgreSQL takes the following strategy for the SSI implementation:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Record all objects (tuples, pages, relations) accessed by transactions as &lt;strong&gt;SIREAD locks&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Detect rw-conflicts using SIREAD locks whenever any heap or index tuple is written.&lt;&#x2F;li&gt;
&lt;li&gt;Abort the transaction if a serialization anomaly is detected by checking detected rw-conflicts.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A SIREAD Lock is a &lt;strong&gt;Predicate Lock&lt;&#x2F;strong&gt; which is a pair of an object and (virtual) txids stored in shared memory that record which transaction has read which object.
They exist at the tuple, page, and relation levels.&lt;&#x2F;p&gt;
&lt;p&gt;To save memory, PostgreSQL automatically aggregates granular locks: multiple tuple locks &quot;promote&quot; to a single page lock, and multiple page locks promote to a relation lock.
However, a sequential scan is an exception, it immediately applies a relation-level lock, ignoring any specific WHERE clauses or available indexes.&lt;&#x2F;p&gt;
&lt;p&gt;An &lt;strong&gt;rw-conflict&lt;&#x2F;strong&gt; tracks the relationship between a transaction that reads data and a transaction that later writes to it.
It consists of the SIREAD lock involved plus the two txids.&lt;&#x2F;p&gt;
&lt;p&gt;The system checks for these conflicts every time data is modified (via INSERT, UPDATE, or DELETE) using the &lt;code&gt;CheckForSerializableConflictIn&lt;&#x2F;code&gt; function. For instance, if Transaction A reads a row and Transaction B tries to update it, a conflict is recorded.&lt;&#x2F;p&gt;
&lt;p&gt;These records are evaluated during the execution and at the final COMMIT in pure Optimistic Concurrency control.
If the system determines that these conflicts would break &quot;serializability,&quot; it enforces a first-committer-win rule: only the first transaction to commit is allowed to finish; all others are rolled back.&lt;&#x2F;p&gt;
&lt;p&gt;Full details at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.interdb.jp&#x2F;pg&#x2F;pgsql05&#x2F;09.html&quot;&gt;Serializable Snapshot Isolation&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;required-maintenance-process&quot;&gt;Required Maintenance Process&lt;a class=&quot;post-anchor&quot; href=&quot;#required-maintenance-process&quot; aria-label=&quot;Anchor link for: required-maintenance-process&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;The concurrency control mechanism requires the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Remove dead tuples and corresponding index tuples.&lt;&#x2F;li&gt;
&lt;li&gt;Remove unnecessary parts of the clog.&lt;&#x2F;li&gt;
&lt;li&gt;Freeze old txids.&lt;&#x2F;li&gt;
&lt;li&gt;Update FSM, VM, and the statistics.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;PostgreSQL uses a 32-bit integer for transaction IDs, which means it can only track about 4.2 billion transactions.
If a row is very old and the current txid &quot;wraps around&quot; past 2.1 billion, the old row suddenly looks like it was created in the &quot;future&quot;, making it invisible to the system.&lt;&#x2F;p&gt;
&lt;p&gt;To dodge this bullet, the system employs &lt;strong&gt;Freeze Processing&lt;&#x2F;strong&gt;.
During a vacuum, PostgreSQL identifies rows that have survived a specific number of transactions and marks them as &quot;Frozen&quot;. By assigning these rows a special reserved status, either by setting their ID to a dedicated &quot;always visible&quot; value or by flipping a specific bit in the row header; this way the database ensures they stay permanently in the past.&lt;&#x2F;p&gt;
&lt;p&gt;This diligent bookkeeping allows the transaction counter to loop safely without the risk of losing access to your oldest data.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;vacuum-processing&quot;&gt;VACUUM Processing&lt;a class=&quot;post-anchor&quot; href=&quot;#vacuum-processing&quot; aria-label=&quot;Anchor link for: vacuum-processing&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;Vacuum processing performs three main tasks:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;removing dead tuples (and the index entries pointing to them)&lt;&#x2F;li&gt;
&lt;li&gt;freezing old transaction IDs to prevent txid wraparound&lt;&#x2F;li&gt;
&lt;li&gt;updating housekeeping structures like the Free Space Map (FSM), Visibility Map (VM), and various statistics.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The process is organized into three main blocks plus a post-processing phase:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scan Heap &amp;amp; Vacuum Indexes&lt;&#x2F;strong&gt;: PostgreSQL scans all pages of the target table, builds a list of dead tuples (stored in &lt;code&gt;maintenance_work_mem&lt;&#x2F;code&gt;), freezes old txids where needed, and then removes index entries that point to those dead tuples. If &lt;code&gt;maintenance_work_mem&lt;&#x2F;code&gt; fills up before scanning is complete, it processes what it has and then resumes scanning. &lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_1.6f542fd54ef3f7ed.png&quot; alt=&quot;VACUUM Step 1&quot;
     width=&quot;1280&quot; height=&quot;456&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_1.c628265498f965eb.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_1.821c5ca4c2637e5b.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_1.040136da2a85717b.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_1.2d697b9bb33ac8cd.png 1920w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Vacuum Heap&lt;&#x2F;strong&gt;: Goes page by page, removing dead tuples and compacting live ones to eliminate fragmentation. FSM and VM are updated per page. Notably, empty line pointers are not removed, they&#x27;re kept for reuse to avoid having to update all associated indexes.&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_2.c4a25bc50f4b5874.png&quot; alt=&quot;VACUUM Step 2&quot;
     width=&quot;1280&quot; height=&quot;362&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_2.94f6127b24d5a6a0.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_2.434c15a3e516b9e9.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_2.e8ea5a4564b3fd9d.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_2.426040e827786e94.png 1920w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cleanup &amp;amp; Truncation&lt;&#x2F;strong&gt;: Runs &lt;code&gt;index_vacuum_cleanup()&lt;&#x2F;code&gt; on indexes, updates per-table statistics and system catalogs, and truncates trailing empty pages from the table file to reclaim disk space. Only trailing empty pages can be removed this way; interior empty pages require &lt;strong&gt;VACUUM FULL&lt;&#x2F;strong&gt;. &lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_3.ec9d006c453402b8.png&quot; alt=&quot;VACUUM Step 3&quot;
     width=&quot;1280&quot; height=&quot;1077&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_3.a2839201072be9f1.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_3.feaef29cc84840f5.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;vacuum_3.567fecfb43ed06e2.png 1280w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Post-processing&lt;&#x2F;strong&gt;: Updates global statistics and system catalogs, and removes now-unnecessary clog files.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;code&gt;VACUUM FULL&lt;&#x2F;code&gt; compacts table files by creating a new file, copying only live tuples to it, and removing the old file, addressing the limitation of concurrent &lt;code&gt;VACUUM&lt;&#x2F;code&gt; which removes dead tuples but doesn&#x27;t reduce table size.&lt;&#x2F;p&gt;
&lt;p&gt;However, it requires exclusive access to the table during processing and temporarily uses up to twice the disk space, so it&#x27;s used when the free space ratio in a table becomes excessive.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;full_vacum.857674f94e1f9933.png&quot; alt=&quot;Full VACUUM&quot;
     width=&quot;1280&quot; height=&quot;511&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;full_vacum.3fc811e01b46e728.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;full_vacum.e3fef6ea77db56b4.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;full_vacum.78eb8782e836adbc.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;full_vacum.e308f4f7ba2bd4fd.png 1920w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;full_vacum.6224a747fcd716cf.png 2560w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;VACUUM processing is expensive. The Visibility Map (VM), reduces this cost by allowing VACUUM to skip pages that don&#x27;t need processing.&lt;&#x2F;p&gt;
&lt;p&gt;Each table has its own VM file (stored with a &lt;code&gt;_vm&lt;&#x2F;code&gt; suffix, e.g. &lt;code&gt;18751_vm&lt;&#x2F;code&gt;).
The VM tracks, per page, whether that page contains any dead tuples.
When VACUUM runs, it consults the VM and skips pages marked as clean, saving significant I&#x2F;O.&lt;&#x2F;p&gt;
&lt;p&gt;Example: A table with 3 pages where pages 0 and 2 have dead tuples but page 1 does not; VACUUM will skip page 1 entirely based on the VM.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;visibility_map.26e58e01b2e70ad6.png&quot; alt=&quot;Visibility Map&quot;
     width=&quot;1280&quot; height=&quot;1109&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;visibility_map.d1a91232fcf02004.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;visibility_map.0bda0f425448bc6e.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;visibility_map.25addbcb0a415bab.png 1280w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;In PostgreSQL 9.6, the VM was extended to also record whether all tuples on a page are frozen.
This second bit allows freeze processing (in eager mode) to skip already-frozen pages, further reducing unnecessary work.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Freeze processing&lt;&#x2F;strong&gt; prevents transaction ID (txid) wraparound, a fundamental PostgreSQL limit where txids eventually cycle back around and old tuples could be misidentified.
Tuples with sufficiently old txids are &quot;frozen&quot; to avoid this. Processing runs in one of two modes depending on various conditions:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Lazy mode&lt;&#x2F;strong&gt; (most often) which only scans pages that contain dead tuples by using the VM to skip clean pages. This mode has the limitation that it might leave some old tuples unfrozen.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Eager mode&lt;&#x2F;strong&gt; which scans all pages regardless of dead tuples. With the VM improvement from PostgreSQL 9.6 described above now eager mode can scan only the relevant pages.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;PostgreSQL attempts to remove old clog files whenever &lt;code&gt;pg_database.datfrozenxid&lt;&#x2F;code&gt; is updated (which happens at the end of eager-mode VACUUM).&lt;&#x2F;p&gt;
&lt;p&gt;The rule is: any clog file whose transactions are all older than the minimum &lt;code&gt;pg_database.datfrozenxid&lt;&#x2F;code&gt; across all databases in the cluster can be safely deleted, because every transaction in those files is already treated as frozen everywhere.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;buffer-manager&quot;&gt;Buffer Manager&lt;a class=&quot;post-anchor&quot; href=&quot;#buffer-manager&quot; aria-label=&quot;Anchor link for: buffer-manager&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 buffer manager manages data transfers between shared memory and persistent storage, and it can have a significant impact on the performance of the DBMS. It is composed of three layers:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Buffer pool&lt;&#x2F;strong&gt;: an array of 8 KB slots, one per data page&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Buffer descriptors&lt;&#x2F;strong&gt;: an array of metadata objects with one-to-one correspondence to pool slots&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Buffer table&lt;&#x2F;strong&gt;: a hash table mapping &lt;code&gt;buffer_tag&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;list[buffer_id]&lt;&#x2F;code&gt; (list is used for collisions).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It coordinates how backend processes read and write pages.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;buffer_manager.bbf5ddd48f8b3bad.png&quot; alt=&quot;Buffer Manager&quot;
     width=&quot;1280&quot; height=&quot;1238&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;buffer_manager.5b2eb66150fd105e.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;buffer_manager.094e0fea5c63472b.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;buffer_manager.f5953aa143b24206.png 1280w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;The &lt;strong&gt;buffer pool&lt;&#x2F;strong&gt; is an array where each slot stores one page of a data file.
The indices of a buffer pool array are referred to as &lt;code&gt;buffer_id&lt;&#x2F;code&gt;.
The pool stores table and index pages as well as freespace maps and visibility maps.&lt;&#x2F;p&gt;
&lt;p&gt;Each page of all data files can be assigned a unique tag: the &lt;code&gt;buffer_tag&lt;&#x2F;code&gt;.
It has five fields:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;spcOid&lt;&#x2F;code&gt; (tablespace OID)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;dbOid&lt;&#x2F;code&gt; (database OID)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;relNumber&lt;&#x2F;code&gt; (relation file number)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;forkNum&lt;&#x2F;code&gt; (fork number: 0 = table, 1 = freespace map, 2 = visibility map)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;blockNum&lt;&#x2F;code&gt; (block number within the relation)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We can hash the &lt;code&gt;buffer_tag&lt;&#x2F;code&gt; to obtain the &lt;code&gt;buffer_id&lt;&#x2F;code&gt; then retrieve the corresponding &lt;strong&gt;buffer descriptor&lt;&#x2F;strong&gt; which contains metadata about the stored page in the corresponding buffer pool slot.&lt;&#x2F;p&gt;
&lt;p&gt;The buffer descriptor contains:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;tag&lt;&#x2F;strong&gt; holds the &lt;code&gt;buffer_tag&lt;&#x2F;code&gt; of the stored page in the corresponding buffer pool slot. It is used to check if it&#x27;s the right one (remember: we have collisions in the buffer table).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;buf_id&lt;&#x2F;strong&gt; identifies the descriptor.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;content_lock&lt;&#x2F;strong&gt; is a light-weight lock that is used to control access to the associated stored page.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;freeNext&lt;&#x2F;strong&gt; is a pointer to the next descriptor to generate a freelist.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;states&lt;&#x2F;strong&gt; can hold several states and variables of the associated stored page, such as &lt;code&gt;refcount&lt;&#x2F;code&gt; and &lt;code&gt;usage_count&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The flags, &lt;code&gt;usage_count&lt;&#x2F;code&gt;, and &lt;code&gt;refcount&lt;&#x2F;code&gt; fields have been combined into a single 32-bit data (states) to use the CPU atomic operations. Therefore, the &lt;strong&gt;io_in_progress_lock&lt;&#x2F;strong&gt; and spin lock (&lt;strong&gt;buf_hdr_lock&lt;&#x2F;strong&gt;) present in version 9.5 or earlier have been removed since there is no longer a need to protect these values.&lt;&#x2F;p&gt;
&lt;p&gt;The buffer table is also protected by &lt;strong&gt;BufferMappingLocks&lt;&#x2F;strong&gt; which are used to protect  the data integrity of the buffer table.
It is held in shared mode for lookups and in exclusive mode for insertions or deletions.&lt;&#x2F;p&gt;
&lt;p&gt;To reduce contention, it is split into 128 partitions (default), each guarding a portion of hash bucket slots, allowing multiple backends to insert entries concurrently.&lt;&#x2F;p&gt;
&lt;p&gt;A backend process sends a request containing the page&#x27;s &lt;code&gt;buffer_tag&lt;&#x2F;code&gt; to the buffer manager, which returns the &lt;code&gt;buffer_id&lt;&#x2F;code&gt; of the slot storing that page, loading it from persistent storage first if necessary.&lt;&#x2F;p&gt;
&lt;p&gt;A page modified in the buffer pool but not yet flushed to storage is called a &lt;strong&gt;dirty page&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;When all buffer pool slots are occupied and the requested page is absent, the buffer manager selects a victim page using the &lt;strong&gt;clock sweep algorithm&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For flushing dirty pages, two background processes handle this asynchronously:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Checkpointer&lt;&#x2F;strong&gt;: at each checkpoint, it writes a checkpoint record to WAL and flushes all dirty pages to storage.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Background Writer&lt;&#x2F;strong&gt;: it continuously and gradually flushes dirty pages to reduce the I&#x2F;O spike at checkpoints. By default it wakes every 200 ms and writes at most 100 pages (&lt;code&gt;bgwriter_lru_maxpages&lt;&#x2F;code&gt;). Unlike the checkpointer, it focuses on pages with &lt;code&gt;usage_count&lt;&#x2F;code&gt; == 0, avoiding frequently accessed &quot;hot&quot; pages to minimise repeated I&#x2F;O.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The two processes were separated in PostgreSQL 9.2, since running both duties in one process prevented efficient final checkpoint fsyncs.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;how-the-buffer-manager-works&quot;&gt;How the Buffer Manager Works&lt;a class=&quot;post-anchor&quot; href=&quot;#how-the-buffer-manager-works&quot; aria-label=&quot;Anchor link for: how-the-buffer-manager-works&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;The entry point is &lt;code&gt;ReadBufferExtended()&lt;&#x2F;code&gt;, which handles three cases:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Page already in the pool&lt;&#x2F;strong&gt;: the buffer manager computes the hash bucket for the requested &lt;code&gt;buffer_tag&lt;&#x2F;code&gt;, acquires a shared &lt;code&gt;BufMappingLock&lt;&#x2F;code&gt;, looks up the &lt;code&gt;buffer_id&lt;&#x2F;code&gt;, pins the descriptor (incrementing &lt;code&gt;refcount&lt;&#x2F;code&gt; and &lt;code&gt;usage_count&lt;&#x2F;code&gt;), releases the lock, and returns access to the pool slot.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Page not in pool, free slot available&lt;&#x2F;strong&gt;: the buffer manager confirms the page is absent, retrieves an empty descriptor from the freelist, acquires an exclusive &lt;code&gt;BufMappingLock&lt;&#x2F;code&gt;, inserts the new entry into the buffer table, sets the &lt;code&gt;io_in_progress&lt;&#x2F;code&gt; bit, loads the page from storage, sets the valid bit, then releases the lock.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Pool full, victim selection required&lt;&#x2F;strong&gt;: the clock sweep selects a victim slot. If that page is dirty, it is flushed to storage (with WAL data written first via &lt;code&gt;XLogFlush()&lt;&#x2F;code&gt;). The old buffer table entry is removed, a new one inserted, the new page loaded, and the dirty bit cleared.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The &lt;strong&gt;Clock Sweep Algorithm&lt;&#x2F;strong&gt; is a variant of NFU (Not Frequently Used) with low overhead; it selects less frequently used pages efficiently.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;ring-buffer-and-local-buffer&quot;&gt;Ring Buffer and Local Buffer&lt;a class=&quot;post-anchor&quot; href=&quot;#ring-buffer-and-local-buffer&quot; aria-label=&quot;Anchor link for: ring-buffer-and-local-buffer&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;For large operations, PostgreSQL uses a small temporary buffer (&lt;strong&gt;Ring Buffer&lt;&#x2F;strong&gt;) in shared memory instead of the main pool, to prevent evicting cached pages.&lt;&#x2F;p&gt;
&lt;p&gt;A 256 KB ring is used for bulk sequential reads (relations larger than 1&#x2F;4 of &lt;code&gt;shared_buffers&lt;&#x2F;code&gt;) and for autovacuum; a 16 MB ring is used for bulk writes (COPY FROM, CREATE TABLE AS, materialized view operations, ALTER TABLE).&lt;&#x2F;p&gt;
&lt;p&gt;The ring is released immediately after use and can be shared between concurrent backends scanning the same relation.&lt;&#x2F;p&gt;
&lt;p&gt;Temporary tables use a per-backend local buffer (&lt;strong&gt;Local Buffer&lt;&#x2F;strong&gt;) with negatively numbered slots (-1, -2, …). Local buffers require no locks (exclusive to their backend), and their pages are neither WAL-logged nor checkpointed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;write-ahead-logging-wal&quot;&gt;Write Ahead Logging (WAL)&lt;a class=&quot;post-anchor&quot; href=&quot;#write-ahead-logging-wal&quot; aria-label=&quot;Anchor link for: write-ahead-logging-wal&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;To maintain high performance during normal operations while simultaneously ensuring data integrity in the event of a system failure, databases use transaction logs.&lt;&#x2F;p&gt;
&lt;p&gt;These logs serve as a historical record of all changes and actions within the system, enabling the database server to recover the database cluster by replaying the recorded changes and actions following a server crash.&lt;&#x2F;p&gt;
&lt;p&gt;Writing transaction logs is significantly lighter and more efficient than writing full pages, since appending to a file is one of the fastest operations.&lt;&#x2F;p&gt;
&lt;p&gt;In PostgreSQL, WAL stands for Write Ahead Log and is used interchangeably with &quot;transaction log&quot;. It also refers to the mechanism for writing actions to the transaction log. The WAL data itself is stored in &lt;strong&gt;XLOG records&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The WAL mechanism made possible the implementation of &lt;strong&gt;Point-in-Time Recovery&lt;&#x2F;strong&gt; (PITR) and &lt;strong&gt;Streaming Replication&lt;&#x2F;strong&gt; (SR), both essential features for production databases.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;how-xlog-records-are-created-and-stored&quot;&gt;How XLOG Records Are Created and Stored&lt;a class=&quot;post-anchor&quot; href=&quot;#how-xlog-records-are-created-and-stored&quot; aria-label=&quot;Anchor link for: how-xlog-records-are-created-and-stored&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;XLOG records are written to the in-memory WAL buffer by change operations such as insertion, deletion, or transaction commits. They are immediately written to a WAL segment file on storage when a transaction commits or aborts.&lt;&#x2F;p&gt;
&lt;p&gt;Each XLOG record has a &lt;strong&gt;Log Sequence Number&lt;&#x2F;strong&gt; (LSN), which represents its location in the transaction log and serves as its unique identifier.&lt;&#x2F;p&gt;
&lt;p&gt;During recovery, the database starts from the REDO point: the location of the XLOG record written when the latest checkpoint began. This is the reference point that tells PostgreSQL where to resume operations.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;wal-segment-organization&quot;&gt;WAL Segment Organization&lt;a class=&quot;post-anchor&quot; href=&quot;#wal-segment-organization&quot; aria-label=&quot;Anchor link for: wal-segment-organization&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;Since PostgreSQL&#x27;s theoretical address space is enormous, managing a single transaction log file is impractical. Instead, the transaction log is divided into 16 MB files called WAL segments. The address space is structured so that some bits represent the segment file, while others represent the position within it, making the system organized and efficient.&lt;&#x2F;p&gt;
&lt;p&gt;You can use the &lt;code&gt;pg_walfile_name()&lt;&#x2F;code&gt; function to find which segment file contains a specific transaction record.&lt;&#x2F;p&gt;
&lt;p&gt;A WAL segment is internally divided into pages of 8 KB each. Each page contains a header with metadata, followed by XLOG records written sequentially from the beginning of the page.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;wal_segment.e451169022ee311e.png&quot; alt=&quot;WAL Segment&quot;
     width=&quot;1280&quot; height=&quot;548&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;wal_segment.30a413c02ad4ae0b.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;wal_segment.876086f789f29ae2.png 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;wal_segment.c7b30ad6deb3ce21.png 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;wal_segment.da49dd14c7f1df8d.png 1920w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;h4 id=&quot;xlog-record-structure&quot;&gt;XLOG Record Structure&lt;a class=&quot;post-anchor&quot; href=&quot;#xlog-record-structure&quot; aria-label=&quot;Anchor link for: xlog-record-structure&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;All XLOG records have a general header portion defined by the &lt;code&gt;XLogRecord&lt;&#x2F;code&gt; structure:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;typedef&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; XLogRecord&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;        uint32          xl_tot_len;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;             &#x2F;* total len of entire record *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        TransactionId   xl_xid;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;                 &#x2F;* transaction id *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        XLogRecPtr      xl_prev;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;                &#x2F;* ptr to previous record in log *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        uint8           xl_info;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;                &#x2F;* flag bits *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        RmgrId          xl_rmid;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;                &#x2F;* resource manager for this record *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;        &#x2F;* 2 bytes of padding here, initialize to zero *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        pg_crc32c       xl_crc;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;                 &#x2F;* CRC for this record *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;        &#x2F;* XLogRecordBlockHeaders and XLogRecordDataHeader follow, no padding *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;} XLogRecord;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;xl_rmid&lt;&#x2F;code&gt; and &lt;code&gt;xl_info&lt;&#x2F;code&gt; fields identify which resource manager should handle the record. Resource managers are collections of operations that define how XLOG records are written and replayed during recovery.&lt;&#x2F;p&gt;
&lt;p&gt;As of recent versions PostgreSQL includes the following resource managers:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Operation&lt;&#x2F;th&gt;&lt;th&gt;Resource Manager&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Heap tuple operations&lt;&#x2F;td&gt;&lt;td&gt;RM_HEAP, RM_HEAP2&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Index operations&lt;&#x2F;td&gt;&lt;td&gt;RM_BTREE, RM_HASH, RM_GIN, RM_GIST, RM_SPGIST, RM_BRIN&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Sequence operations&lt;&#x2F;td&gt;&lt;td&gt;RM_SEQ&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Transaction operations&lt;&#x2F;td&gt;&lt;td&gt;RM_XACT, RM_MULTIXACT, RM_CLOG, RM_XLOG, RM_COMMIT_TS&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Tablespace operations&lt;&#x2F;td&gt;&lt;td&gt;RM_SMGR, RM_DBASE, RM_TBLSPC, RM_RELMAP&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Replication and hot standby operations&lt;&#x2F;td&gt;&lt;td&gt;RM_STANDBY, RM_REPLORIGIN, RM_GENERIC_ID, RM_LOGICALMSG_ID&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Here are representative examples of how resource managers work:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;When an INSERT statement is issued, the header variables &lt;code&gt;xl_rmid&lt;&#x2F;code&gt; and &lt;code&gt;xl_info&lt;&#x2F;code&gt; are set to RM_HEAP and XLOG_HEAP_INSERT, respectively. During recovery, the RM_HEAP resource manager calls its &lt;code&gt;heap_xlog_insert()&lt;&#x2F;code&gt; function to replay this XLOG record.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;For an UPDATE statement, &lt;code&gt;xl_info&lt;&#x2F;code&gt; is set to XLOG_HEAP_UPDATE, and the &lt;code&gt;heap_xlog_update()&lt;&#x2F;code&gt; function replays the record during recovery.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;When a transaction commits, &lt;code&gt;xl_rmid&lt;&#x2F;code&gt; and &lt;code&gt;xl_info&lt;&#x2F;code&gt; are set to RM_XACT and XLOG_XACT_COMMIT, respectively, and the &lt;code&gt;xact_redo_commit()&lt;&#x2F;code&gt; function replays the record during recovery.&lt;&#x2F;p&gt;
&lt;p&gt;PostgreSQL changed its XLOG record structure in version 9.5, introducing a common format that all resource managers can use. This improved maintainability and enables features like &lt;strong&gt;WAL compression&lt;&#x2F;strong&gt;, which compresses full-page images to save disk space.&lt;&#x2F;p&gt;
&lt;p&gt;While compression does require additional CPU resources during logging, it reduces I&#x2F;O overhead and WAL file consumption, making it worthwhile for databases with heavy write workloads.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;wal-writer-process&quot;&gt;WAL Writer Process&lt;a class=&quot;post-anchor&quot; href=&quot;#wal-writer-process&quot; aria-label=&quot;Anchor link for: wal-writer-process&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;The WAL writer is a background process that periodically checks the WAL buffer and writes all unwritten XLOG records to WAL segment files. This prevents bottlenecks that could occur when large amounts of data are committed simultaneously.&lt;&#x2F;p&gt;
&lt;p&gt;The WAL writer is enabled by default and cannot be disabled. It checks the buffer at intervals specified by the &lt;code&gt;wal_writer_delay&lt;&#x2F;code&gt; parameter, which defaults to 200 milliseconds.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;checkpoint-processing&quot;&gt;Checkpoint Processing&lt;a class=&quot;post-anchor&quot; href=&quot;#checkpoint-processing&quot; aria-label=&quot;Anchor link for: checkpoint-processing&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;A checkpoint is a crucial maintenance operation that occurs periodically to prepare the database for recovery and clean up memory.&lt;&#x2F;p&gt;
&lt;p&gt;Checkpoints are triggered when:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The interval time specified by &lt;code&gt;checkpoint_timeout&lt;&#x2F;code&gt; (default 300 seconds) has elapsed since the previous checkpoint.&lt;&#x2F;li&gt;
&lt;li&gt;The total size of WAL segment files exceeds the &lt;code&gt;max_wal_size&lt;&#x2F;code&gt; threshold (default 1 GB).&lt;&#x2F;li&gt;
&lt;li&gt;The server shuts down.&lt;&#x2F;li&gt;
&lt;li&gt;A superuser manually issues the CHECKPOINT command.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;When a checkpoint starts, PostgreSQL performs the following steps:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Records the REDO point—the location where recovery should begin.&lt;&#x2F;li&gt;
&lt;li&gt;Flushes all data from shared memory to storage.&lt;&#x2F;li&gt;
&lt;li&gt;Gradually writes all dirty pages from the buffer pool to disk.&lt;&#x2F;li&gt;
&lt;li&gt;Writes a checkpoint record to the WAL containing the REDO point and other critical information.&lt;&#x2F;li&gt;
&lt;li&gt;Updates the &lt;code&gt;pg_control&lt;&#x2F;code&gt; file with the latest checkpoint location and metadata.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4 id=&quot;the-pg-control-file&quot;&gt;The pg_control File&lt;a class=&quot;post-anchor&quot; href=&quot;#the-pg-control-file&quot; aria-label=&quot;Anchor link for: the-pg-control-file&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;The &lt;code&gt;pg_control&lt;&#x2F;code&gt; file is essential for recovery, it is the database&#x27;s record of where the last checkpoint occurred. It stores fundamental information including the location of the latest checkpoint record, the database state at that time, and transaction IDs needed for recovery.&lt;&#x2F;p&gt;
&lt;p&gt;If the &lt;code&gt;pg_control&lt;&#x2F;code&gt; file becomes corrupted, PostgreSQL cannot start recovery because it lacks a reference point. This makes it one of the most critical files in the database cluster.&lt;&#x2F;p&gt;
&lt;h2 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;h2&gt;
&lt;p&gt;Now that the groundwork is laid, the natural next step is understanding how TimescaleDB builds on PostgreSQL to handle high-frequency time series data more efficiently. We&#x27;ll get into that in the next post.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>One-click homelab: integrating Gitlab, Proxmox and K8s with GitOps Principles</title>
          <pubDate>Mon, 08 Dec 2025 00:00:00 +0000</pubDate>
          <author>Andrea Straforini</author>
          <link>https://strafo.net/blog/iac-homelab/</link>
          <guid>https://strafo.net/blog/iac-homelab/</guid>
          <description xml:base="https://strafo.net/blog/iac-homelab/">&lt;p&gt;One unlucky day, I destroyed my homelab while I was trying to upgrade various components. I had a backup of my virtual machines where my Kubernetes cluster was running, but unfortunately, I forgot to enable the backup of the secondary disk attached to each VM. This secondary disk was used to store all the persistent data of my cluster, including the Longhorn volumes. I thought that the backups were fine, but when I restored the VMs, I realized that the secondary disks were missing.
As a result, I lost all my persistent data, and I had to start from scratch.&lt;&#x2F;p&gt;
&lt;p&gt;My homelab was at least 3&#x2F;4 years old, and I had accumulated a lot of configurations, customizations, and data over time. I started it while approaching the DevOps world, so it didn&#x27;t really follow best practices. Rebuilding everything from scratch was a daunting task, and I realized that I needed a better way to manage my homelab infrastructure.&lt;&#x2F;p&gt;
&lt;p&gt;It might sound incredible, but while it might be acceptable for a personal homelab, losing data due to a lack of proper backup strategies is a common issue in the industry as well.
Some notable examples include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;South Korea&#x27;s NIRS &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.techspot.com&#x2F;news&#x2F;109799-858tb-gone-forever-south-korea-data-center-fire.html&quot;&gt;lost 858TB of government data&lt;&#x2F;a&gt; due to not having an offsite backup&lt;&#x2F;li&gt;
&lt;li&gt;Gitlab lost 6 hours of data due to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;blog&#x2F;gitlab-dot-com-database-incident&#x2F;&quot;&gt;improper backup procedures&lt;&#x2F;a&gt; and a lack of verification&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Given the experience I gathered over the years, I decided to rebuild my homelab using Infrastructure as Code (IaC) principles. This time, I wanted to ensure that I could easily recreate my entire setup with just one click, without having to go through the tedious process of manual configuration.
So my objective was not just having a backup of my infrastructure, but having a recovery time objective (RTO) of minutes.&lt;&#x2F;p&gt;
&lt;p&gt;This way, I can also brag about having a better infrastructure than most companies out there! 😄&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-to-expect-from-this-blog-post&quot;&gt;What to Expect from This Blog Post&lt;a class=&quot;post-anchor&quot; href=&quot;#what-to-expect-from-this-blog-post&quot; aria-label=&quot;Anchor link for: what-to-expect-from-this-blog-post&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 this blog post you will find my personal solution to address the creation of a personal homelab following IaC principles.
In particular, I will show you how I&#x27;ve successfully integrated open source tools with minimal cost and custom code to achieve a one-click deployment of my homelab infrastructure.&lt;&#x2F;p&gt;
&lt;p&gt;The solution has the following properties:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;abbr title=&quot;Recovery Time Objective&quot;&gt;RTO&lt;&#x2F;abbr&gt; of 1 hour&lt;&#x2F;li&gt;
&lt;li&gt;&lt;abbr title=&quot;Recovery Point Objective&quot;&gt;RPO&lt;&#x2F;abbr&gt; of 1 day (can be customized)&lt;&#x2F;li&gt;
&lt;li&gt;Cost effective (less than 10$ &#x2F; month in my case; depends on how much data you have to backup to the offsite location)&lt;&#x2F;li&gt;
&lt;li&gt;Privacy oriented (more later)&lt;&#x2F;li&gt;
&lt;li&gt;Fully automated using GitOps principles&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The tech stack of the solution includes:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;&quot;&gt;GitLab SaaS&lt;&#x2F;a&gt;: I use it for simplicity, but you can also use your own GitLab instance at your discretion.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.proxmox.com&#x2F;en&#x2F;&quot;&gt;Proxmox&lt;&#x2F;a&gt;: I use it in my homelab, but you can use any Hypervisor you prefer, the only important thing is that there is an OpenTofu provider for that.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;OpenTofu.org&#x2F;&quot;&gt;OpenTofu&lt;&#x2F;a&gt;: Necessary to create the various component of the solution. OpenTofu is the open-source fork of Terraform, which I use for infrastructure provisioning.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;documentation.ubuntu.com&#x2F;server&#x2F;explanation&#x2F;clouds&#x2F;find-cloud-images&#x2F;&quot;&gt;Ubuntu Cloud Images&lt;&#x2F;a&gt;: I use Ubuntu Cloud Images as the base operating system for the VMs in my Proxmox cluster. These images are optimized for cloud environments and provide an automated way to provision VMs with Cloud-Init.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kubespray.io&#x2F;#&#x2F;&quot;&gt;KubeSpray&lt;&#x2F;a&gt;: I use KubeSpray to create the Kubernetes cluster on top of Proxmox VMs. KubeSpray is a popular open-source project that provides a set of Ansible playbooks for deploying and managing Kubernetes clusters.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fluxcd.io&#x2F;flux&#x2F;&quot;&gt;FluxCD&lt;&#x2F;a&gt;: I use FluxCD for GitOps management of the Kubernetes cluster. FluxCD is a popular open-source project that enables continuous delivery and GitOps for Kubernetes.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bitnami-labs&#x2F;sealed-secrets&quot;&gt;Sealed Secrets&lt;&#x2F;a&gt;: I use Sealed Secrets for storing all the credentials directly on git. While for an homelab solution is more than enough, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;openbao.org&#x2F;&quot;&gt;OpenBao&lt;&#x2F;a&gt; might be a better solution for an Enterprise.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;longhorn.io&#x2F;&quot;&gt;Longhorn&lt;&#x2F;a&gt;: I use Longhorn as the storage solution for the Kubernetes cluster. Longhorn is a popular open-source project that provides a distributed block storage system for Kubernetes.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;storage&quot;&gt;GCP Cloud Storage&lt;&#x2F;a&gt;: In my solution I use GCP cloud storage for off-site backup. Please note that the solution I will provide ensures client-side encryption of data (so even Google will not be able to decipher your data). Additionally, you can use any NFS server  or self-hosted S3 Object store solution like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;garagehq.deuxfleurs.fr&#x2F;&quot;&gt;Garage&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;recovery-time-breakdown&quot;&gt;Recovery Time Breakdown&lt;a class=&quot;post-anchor&quot; href=&quot;#recovery-time-breakdown&quot; aria-label=&quot;Anchor link for: recovery-time-breakdown&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;Here&#x27;s what happens during a full disaster recovery (1 hour RTO):&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;┌─────────────────────────────────────────────────────────────────┐&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│ Full Infrastructure Recovery Timeline                           │&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;│                                                                 │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│ Parallel:                                                       │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  GCP Bucket ████████                                            │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  (4-5 min, ~8% - runs in parallel)                              │&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;│ Main Flow:                                                      │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  0min                                                     60min │&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;│  │         │                        │       │              │    │&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;│  VM       K8s                    Sealed  Flux          Flux     │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  Prov.    Bootstrap              Secrets Deploy        Reconcile│&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;│  9-10     23                     1-2   3-4              20-25   │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  min      min                    min   min              min     │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  (~16%)   (~38%)                 (~2%) (~6%)            (~38%)  │&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;│  Total: ~60 minutes                                             │&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Phase Details:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├─ GCP Bucket (4-5 min): Create&#x2F;verify backup storage [RUNS IN PARALLEL]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├─ VM Provisioning (9-10 min): Download Ubuntu Cloud Images, create VMs with Cloud-Init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├─ K8s Bootstrap (23 min): KubeSpray cluster deployment (long phase - ~38% of total time)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├─ Sealed Secrets (1-2 min): Deploy secret management controller&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├─ FluxCD Deploy (3-4 min): Install GitOps operator and sync repositories&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└─ FluxCD Reconcile (20-25 min): Complete reconciliation of cluster state from GitOps repos (long phase - ~38% of total time)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; The Kubernetes bootstrapping phase accounts for approximately 38% of the total RTO. The GCP bucket creation runs in parallel with VM provisioning, so it doesn&#x27;t add to the overall recovery time. Also it might be possible to optimize the FluxCD reconciliation time by tweaking its configuration for more aggressive syncs.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a time-lapse of the complete infrastructure deployment from scratch to a running cluster (~60 minutes compressed):&lt;&#x2F;p&gt;
&lt;div class=&quot;video-container&quot;&gt;
    &lt;video width=&quot;100%&quot; controls preload=&quot;metadata&quot;&gt;
        
        
        &lt;source src=&quot;&amp;#x2F;gitops&amp;#x2F;deploy.mp4&quot; type=&quot;video&#x2F;mp4&quot;&gt;
        
        Your browser doesn&#x27;t support the video tag.
    &lt;&#x2F;video&gt;
&lt;&#x2F;div&gt;

&lt;style&gt;
    .video-container {
        margin: 2rem 0;
    }
&lt;&#x2F;style&gt;&lt;h2 id=&quot;repositories-structure&quot;&gt;Repositories Structure&lt;a class=&quot;post-anchor&quot; href=&quot;#repositories-structure&quot; aria-label=&quot;Anchor link for: repositories-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;All the infrastructure code is organized in five repositories:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;gitlab-runner&lt;&#x2F;strong&gt; repository: contains the OpenTofu code to create the Gitlab Runner on Proxmox as an LXC container.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;IaC&lt;&#x2F;strong&gt; repository: contains all the code necessary to bootstrap the Proxmox VMs and the Kubernetes cluster using KubeSpray.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;d2-fleet&lt;&#x2F;strong&gt; repository: defines the desired state of the Kubernetes clusters and tenants in the fleet.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;d2-infra&lt;&#x2F;strong&gt; repository: defines the desired state of the cluster add-ons and the monitoring stack.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;d2-apps&lt;&#x2F;strong&gt; repositories: defines the desired state of the applications deployed across environments.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;We will examine the IaC repository in detail, the &lt;code&gt;d2-*&lt;&#x2F;code&gt; repositories simply apply the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fluxcd.control-plane.io&#x2F;guides&#x2F;d2-architecture-reference&#x2F;&quot;&gt;d2-reference-architecture&lt;&#x2F;a&gt; provided by the FluxCD team, which, I must say, is very well thought out and implemented. 👍&lt;&#x2F;p&gt;
&lt;p&gt;The Gitlab Runner repository is also quite straightforward, as it only contains the OpenTofu code to create the LXC container and register the runner with Gitlab, so I will not cover it here.&lt;&#x2F;p&gt;
&lt;!-- In the resource section I will provide the links to all the repositories so you can check them out by yourself. TODO: add repositories zip links --&gt;
&lt;h3 id=&quot;how-they-work-together&quot;&gt;How They Work Together&lt;a class=&quot;post-anchor&quot; href=&quot;#how-they-work-together&quot; aria-label=&quot;Anchor link for: how-they-work-together&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 deployment flow follows a clear progression:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;gitlab-runner&lt;&#x2F;strong&gt; → Bootstraps the CI&#x2F;CD infrastructure needed to run the automated pipelines&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;IaC&lt;&#x2F;strong&gt; → Handles the foundational layer: VMs, backup storage, Kubernetes cluster, and essential components (Sealed Secrets, FluxCD operator)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;d2-fleet, d2-infra, d2-apps&lt;&#x2F;strong&gt; → Once the IaC pipeline completes, FluxCD takes over and continuously reconciles the cluster state based on these GitOps repositories&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;In essence, the &lt;strong&gt;IaC repository gets you from an empty Hypervisor to a GitOps-ready cluster&lt;&#x2F;strong&gt;, then the &lt;strong&gt;d2-repositories manage everything from that point forward&lt;&#x2F;strong&gt;. This separation means the IaC pipeline only needs to run for infrastructure changes and periodic disaster recovery tests, while the d2 repositories handle all day-to-day operations through FluxCD&#x27;s automatic reconciliation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;iac-pipeline&quot;&gt;IaC Pipeline&lt;a class=&quot;post-anchor&quot; href=&quot;#iac-pipeline&quot; aria-label=&quot;Anchor link for: iac-pipeline&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 high-level steps of provisioning the homelab infrastructure performed by the IaC pipeline are:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Creates the GCP Cloud Storage bucket for offsite backups.&lt;&#x2F;li&gt;
&lt;li&gt;Creates the Proxmox VMs using OpenTofu.&lt;&#x2F;li&gt;
&lt;li&gt;Bootstraps the Kubernetes cluster using KubeSpray.&lt;&#x2F;li&gt;
&lt;li&gt;Uploads the generated Kubeconfig file to GitLab as an artifact with restricted access.&lt;&#x2F;li&gt;
&lt;li&gt;Trigger the deployment of Sealed Secrets sub-pipeline.&lt;&#x2F;li&gt;
&lt;li&gt;Trigger the deployment of FluxCD sub-pipeline.&lt;&#x2F;li&gt;
&lt;li&gt;Longhorn will be deployed by FluxCD as part of the d2-infra repository.&lt;&#x2F;li&gt;
&lt;li&gt;Restore Jobs defined in the d2-infra repository will restore Longhorn volumes from the GCP Cloud Storage bucket.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;gcp-cloud-storage-bucket&quot;&gt;GCP Cloud Storage Bucket&lt;a class=&quot;post-anchor&quot; href=&quot;#gcp-cloud-storage-bucket&quot; aria-label=&quot;Anchor link for: gcp-cloud-storage-bucket&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 only prerequisites to create this part of the pipeline are:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create an OpenTofu service account with the necessary permissions to create and manage GCP Cloud Storage buckets.&lt;&#x2F;li&gt;
&lt;li&gt;Create a GitLab Personal Access Token with the necessary permissions to modify CI&#x2F;CD variables in the GitLab project.
&lt;strong&gt;Note:&lt;&#x2F;strong&gt; I had to use a &lt;abbr title=&quot;Personal Access Token&quot;&gt;PAT&lt;&#x2F;abbr&gt; because I&#x27;m on the free tier of GitLab SaaS, which does not yet support &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;user&#x2F;project&#x2F;settings&#x2F;project_access_tokens.html&quot;&gt;Project Access Tokens&lt;&#x2F;a&gt;. If you have a paid plan, you should use a Project Access Token instead.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The process the pipeline follows is:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;If not already present, it creates the GCP Cloud Storage bucket using &lt;code&gt;google_storage_bucket&lt;&#x2F;code&gt; resource.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;hcl&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;text-decoration: underline;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;google_storage_bucket&amp;quot; &amp;quot;longhorn_backup_bucket&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;    name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                        =&lt;&#x2F;span&gt;&lt;span&gt; var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;gcp_backup_longhorn_bucket_name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;    location&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                    =&lt;&#x2F;span&gt;&lt;span&gt; var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;gcp_backup_region&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;    storage_class&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;NEARLINE&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt;    uniform_bucket_level_access&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span&gt;    public_access_prevention&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;enforced&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span style=&quot;color: #000000;background-color: #FFFFFF;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;text-decoration: underline;&quot;&gt;hierarchical_namespace&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span&gt;        enabled&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 9&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;It creates a &lt;code&gt;longhorn_backup_service_account&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Assigns to the newly created service account the &lt;code&gt;roles&#x2F;storage.objectAdmin&lt;&#x2F;code&gt; role for the created bucket.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Creates&#x2F;Syncs a HMAC key for the service account.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;The GitLab Pipeline will save the generated HMAC key as a CI&#x2F;CD variable in the GitLab project. In my case, it will use the Access Token created in the pre-requisites step to do so.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This HMAC key is later injected as a secret into the Kubernetes cluster, allowing Longhorn to connect to the GCP Cloud Storage bucket.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;proxmox-vms&quot;&gt;Proxmox VMs&lt;a class=&quot;post-anchor&quot; href=&quot;#proxmox-vms&quot; aria-label=&quot;Anchor link for: proxmox-vms&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 OpenTofu provider used is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;search.OpenTofu.org&#x2F;provider&#x2F;bpg&#x2F;proxmox&#x2F;latest&quot;&gt;bpg&#x2F;proxmox&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The prerequisites to create this part of the pipeline are:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create a Proxmox API token with the necessary permissions to create and manage VMs.&lt;&#x2F;li&gt;
&lt;li&gt;Create an SSH key pair and store the private key as a GitLab CI&#x2F;CD variable. This key will be used by the on-premise GitLab Runner to connect to the Proxmox VMs.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The pipeline follows this process:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;OpenTofu makes Proxmox download the Ubuntu Cloud Image using &lt;code&gt;proxmox_virtual_environment_download_file&lt;&#x2F;code&gt; resource.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;hcl&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;text-decoration: underline;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;proxmox_virtual_environment_download_file&amp;quot; &amp;quot;ubuntu_24_noble_qcow2_img&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;content_type&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;iso&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;datastore_id&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;nfs-nas-1&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;node_name&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;pve2&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                =&lt;&#x2F;span&gt;&lt;span&gt; var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;proxmox_k8s_node_image_url&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span&gt;overwrite&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span&gt;file_name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          =&lt;&#x2F;span&gt;&lt;span&gt; var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;proxmox_k8s_node_image_name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span&gt;checksum&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;           =&lt;&#x2F;span&gt;&lt;span&gt; var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;proxmox_k8s_node_image_checksum&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 9&lt;&#x2F;span&gt;&lt;span&gt;checksum_algorithm&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;proxmox_k8s_node_image_checksum_algorithm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;OpenTofu creates the Cloud-Init configuration file for the worker and master nodes using &lt;code&gt;proxmox_virtual_environment_file&lt;&#x2F;code&gt; resource.
The important configurations to add to the Cloud-Init file are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SSH Keys:&lt;&#x2F;strong&gt; Inject both your personal public key and the GitLab Runner&#x27;s public key&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Package Management:&lt;&#x2F;strong&gt; Install necessary packages (like &lt;code&gt;qemu-guest-agent&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Longhorn Prerequisites:&lt;&#x2F;strong&gt; Configure according to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;longhorn.io&#x2F;docs&#x2F;1.10.1&#x2F;nodes-and-volumes&#x2F;nodes&#x2F;node-conditions&#x2F;&quot;&gt;Longhorn documentation&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
 &lt;details&gt;
 &lt;summary&gt;Code snippet (worker nodes)&lt;&#x2F;summary&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;hcl&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;text-decoration: underline;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;proxmox_virtual_environment_file&amp;quot; &amp;quot;ubuntu_cloud_init_worker&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;content_type&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;snippets&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;datastore_id&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;nfs-nas-1&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;node_name&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;pve2&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt;overwrite&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;text-decoration: underline;&quot;&gt;source_raw&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span&gt;    data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      = &amp;lt;&amp;lt;EOF&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;#cloud-config&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 9&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;users:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;- default&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;11&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;- name: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;proxmox_k8s_node_username&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    groups:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;13&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - sudo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    shell: &#x2F;bin&#x2F;bash&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    ssh_authorized_keys:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;%{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;~&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;proxmox_k8s_node_ssh_keys&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;17&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;18&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;%{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;~&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;endfor}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;19&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    sudo: ALL=(ALL) NOPASSWD:ALL&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;20&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;21&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;package_update: true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;22&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;package_upgrade: true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;23&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;packages:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;24&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;- qemu-guest-agent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;- nfs-common&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;26&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;27&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;# Disk partitioning setup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;28&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;disk_setup:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;29&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&#x2F;dev&#x2F;sdb:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    table_type: gpt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;31&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    layout: true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;32&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    overwrite: false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;33&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;34&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;# Filesystem setup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;35&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;fs_setup:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;36&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;- label: data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;37&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    filesystem: ext4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;38&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    device: &#x2F;dev&#x2F;sdb1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;39&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    partition: auto&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;40&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    overwrite: false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;41&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;42&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;# Mount configuration&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;43&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;mounts:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;44&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;- [&#x2F;dev&#x2F;sdb1, &#x2F;mnt&#x2F;data, ext4, &amp;quot;defaults,nofail&amp;quot;, &amp;quot;0&amp;quot;, &amp;quot;2&amp;quot;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;45&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;46&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;write_files:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;47&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;- path: &#x2F;etc&#x2F;modules-load.d&#x2F;dm_crypt.conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;48&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    content: |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;49&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    dm_crypt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;50&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    owner: root:root&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;51&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    permissions: &amp;#39;0644&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;52&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;53&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    runcmd:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;54&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - systemctl enable qemu-guest-agent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;55&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - systemctl start qemu-guest-agent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;56&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - systemctl stop multipathd.socket&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;57&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - systemctl stop multipathd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;58&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - systemctl disable multipathd.socket&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;59&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - systemctl disable multipathd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - systemctl mask multipathd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;61&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - systemctl mask multipathd.socket&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;62&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - modprobe dm_crypt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;63&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - systemctl enable iscsid&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - systemctl start iscsid&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;65&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    - echo &amp;quot;done&amp;quot; &amp;gt; &#x2F;tmp&#x2F;cloud-config.done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;66&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;EOF&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;67&lt;&#x2F;span&gt;&lt;span&gt;      file_name&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;ubuntu.cloud-config-worker.yaml&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;68&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;69&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt; &lt;&#x2F;details&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;OpenTofu generates the output &lt;strong&gt;kubespray_inventory&lt;&#x2F;strong&gt; using the &lt;code&gt;templatefile&lt;&#x2F;code&gt; function with inventory.tpl.
You can customize the inventory.tpl to fit your needs following the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kubespray.io&#x2F;#&#x2F;docs&#x2F;ansible&#x2F;inventory&quot;&gt;KubeSpray documentation&lt;&#x2F;a&gt;. Here&#x27;s my version of the inventory.tpl file.&lt;&#x2F;p&gt;
 &lt;details&gt;
 &lt;summary&gt;inventory.tpl&lt;&#x2F;summary&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 1&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;all&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;vars&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;ansible_user&amp;quot;: &amp;quot;${ansible_user}&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;ansible_become&amp;quot;: true,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;calico_cni_name&amp;quot;: &amp;quot;k8s-pod-network&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;nat_outgoing&amp;quot;: true,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;nat_outgoing_ipv6&amp;quot;: true,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 9&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;calico_pool_blocksize&amp;quot;: 26,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;calico_network_backend&amp;quot;: &amp;quot;vxlan&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;11&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;calico_vxlan_mode&amp;quot;: &amp;quot;CrossSubnet&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;12&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;kube_proxy_strict_arp&amp;quot;: true,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;13&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;kube_encrypt_secret_data&amp;quot;: true,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;kubeconfig_localhost&amp;quot;: true,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;artifacts_dir&amp;quot;: &amp;quot;&#x2F;output&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;etcd_deployment_type&amp;quot;: &amp;quot;host&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;17&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;etcd_metrics_port&amp;quot;: 2381,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;18&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;etcd_listen_metrics_urls&amp;quot;: &amp;quot;http:&#x2F;&#x2F;0.0.0.0:2381&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;19&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;etcd_metrics_service_labels&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;k8s-app&amp;quot;: &amp;quot;etcd&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;21&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;app.kubernetes.io&#x2F;managed-by&amp;quot;: &amp;quot;kubespray&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;22&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;app&amp;quot;: &amp;quot;kube-prometheus-stack-kube-etcd&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;23&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;release&amp;quot;: &amp;quot;kube-prometheus-stack&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;24&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;kube_proxy_metrics_bind_address&amp;quot;: &amp;quot;0.0.0.0:10249&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;26&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;27&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;children&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;28&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;kube_control_plane&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;29&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;hosts&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span&gt;    %{ for name in master_nodes ~}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;31&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;${name}&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;32&lt;&#x2F;span&gt;&lt;span&gt;    %{ endfor ~}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;33&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;34&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;35&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;etcd&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;36&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;hosts&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;37&lt;&#x2F;span&gt;&lt;span&gt;    %{ for name in master_nodes ~}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;38&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;${name}&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;39&lt;&#x2F;span&gt;&lt;span&gt;    %{ endfor ~}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;40&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;41&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;42&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;kube_node&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;43&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;hosts&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;44&lt;&#x2F;span&gt;&lt;span&gt;    %{ for name in worker_nodes ~}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;45&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;${name}&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;46&lt;&#x2F;span&gt;&lt;span&gt;    %{ endfor ~}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;47&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;48&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;49&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;k8s_cluster&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;50&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;children&amp;quot;: [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;51&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;kube_control_plane&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;52&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;kube_node&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;53&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;54&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;55&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;56&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;57&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt; &lt;&#x2F;details&gt;
&lt;p&gt;IMHO, the most important thing here is to &lt;strong&gt;limit Kubespray to install only the necessary components&lt;&#x2F;strong&gt; to have a minimal Kubernetes cluster ready for FluxCD deployment.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Saves the generated inventory file as a GitLab artifact for use in the next stage.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;kubernetes-cluster-bootstrapping&quot;&gt;Kubernetes Cluster Bootstrapping&lt;a class=&quot;post-anchor&quot; href=&quot;#kubernetes-cluster-bootstrapping&quot; aria-label=&quot;Anchor link for: kubernetes-cluster-bootstrapping&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;This part of the pipeline bootstraps the Kubernetes cluster using KubeSpray.&lt;&#x2F;p&gt;
&lt;p&gt;I think KubeSpray is the best solution for creating a production-ready Kubernetes cluster in an on-premise environment, but I also think it should be limited to only the necessary components for a working cluster. KubeSpray, which is based on Ansible, provides many options to install various components like CNI, Ingress controllers, and monitoring stacks. However, in my opinion, these components should be installed using a more mature GitOps tool like FluxCD.&lt;&#x2F;p&gt;
&lt;p&gt;The prerequisites for this part of the pipeline are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A Personal Access Token with API scope to upload the kubeconfig file as a GitLab CI&#x2F;CD variable. As already mentioned, if you have a paid plan, you should use a Project Access Token instead. Additionally, you can use the same token created for the GCP Bucket creation step.&lt;&#x2F;li&gt;
&lt;li&gt;The GitLab Runner needs to connect to the Proxmox VMs using SSH. The private key for the SSH connection should be stored as a GitLab CI&#x2F;CD variable in the project, as already mentioned in the Proxmox VMs creation step.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The pipeline follows these steps:&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;apply-kubespray-production-home&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  stage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; deploy&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}&#x2F;docker:cli&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  tags&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; mgmt-zone&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; self-hosted&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  needs&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; job&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; opentofu-apply-production-home&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 9&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      artifacts&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  services&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;11&lt;&#x2F;span&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt;dind&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  before_script&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;13&lt;&#x2F;span&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; apk add --no-interactive jq&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  script&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; mkdir -p $CI_PROJECT_DIR&#x2F;inventory&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; mkdir -p output&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;17&lt;&#x2F;span&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; chmod 600 $RUNNER_SSH_PRIVATE_KEY_PATH&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;18&lt;&#x2F;span&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; jq -r &amp;#39;.kubespray_inventory.value&amp;#39; home-tf-output.json &amp;gt; $CI_PROJECT_DIR&#x2F;inventory&#x2F;inventory.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;19&lt;&#x2F;span&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; docker run --mount type=bind,source=$CI_PROJECT_DIR&#x2F;inventory,dst=&#x2F;inventory --mount type=bind,source=$RUNNER_SSH_PRIVATE_KEY_PATH,dst=&#x2F;ssh&#x2F;id --mount type=bind,source=$CI_PROJECT_DIR&#x2F;output,dst=&#x2F;output --rm quay.io&#x2F;kubespray&#x2F;kubespray:$KUBESPRAY_VERSION ansible-playbook -i &#x2F;inventory&#x2F;inventory.json --private-key &#x2F;ssh&#x2F;id  cluster.yml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  environment&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;21&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; production&#x2F;home&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;22&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  artifacts&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;23&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    when&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; on_success&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;24&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    access&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; developer&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    expire_in&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;10 mins&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;26&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    paths&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;27&lt;&#x2F;span&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; output&#x2F;**&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This step leverages the official KubeSpray Docker image to run the Ansible playbooks against the Proxmox VMs created in the previous step. It then saves the generated &lt;code&gt;kubeconfig&lt;&#x2F;code&gt; file as a restricted-access artifact for use in subsequent pipeline stages.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;uploading-kubeconfig-to-gitlab&quot;&gt;Uploading Kubeconfig to Gitlab&lt;a class=&quot;post-anchor&quot; href=&quot;#uploading-kubeconfig-to-gitlab&quot; aria-label=&quot;Anchor link for: uploading-kubeconfig-to-gitlab&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;This step uploads the generated &lt;code&gt;kubeconfig&lt;&#x2F;code&gt; file to GitLab as a CI&#x2F;CD variable using the GitLab API directly.&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;.upload-secret-base64-encoded&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}&#x2F;alpine&#x2F;curl:latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  script&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      set -e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      DATA=$(base64 -w 0 ${DATA_FILE_PATH})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      curl -s -f --request PUT \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        --header &amp;quot;PRIVATE-TOKEN: ${ACCESS_TOKEN}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 9&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        --header &amp;quot;Content-Type: application&#x2F;json&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        --data &amp;quot;{\&amp;quot;variable_type\&amp;quot;:\&amp;quot;file\&amp;quot;,\&amp;quot;key\&amp;quot;:\&amp;quot;${VAR_NAME}\&amp;quot;,\&amp;quot;value\&amp;quot;:\&amp;quot;$DATA\&amp;quot;,\&amp;quot;hidden\&amp;quot;:false,\&amp;quot;protected\&amp;quot;:true,\&amp;quot;masked\&amp;quot;:true,\&amp;quot;raw\&amp;quot;:true,\&amp;quot;description\&amp;quot;:\&amp;quot;\&amp;quot;}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;11&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        &amp;quot;$CI_API_V4_URL&#x2F;projects&#x2F;$CI_PROJECT_ID&#x2F;variables&#x2F;${VAR_NAME}&amp;quot; &amp;gt; &#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This way, after the IaC pipeline finishes, the &lt;code&gt;kubeconfig&lt;&#x2F;code&gt; file will be available for administrators to download and use.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;sealed-secrets-deployment&quot;&gt;Sealed Secrets Deployment&lt;a class=&quot;post-anchor&quot; href=&quot;#sealed-secrets-deployment&quot; aria-label=&quot;Anchor link for: sealed-secrets-deployment&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;This part of the pipeline creates the Sealed Secrets controller in the Kubernetes cluster.&lt;&#x2F;p&gt;
&lt;p&gt;The prerequisite is a certificate&#x2F;key pair to be used by the Sealed Secrets controller. You can find the instructions here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bitnami-labs&#x2F;sealed-secrets&#x2F;blob&#x2F;main&#x2F;docs&#x2F;bring-your-own-certificates.md&quot;&gt;Bring your own certificates&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The private key needs to be stored as a GitLab CI&#x2F;CD variable, while the certificate can be stored directly in the IaC repository.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fluxcd-deployment&quot;&gt;FluxCD Deployment&lt;a class=&quot;post-anchor&quot; href=&quot;#fluxcd-deployment&quot; aria-label=&quot;Anchor link for: fluxcd-deployment&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;This part of the pipeline uses OpenTofu to deploy the FluxCD Operator in the Kubernetes cluster as described in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fluxcd.control-plane.io&#x2F;operator&#x2F;install&#x2F;&quot;&gt;official documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This step also creates the Kubernetes secret with the GCP HMAC key for Longhorn and the secret with the registry credentials to pull container images from private registries.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;longhorn-volumes-restoration-process&quot;&gt;Longhorn Volumes Restoration Process&lt;a class=&quot;post-anchor&quot; href=&quot;#longhorn-volumes-restoration-process&quot; aria-label=&quot;Anchor link for: longhorn-volumes-restoration-process&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;Unfortunately, at the time of writing, Longhorn does not support a declarative way to restore volumes from offsite backups (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;longhorn&#x2F;longhorn&#x2F;issues&#x2F;5787&quot;&gt;issue#5787&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;To work around this limitation, I have created a simple Dockerized Python program that leverages the Longhorn API to restore the latest backup for defined volumes from offsite storage. You can find the repository &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Strafo&#x2F;longhorn-backup-restore&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Simply define a Job in the &lt;code&gt;d2-infra&lt;&#x2F;code&gt; repository for each volume you want to restore. For example, here is the Job definition to restore the Authelia Longhorn volume:&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 1&lt;&#x2F;span&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; batch&#x2F;v1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 2&lt;&#x2F;span&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; Job&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 3&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 4&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; authelia-volume-restore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 5&lt;&#x2F;span&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; authelia&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 6&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  template&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 8&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 9&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      containers&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;10&lt;&#x2F;span&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; restore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;11&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; your-registry&#x2F;longhorn-backup-restore:latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          env&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;13&lt;&#x2F;span&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; LONGHORN_URL&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              value&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; http:&#x2F;&#x2F;longhorn-frontend.longhorn-system.svc.cluster.local&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;15&lt;&#x2F;span&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; VOLUME_HANDLE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              value&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; authelia-production-vol&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;17&lt;&#x2F;span&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; NUMBER_OF_REPLICAS&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;18&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              value&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;3&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;19&lt;&#x2F;span&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; LOG_LEVEL&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              value&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; INFO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;21&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      restartPolicy&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; Never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;22&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  backoffLimit&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then you need to create the corresponding &lt;abbr title=&quot;persistent volume&quot;&gt;PV&lt;&#x2F;abbr&gt; and &lt;abbr title=&quot;persistent volume claim&quot;&gt;PVC&lt;&#x2F;abbr&gt; to use the restored volume in your application. These can be defined in the &lt;code&gt;d2-infra&lt;&#x2F;code&gt; repository, for example in the same file as the restore job.&lt;&#x2F;p&gt;
&lt;p&gt;You can find more information in the README of the repository.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;encrypt-longhorn-backups-client-side&quot;&gt;Encrypt Longhorn Backups Client-Side&lt;a class=&quot;post-anchor&quot; href=&quot;#encrypt-longhorn-backups-client-side&quot; aria-label=&quot;Anchor link for: encrypt-longhorn-backups-client-side&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;Unfortunately, at the time of writing, Longhorn does not support client-side encryption of backups natively (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;longhorn&#x2F;longhorn&#x2F;issues&#x2F;5220&quot;&gt;issue#5220&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;A simple solution I found is to use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rclone.org&#x2F;&quot;&gt;rclone&lt;&#x2F;a&gt; to encrypt the backups client-side before uploading them to the offsite backup location.&lt;&#x2F;p&gt;
&lt;p&gt;Simply declare a Deployment, a Service, some Secrets, and a one-time Job in the same namespace where Longhorn is installed.&lt;&#x2F;p&gt;
&lt;p&gt;The important bits in the&lt;&#x2F;p&gt;
&lt;details&gt;
&lt;summary&gt;deployment manifest&lt;&#x2F;summary&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 1&lt;&#x2F;span&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; apps&#x2F;v1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 2&lt;&#x2F;span&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; Deployment&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 3&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 4&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; s3-rclone-longhorn-bck&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  labels&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    app.kubernetes.io&#x2F;name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; s3-rclone-longhorn-bck&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 7&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  replicas&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 9&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  selector&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    matchLabels&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;11&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      app.kubernetes.io&#x2F;name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; s3-rclone-longhorn-bck&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  template&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;13&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      labels&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        app.kubernetes.io&#x2F;name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; s3-rclone-longhorn-bck&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;16&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;17&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      containers&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;18&lt;&#x2F;span&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; rclone&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;19&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; ghcr.io&#x2F;rclone&#x2F;rclone:1.71.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;20&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;21&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          command&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;22&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;rclone&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;23&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;serve&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;24&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;s3&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;--no-cleanup&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;26&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;--auth-key&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;27&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;$(RC_ACCESS_KEY_ID),$(RC_ACCESS_KEY)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;28&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;crypt_out_s3:&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;29&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;--s3-force-path-style=true&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;--addr=:8080&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;31&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;--log-level=WARNING&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;32&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          env&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;33&lt;&#x2F;span&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; RCLONE_CONFIG_OUT_S3_ACCESS_KEY_ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;34&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              valueFrom&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;35&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                secretKeyRef&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;36&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; longhorn-backup-hmac-key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;37&lt;&#x2F;span&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_id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;38&lt;&#x2F;span&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; RCLONE_CONFIG_OUT_S3_SECRET_ACCESS_KEY&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;39&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              valueFrom&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;40&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                secretKeyRef&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;41&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; longhorn-backup-hmac-key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;42&lt;&#x2F;span&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&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;43&lt;&#x2F;span&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; RCLONE_CONFIG_CRYPT_OUT_S3_PASSWORD&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;44&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              valueFrom&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;45&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                secretKeyRef&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;46&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; rclone-secret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;47&lt;&#x2F;span&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; password&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;48&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;            # salt is used as password2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;49&lt;&#x2F;span&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; RCLONE_CONFIG_CRYPT_OUT_S3_PASSWORD2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;50&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              valueFrom&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;51&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                secretKeyRef&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;52&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; rclone-secret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;53&lt;&#x2F;span&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; salt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;54&lt;&#x2F;span&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; RC_ACCESS_KEY_ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;55&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              valueFrom&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;56&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                secretKeyRef&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;57&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; longhorn-rclone-bck-key-secret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;58&lt;&#x2F;span&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; AWS_ACCESS_KEY_ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;59&lt;&#x2F;span&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; RC_ACCESS_KEY&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              valueFrom&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;61&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                secretKeyRef&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;62&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; longhorn-rclone-bck-key-secret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;63&lt;&#x2F;span&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; AWS_SECRET_ACCESS_KEY&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          ports&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;65&lt;&#x2F;span&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; http&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;66&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              containerPort&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 8080&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;67&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              protocol&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; TCP&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;68&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          volumeMounts&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;69&lt;&#x2F;span&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; config&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;70&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              mountPath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &#x2F;root&#x2F;.config&#x2F;rclone&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;71&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      volumes&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;72&lt;&#x2F;span&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; config&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;73&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          configMap&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;74&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; s3-rclone-longhorn-bck-config&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;75&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;            items&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;76&lt;&#x2F;span&gt;&lt;span&gt;              -&lt;&#x2F;span&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; rclone.conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;77&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                path&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; rclone.conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;78&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;details&gt;
&lt;p&gt;and in the&lt;&#x2F;p&gt;
&lt;details&gt;
&lt;summary&gt;rclone config&lt;&#x2F;summary&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;out_s3&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;type = s3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;provider = GCS&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;endpoint = https:&#x2F;&#x2F;storage.googleapis.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;region = europe-west4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;use_multipart_uploads = false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;crypt_out_s3&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 9&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;type = crypt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;remote = out_s3:&amp;lt;BUCKET-NAME-REDACTED&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;11&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;directory_name_encryption = false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;filename_encryption = off&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;details&gt;
&lt;p&gt;I used are&lt;&#x2F;p&gt;
&lt;h4 id=&quot;why-we-use-rclone-serve-s3&quot;&gt;Why We Use &lt;code&gt;rclone serve s3&lt;&#x2F;code&gt;&lt;a class=&quot;post-anchor&quot; href=&quot;#why-we-use-rclone-serve-s3&quot; aria-label=&quot;Anchor link for: why-we-use-rclone-serve-s3&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;code&gt;rclone serve s3&lt;&#x2F;code&gt; is the key component that makes this solution work. It implements a basic S3-compatible server that exposes any rclone backend (in our case, the encrypted &lt;code&gt;crypt_out_s3&lt;&#x2F;code&gt; remote) as an S3 endpoint.&lt;&#x2F;p&gt;
&lt;p&gt;This is essential because:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;S3 Gateway&lt;&#x2F;strong&gt;: Longhorn expects to communicate with an S3-compatible storage backend for backups. By using &lt;code&gt;rclone serve s3&lt;&#x2F;code&gt;, we provide Longhorn with a standard S3 interface.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Encryption layer&lt;&#x2F;strong&gt;: By pointing the S3 server at the &lt;code&gt;crypt_out_s3&lt;&#x2F;code&gt; remote, all data is transparently encrypted&#x2F;decrypted as it passes through rclone&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;No Longhorn modifications&lt;&#x2F;strong&gt;: This approach requires zero changes to Longhorn itself—it just sees a standard S3 endpoint&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Authentication&lt;&#x2F;strong&gt;: The &lt;code&gt;--auth-key&lt;&#x2F;code&gt; parameter allows us to secure the S3 endpoint with credentials that Longhorn can use&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The command essentially creates an S3 gateway that sits between Longhorn and the actual storage backend, handling all encryption&#x2F;decryption automatically.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;understanding-the-rclone-crypt-options&quot;&gt;Understanding the rclone crypt Options&lt;a class=&quot;post-anchor&quot; href=&quot;#understanding-the-rclone-crypt-options&quot; aria-label=&quot;Anchor link for: understanding-the-rclone-crypt-options&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;The crypt remote configuration uses two important settings:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;directory_name_encryption = false&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This keeps directory names in plaintext (unencrypted). While this reduces security slightly, it has practical benefits:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Easier to navigate the bucket structure directly if needed&lt;&#x2F;li&gt;
&lt;li&gt;Simpler debugging—you can see the folder structure at a glance&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The actual file data is still fully encrypted, so the main content security is preserved.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;filename_encryption = off&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With this setting, files only get a &lt;code&gt;.bin&lt;&#x2F;code&gt; extension added instead of having their filenames encrypted. This provides several advantages:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Shorter encrypted filenames, avoiding path length limits on some cloud storage providers&lt;&#x2F;li&gt;
&lt;li&gt;Easier to identify files when accessing the backend directly&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Security trade-off:&lt;&#x2F;strong&gt; This setting trades some security for practicality. If you need maximum security, you could use &lt;code&gt;standard&lt;&#x2F;code&gt; encryption, which encrypts filenames completely.&lt;&#x2F;p&gt;
&lt;p&gt;Both options make the encrypted remote more manageable and reduce the risk of hitting storage provider limitations while keeping the actual file content fully encrypted.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Next, create the corresponding Service and a simple Job to initialize the backup bucket.&lt;&#x2F;p&gt;
&lt;p&gt;The important bits in the&lt;&#x2F;p&gt;
&lt;details&gt;
&lt;summary&gt;job manifest&lt;&#x2F;summary&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;  1&lt;&#x2F;span&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; batch&#x2F;v1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;  2&lt;&#x2F;span&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; Job&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;  3&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;  4&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; s3-rclone-longhorn-bck-init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;  5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  labels&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;  6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    app.kubernetes.io&#x2F;name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; s3-rclone-longhorn-bck&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;  7&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    app.kubernetes.io&#x2F;component&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;  8&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;  9&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  backoffLimit&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  template&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 11&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      labels&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 13&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        app.kubernetes.io&#x2F;name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; s3-rclone-longhorn-bck&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 14&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        app.kubernetes.io&#x2F;component&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 15&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 16&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      restartPolicy&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; OnFailure&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 17&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      containers&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 18&lt;&#x2F;span&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; rclone-init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 19&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; ghcr.io&#x2F;rclone&#x2F;rclone:1.71.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 20&lt;&#x2F;span&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 21&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          command&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 22&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;&#x2F;bin&#x2F;sh&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 23&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;-c&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 24&lt;&#x2F;span&gt;&lt;span&gt;            -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 25&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              set -e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 26&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 27&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Waiting for rclone S3 service to be ready...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 28&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              # limit to 10 minutes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 29&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              COUNTER=0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 30&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              until nc -z s3-rclone-longhorn-bck 8080 2&amp;gt;&#x2F;dev&#x2F;null; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 31&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                echo &amp;quot;Waiting for service...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 32&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                sleep 10&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 33&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                COUNTER=`expr $COUNTER + 1`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 34&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                if [ $COUNTER -ge 60 ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 35&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  echo &amp;quot;Timeout waiting for service after 10 minutes&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 36&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  exit 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 37&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 38&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 39&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Service is ready!&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 40&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              COUNTER=0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 41&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 42&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Using bucket name: $${BUCKET_NAME}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 43&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 44&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              # Set up the remote for the local rclone S3 service (encrypted)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 45&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              export RCLONE_CONFIG_LOCAL_S3_TYPE=s3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 46&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              export RCLONE_CONFIG_LOCAL_S3_PROVIDER=Other&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 47&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              export RCLONE_CONFIG_LOCAL_S3_ENDPOINT=http:&#x2F;&#x2F;s3-rclone-longhorn-bck:8080&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 48&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              export RCLONE_CONFIG_LOCAL_S3_ACCESS_KEY_ID=&amp;quot;$${RC_ACCESS_KEY_ID}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 49&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              export RCLONE_CONFIG_LOCAL_S3_SECRET_ACCESS_KEY=&amp;quot;$${RC_ACCESS_KEY}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 50&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              export RCLONE_CONFIG_LOCAL_S3_FORCE_PATH_STYLE=true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 51&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 52&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              export RCLONE_CONFIG_OUT_S3_USE_MULTIPART_UPLOADS=false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 53&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              export RCLONE_CONFIG_OUT_S3_NO_CHECK_BUCKET=true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 54&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              export RCLONE_CONFIG_OUT_S3_ACCESS_KEY_ID=&amp;quot;$${BACKEND_S3_ACCESS_KEY_ID}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 55&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              export RCLONE_CONFIG_OUT_S3_SECRET_ACCESS_KEY=&amp;quot;$${BACKEND_S3_SECRET_ACCESS_KEY}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 56&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 57&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              # Check if INFO.txt already exists in the backend (unencrypted)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 58&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Checking if INFO.txt already exists in backend...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 59&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              if rclone lsf &amp;quot;out_s3:$${BUCKET_NAME}&#x2F;INFO.txt&amp;quot; 2&amp;gt;&#x2F;dev&#x2F;null | grep -q &amp;quot;INFO.txt&amp;quot;; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 60&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                echo &amp;quot;INFO.txt already exists in backend - initialization already complete&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 61&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                echo &amp;quot;Bucket is ready for Longhorn backups&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 62&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                exit 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 63&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 65&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              # Check if bucket exists (via encrypted endpoint)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 66&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Checking if bucket exists...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 67&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              if rclone lsd local_s3: 2&amp;gt;&#x2F;dev&#x2F;null | grep -q &amp;quot;$${BUCKET_NAME}&amp;quot;; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 68&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                echo &amp;quot;Bucket &amp;#39;$${BUCKET_NAME}&amp;#39; already exists&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 69&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 70&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                # Check if bucket contains any encrypted data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 71&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                echo &amp;quot;Checking bucket contents (encrypted view)...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 72&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                FILE_COUNT=$$(rclone ls &amp;quot;local_s3:$${BUCKET_NAME}&#x2F;&amp;quot; 2&amp;gt;&#x2F;dev&#x2F;null | wc -l)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 73&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 74&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                if [ &amp;quot;$$FILE_COUNT&amp;quot; -gt 0 ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 75&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  echo &amp;quot;Bucket contains $${FILE_COUNT} encrypted file(s)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 76&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  echo &amp;quot;Listing existing files:&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 77&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  rclone ls &amp;quot;local_s3:$${BUCKET_NAME}&#x2F;&amp;quot; --max-depth 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 78&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 79&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 80&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                echo &amp;quot;Creating new bucket: $${BUCKET_NAME}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 81&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                rclone mkdir &amp;quot;local_s3:$${BUCKET_NAME}&amp;quot; --log-level=INFO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 82&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                echo &amp;quot;Bucket created successfully&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 83&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 84&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 85&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Listing all buckets...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 86&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              rclone lsd local_s3: --log-level=INFO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 87&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 88&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Generating INFO.txt file...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 89&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              TIMESTAMP=$$(date -u +&amp;quot;%Y-%m-%d %H:%M:%S UTC&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 90&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              HOSTNAME=$$(hostname)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 91&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 92&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              cat &amp;gt; &#x2F;tmp&#x2F;INFO.txt &amp;lt;&amp;lt;EOF&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 93&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              ============================================&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 94&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              Longhorn Backup Bucket Information&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 95&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              ============================================&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 96&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 97&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              Bucket Name: $${BUCKET_NAME}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 98&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              Created: $${TIMESTAMP}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt; 99&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              Created By: $${HOSTNAME}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;100&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;101&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              Configuration:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;102&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Service: s3-rclone-longhorn-bck&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;103&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Endpoint: http:&#x2F;&#x2F;s3-rclone-longhorn-bck:8080&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;104&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Encryption: Enabled (rclone crypt)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;105&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Backend: Google Cloud Storage (GCS)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;106&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Region: europe-west4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;107&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;108&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              Rclone Configuration:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;109&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Remote: crypt_out_s3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;110&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Base Remote: out_s3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;111&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Encryption: Standard encryption with password and salt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;112&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Directory Name Encryption: Disabled&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;113&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Filename Encryption: Disabled&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;114&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;115&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              Environment:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;116&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Kubernetes Namespace: $${K8S_NAMESPACE}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;117&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Init Job: s3-rclone-longhorn-bck-init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;118&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;119&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              Notes:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;120&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - All data stored in this bucket is encrypted using rclone crypt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;121&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Access requires proper HMAC credentials (stored in secrets)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;122&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - Encryption password and salt are required for decryption&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;123&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - INFO.txt is stored UNENCRYPTED for easy access&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;124&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;125&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              Secrets Used:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;126&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - longhorn-backup-hmac-key: GCS HMAC credentials&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;127&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - rclone-secret: Encryption password and salt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;128&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - longhorn-rclone-bck-key-secret: API access credentials&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;129&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;130&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              ============================================&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;131&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              EOF&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;132&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;133&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Uploading INFO.txt to backend GCS (UNENCRYPTED)...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;134&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              rclone copy &#x2F;tmp&#x2F;INFO.txt &amp;quot;out_s3:$${BUCKET_NAME}&#x2F;&amp;quot; --log-level=INFO --s3-no-check-bucket&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;135&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;136&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Verifying upload...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;137&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Files in encrypted view:&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;138&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              rclone ls &amp;quot;local_s3:$${BUCKET_NAME}&#x2F;&amp;quot; --log-level=INFO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;139&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;140&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Files in unencrypted backend:&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;141&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              rclone ls &amp;quot;out_s3:$${BUCKET_NAME}&#x2F;&amp;quot; --max-depth 1 --log-level=INFO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;142&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;143&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Initialization complete!&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;144&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Bucket &amp;#39;$${BUCKET_NAME}&amp;#39; is ready for Longhorn backups&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;145&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;INFO.txt is available unencrypted in the backend storage&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;146&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          env&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;147&lt;&#x2F;span&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; BUCKET_NAME&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;148&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              value&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; ~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;149&lt;&#x2F;span&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; RCLONE_CONFIG_OUT_S3_TYPE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;150&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              value&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; ~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;151&lt;&#x2F;span&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; RCLONE_CONFIG_OUT_S3_PROVIDER&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;152&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              value&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; ~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;153&lt;&#x2F;span&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; RCLONE_CONFIG_OUT_S3_ENDPOINT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;154&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              value&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; ~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;155&lt;&#x2F;span&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; RCLONE_CONFIG_OUT_S3_REGION&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;156&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              value&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; ~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;157&lt;&#x2F;span&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; RCLONE_CONFIG_OUT_S3_USE_MULTIPART_UPLOADS&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;158&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              value&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; ~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;159&lt;&#x2F;span&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; K8S_NAMESPACE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;160&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              valueFrom&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;161&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                fieldRef&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;162&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                  fieldPath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; metadata.namespace&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;163&lt;&#x2F;span&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; RC_ACCESS_KEY_ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;164&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              valueFrom&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;165&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                secretKeyRef&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;166&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; longhorn-rclone-bck-key-secret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;167&lt;&#x2F;span&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; AWS_ACCESS_KEY_ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;168&lt;&#x2F;span&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; RC_ACCESS_KEY&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;169&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              valueFrom&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;170&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                secretKeyRef&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;171&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; longhorn-rclone-bck-key-secret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;172&lt;&#x2F;span&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; AWS_SECRET_ACCESS_KEY&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;173&lt;&#x2F;span&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; BACKEND_S3_ACCESS_KEY_ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;174&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              valueFrom&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;175&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                secretKeyRef&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;176&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; longhorn-backup-hmac-key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;177&lt;&#x2F;span&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_id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;178&lt;&#x2F;span&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; BACKEND_S3_SECRET_ACCESS_KEY&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;179&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;              valueFrom&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;180&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                secretKeyRef&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 aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;181&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; longhorn-backup-hmac-key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #90908A;&quot;&gt;182&lt;&#x2F;span&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&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;details&gt;
&lt;p&gt;are:&lt;&#x2F;p&gt;
&lt;h4 id=&quot;why-we-generate-the-info-txt-file&quot;&gt;Why We Generate the INFO.txt File&lt;a class=&quot;post-anchor&quot; href=&quot;#why-we-generate-the-info-txt-file&quot; aria-label=&quot;Anchor link for: why-we-generate-the-info-txt-file&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;The INFO.txt file serves several important purposes:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Documentation&lt;&#x2F;strong&gt;: It provides human-readable information about the bucket configuration, encryption setup, and required credentials. This is invaluable when you need to restore or troubleshoot backups months or years later.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Accessible without encryption&lt;&#x2F;strong&gt;: Critically, INFO.txt is stored directly in the backend (&lt;code&gt;out_s3&lt;&#x2F;code&gt;), bypassing the encryption layer. This means it can be read without needing the encryption keys, making it a self-documenting backup location.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verification&lt;&#x2F;strong&gt;: By uploading it to the backend, we verify that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The GCS backend connection works correctly&lt;&#x2F;li&gt;
&lt;li&gt;Credentials are properly configured&lt;&#x2F;li&gt;
&lt;li&gt;The bucket is accessible and writable&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Recovery aid&lt;&#x2F;strong&gt;: If you ever lose your rclone configuration but still have the encryption passwords in a password manager, the INFO.txt file tells you exactly how the encryption was configured. This makes it possible to reconstruct the setup and recover your backups.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Idempotency check&lt;&#x2F;strong&gt;: The script checks for INFO.txt existence to determine if initialization has already been completed, preventing duplicate initialization runs.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4 id=&quot;why-we-create-the-bucket-with-rclone-mkdir-local-s3-bucket-name&quot;&gt;Why We Create the Bucket with &lt;code&gt;rclone mkdir &quot;local_s3:${BUCKET_NAME}&quot;&lt;&#x2F;code&gt;&lt;a class=&quot;post-anchor&quot; href=&quot;#why-we-create-the-bucket-with-rclone-mkdir-local-s3-bucket-name&quot; aria-label=&quot;Anchor link for: why-we-create-the-bucket-with-rclone-mkdir-local-s3-bucket-name&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;Creating the bucket through the &lt;code&gt;local_s3:&lt;&#x2F;code&gt; remote (the encrypted S3 endpoint) rather than directly on the backend has several advantages:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;End-to-end testing&lt;&#x2F;strong&gt;: This verifies the entire encryption pipeline is working:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The rclone serve s3 service is running and accessible&lt;&#x2F;li&gt;
&lt;li&gt;Authentication is correctly configured&lt;&#x2F;li&gt;
&lt;li&gt;The crypt layer is properly set up&lt;&#x2F;li&gt;
&lt;li&gt;The underlying GCS backend is reachable&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;S3 API validation&lt;&#x2F;strong&gt;: It ensures that bucket creation operations work through the S3 API layer, which is exactly how Longhorn will interact with the system. If &lt;code&gt;rclone mkdir&lt;&#x2F;code&gt; succeeds through &lt;code&gt;local_s3:&lt;&#x2F;code&gt;, we know Longhorn&#x27;s S3 operations will also work.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consistent access path&lt;&#x2F;strong&gt;: By creating the bucket the same way Longhorn will access it (through the S3 API), we ensure there are no surprises or incompatibilities when Longhorn starts using the bucket.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Automatic bucket initialization&lt;&#x2F;strong&gt;: On GCS, when you create a &quot;bucket&quot; through rclone&#x27;s S3 interface, it actually creates a folder&#x2F;prefix in the specified GCS bucket (configured as &lt;code&gt;&amp;lt;BUCKET-NAME-REDACTED&amp;gt;&lt;&#x2F;code&gt; in the config). This happens automatically through the crypt layer.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Proper permissions verification&lt;&#x2F;strong&gt;: This confirms that the service account credentials (&lt;code&gt;RC_ACCESS_KEY_ID&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;RC_ACCESS_KEY&lt;&#x2F;code&gt;) have the necessary permissions to create buckets through the S3 interface.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;rto-and-rpo&quot;&gt;RTO and RPO&lt;a class=&quot;post-anchor&quot; href=&quot;#rto-and-rpo&quot; aria-label=&quot;Anchor link for: rto-and-rpo&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;With this setup, you can easily modify the RPO by adjusting the Longhorn backup schedule to fit your needs.&lt;&#x2F;p&gt;
&lt;p&gt;For RTO, it mainly depends on the pipeline execution time. The main time-consuming components are the Kubernetes cluster bootstrapping using KubeSpray and the FluxCD reconciliation process.&lt;&#x2F;p&gt;
&lt;p&gt;In my case, the total time to provision the entire infrastructure from scratch and reconcile the cluster state is around 1 hour, which works well for my use case and is also acceptable for most organizations.&lt;&#x2F;p&gt;
&lt;p&gt;To lower the RTO further, you could customize FluxCD timeouts, retry periods, and parallelism to achieve more aggressive reconciliation.&lt;&#x2F;p&gt;
&lt;p&gt;However, I&#x27;m planning to set up a disaster recovery site for even better redundancy. More on this in the &quot;Next Steps&quot; section.&lt;&#x2F;p&gt;
&lt;h2 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;h2&gt;
&lt;p&gt;This journey from a homelab data loss incident to a production-grade IaC setup taught me that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Recovery Time Objectives aren&#x27;t just for enterprises&lt;&#x2F;li&gt;
&lt;li&gt;GitOps principles significantly reduce operational burden in the long run&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;While the initial setup took about a month (including research, testing, and iteration), I can now rebuild my entire infrastructure in 1 hour. More importantly, I&#x27;ve eliminated the anxiety of &quot;did I back that up?&quot;—everything is code, versioned, and reproducible.&lt;&#x2F;p&gt;
&lt;p&gt;The total monthly cost (~€10 for GCS storage) is minimal compared to the value of reliable, reproducible infrastructure. If you&#x27;re running a homelab, I encourage you to treat it like production—your future self will thank you.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;limitations&quot;&gt;Limitations&lt;a class=&quot;post-anchor&quot; href=&quot;#limitations&quot; aria-label=&quot;Anchor link for: limitations&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;As you might have noticed, some components are not yet part of the automated rebuild process.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Infrastructure Assumptions:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Proxmox Host:&lt;&#x2F;strong&gt; The Proxmox hypervisor itself is treated as static infrastructure and is not recreated from scratch&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;DNS Records:&lt;&#x2F;strong&gt; External DNS records for services are assumed to be pre-configured&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Network configuration:&lt;&#x2F;strong&gt; The underlying network infrastructure (VLANs, subnets, firewall rules) is not managed by the IaC pipeline&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;While this is acceptable for a homelab, an enterprise-grade setup should include these components in the IaC pipeline as well. For on-premises environments, this presents additional challenges:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Hypervisor bootstrapping requires out-of-band management (IPMI&#x2F;iLO)&lt;&#x2F;li&gt;
&lt;li&gt;Network configuration can be scripted using OpenTofu, which leverages Proxmox Software Defined Networks&lt;&#x2F;li&gt;
&lt;li&gt;DNS automation depends on your DNS provider&#x27;s API availability&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These omissions mean a true &quot;datacenter destroyed&quot; scenario still requires some manual intervention. However, for the more common scenarios (VM corruption, cluster misconfiguration, accidental deletion), the current setup provides comprehensive protection.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;a class=&quot;post-anchor&quot; href=&quot;#next-steps&quot; aria-label=&quot;Anchor link for: next-steps&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;While Google Cloud is a great option and my monthly cost is only ~€10, in the future I would like to explore &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.hetzner.com&#x2F;&quot;&gt;Hetzner&lt;&#x2F;a&gt;. The pricing is really competitive and they have an S3-compatible object storage service. They also have an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;search.OpenTofu.org&#x2F;provider&#x2F;OpenTofu&#x2F;hcloud&#x2F;latest&quot;&gt;OpenTofu provider&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Another area I would like to explore is leveraging &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;longhorn.io&#x2F;docs&#x2F;1.10.1&#x2F;snapshots-and-backups&#x2F;setup-disaster-recovery-volumes&#x2F;&quot;&gt;Longhorn Disaster Recovery Volumes&lt;&#x2F;a&gt; in conjunction with the FluxCD &lt;code&gt;d2&lt;&#x2F;code&gt; architecture. This way, I might be able to create a recovery cluster in another location and have a more robust disaster recovery plan. I think that by using Hetzner with only the strictly necessary services, a single server might be sufficient to host the recovery cluster cheaply.&lt;&#x2F;p&gt;
&lt;p&gt;With this setup, it might be possible to achieve a very low RTO and RPO.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, I hope that in the future Longhorn will natively support client-side encryption and a declarative way to restore volumes from offsite backups so that I can simplify the current setup.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;section class=&quot;alert note&quot; role=&quot;note&quot; aria-labelledby=&quot;Yo8JWS1N&quot;&gt;
    &lt;div class=&quot;alert-icon alert-icon-info&quot;&gt;&lt;&#x2F;div&gt;
    &lt;div class=&quot;alert-content&quot; role=&quot;presentation&quot;&gt;
        &lt;strong id=&quot;Yo8JWS1N&quot; class=&quot;alert-title&quot; aria-hidden=&quot;true&quot;&gt;License&lt;&#x2F;strong&gt;
        &lt;p&gt;This article is licensed under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;CC BY-SA 4.0 license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

    &lt;&#x2F;div&gt;
&lt;&#x2F;section&gt;
</description>
      </item>
      <item>
          <title>InfluxDB Alerting in an Event-Driven Architecture: Walkthrough and considerations</title>
          <pubDate>Wed, 20 Nov 2024 00:00:00 +0000</pubDate>
          <author>Andrea Straforini</author>
          <link>https://strafo.net/blog/influx-alerting/</link>
          <guid>https://strafo.net/blog/influx-alerting/</guid>
          <description xml:base="https://strafo.net/blog/influx-alerting/">&lt;p&gt;This article documents an implementation of automated alerting for time-series data in an event-driven architecture. We&#x27;ll walk through building a system that monitors data ingestion from 2000+ IoT devices and automatically creates, enables, and disables alerts based on device lifecycle events.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;What we&#x27;re solving:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Automated alert management for thousands of measurement streams&lt;&#x2F;li&gt;
&lt;li&gt;Decoupling alerting logic from configuration databases&lt;&#x2F;li&gt;
&lt;li&gt;Ensuring consistency between database state and alert state using the transactional outbox pattern&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Technologies covered:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;InfluxDB v1 &amp;amp; Kapacitor for time-series storage and alerting&lt;&#x2F;li&gt;
&lt;li&gt;Debezium &amp;amp; Kafka Connect for change data capture&lt;&#x2F;li&gt;
&lt;li&gt;Benthos for stream processing and orchestration&lt;&#x2F;li&gt;
&lt;li&gt;Kubernetes&#x2F;OpenShift for deployment&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;problem-statement&quot;&gt;Problem Statement&lt;a class=&quot;post-anchor&quot; href=&quot;#problem-statement&quot; aria-label=&quot;Anchor link for: problem-statement&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 a project where numerous time series from IoT devices are stored, the following architecture is in place:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A Kafka broker that holds the measurements obtained from the IoT devices.&lt;&#x2F;li&gt;
&lt;li&gt;A simple sink connector that writes the measurements to the InfluxDB database for historical storage.&lt;&#x2F;li&gt;
&lt;li&gt;A legacy backend service for process configuration, which uses MySQL for saving configurations.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;base-schema.a214b2f6f935dcbb.jpg&quot; alt=&quot;Base Schema&quot;
     width=&quot;1280&quot; height=&quot;557&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;base-schema.68d28a5e3374e8b2.jpg 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;base-schema.30dd201937084bf5.jpg 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;base-schema.217a9496499c8d04.jpg 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;base-schema.53dbf83e99e90162.jpg 1920w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;base-schema.6a28d60695ef0684.jpg 2560w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;To ensure that the measurements are being stored in the time series database, we want to create an alerting mechanism that can notify the user if data from an &quot;active&quot; IoT device stops arriving at the database.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;attempted-solution-native-influxdb-deadman-checks&quot;&gt;Attempted Solution: Native InfluxDB Deadman Checks&lt;a class=&quot;post-anchor&quot; href=&quot;#attempted-solution-native-influxdb-deadman-checks&quot; aria-label=&quot;Anchor link for: attempted-solution-native-influxdb-deadman-checks&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 solution we investigated uses the native checks in InfluxDB version 1, as demonstrated in this article: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.influxdata.com&#x2F;blog&#x2F;how-deadman-check-alert-service-outage&#x2F;&quot;&gt;How to Use a Deadman Check to Alert on Service Outage&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The article illustrates the creation of these checks for InfluxDB version 2, and the process for version 1 is similar.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-this-didn-t-work&quot;&gt;Why This Didn&#x27;t Work&lt;a class=&quot;post-anchor&quot; href=&quot;#why-this-didn-t-work&quot; aria-label=&quot;Anchor link for: why-this-didn-t-work&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 outlined solution has a significant limitation, at least for InfluxDB version 1:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!Warning]
The Deadman check can be created for a single measurement only.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It is not possible to create a single Deadman check that spans multiple measurements, even if they reside in the same database.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The scale problem:&lt;&#x2F;strong&gt;
In our case, each IoT device is associated with a specific measurement, which means we would need to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Manually create approximately 2000 Deadman checks&lt;&#x2F;li&gt;
&lt;li&gt;Manually activate and deactivate checks when operators turn devices on or off&lt;&#x2F;li&gt;
&lt;li&gt;Maintain consistency between device state in MySQL and alert state in Kapacitor&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This manual approach doesn&#x27;t scale and is error-prone.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Note on data modeling:&lt;&#x2F;strong&gt;
It&#x27;s possible that a different data layout, as discussed in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.influxdata.com&#x2F;blog&#x2F;data-layout-and-schema-design-best-practices-for-influxdb&#x2F;&quot;&gt;Data Layout and Schema Design Best Practices for InfluxDB&lt;&#x2F;a&gt;, could have avoided this issue (e.g., using tags instead of separate measurements). However, this was a legacy system constraint we couldn&#x27;t change.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;solution-event-driven-alerting-architecture&quot;&gt;Solution: Event-Driven Alerting Architecture&lt;a class=&quot;post-anchor&quot; href=&quot;#solution-event-driven-alerting-architecture&quot; aria-label=&quot;Anchor link for: solution-event-driven-alerting-architecture&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 second solution aims to create an automated, decoupled alerting system that addresses the limitations of manual alert management.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;design-principles&quot;&gt;Design Principles&lt;a class=&quot;post-anchor&quot; href=&quot;#design-principles&quot; aria-label=&quot;Anchor link for: design-principles&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;Our solution follows these key principles:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Event-Driven&lt;&#x2F;strong&gt;: All state changes flow through Kafka as events, ensuring auditability and loose coupling&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Technology Agnostic&lt;&#x2F;strong&gt;: The event schema is independent of InfluxDB, making it easier to migrate to other time-series databases&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Consistency Guarantees&lt;&#x2F;strong&gt;: Using the transactional outbox pattern ensures database writes and event publishing happen atomically&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Automated Lifecycle Management&lt;&#x2F;strong&gt;: Alerts are automatically created, enabled, and disabled based on device state changes&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;architecture-components&quot;&gt;Architecture Components&lt;a class=&quot;post-anchor&quot; href=&quot;#architecture-components&quot; aria-label=&quot;Anchor link for: architecture-components&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;solution-schema.066925b9f035f26b.jpg&quot; alt=&quot;Solution Schema&quot;
     width=&quot;1280&quot; height=&quot;1108&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;solution-schema.378c2aee43c08563.jpg 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;solution-schema.488f566879d9bbb5.jpg 784w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;solution-schema.893a3dc93c9184bf.jpg 1280w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;solution-schema.76da61d9a3a482bb.jpg 1920w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;solution-schema.e2e3add5c67e686d.jpg 2560w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;&lt;strong&gt;Component breakdown:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;MySQL + Outbox Table&lt;&#x2F;strong&gt;: Configuration database with an outbox table for transactional event publishing&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Debezium (Kafka Connect)&lt;&#x2F;strong&gt;: Captures changes from the outbox table and publishes Cloud Events to Kafka&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Kafka&lt;&#x2F;strong&gt;: Event backbone carrying both IoT measurements and device lifecycle events&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Benthos&lt;&#x2F;strong&gt;: Stream processor that translates lifecycle events into Kapacitor API calls and converts alerts back to events&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Kapacitor&lt;&#x2F;strong&gt;: InfluxDB&#x27;s native alerting engine that monitors measurements and triggers deadman alerts&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;InfluxDB&lt;&#x2F;strong&gt;: Time-series database storing IoT measurements&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;data-flow&quot;&gt;Data Flow&lt;a class=&quot;post-anchor&quot; href=&quot;#data-flow&quot; aria-label=&quot;Anchor link for: data-flow&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;Here&#x27;s what happens when an operator enables a device:&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;1. Operator action → Configurator service updates MySQL + Outbox table (single transaction)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2. Debezium detects outbox change → Publishes Cloud Event to Kafka&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3. Benthos consumes event → Translates to Kapacitor API call&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;4. Kapacitor creates&#x2F;enables alert → Monitors InfluxDB for device measurements&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;5. [Time passes, no data arrives]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;6. Kapacitor triggers deadman alert → POSTs to Benthos&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;7. Benthos receives alert → Publishes alert event to Kafka&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8. Downstream consumers react → Notify operators, update dashboards, etc.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;key-architectural-decisions&quot;&gt;Key Architectural Decisions&lt;a class=&quot;post-anchor&quot; href=&quot;#key-architectural-decisions&quot; aria-label=&quot;Anchor link for: key-architectural-decisions&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;&lt;strong&gt;Why Debezium + Outbox Pattern?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The transactional outbox pattern solves the &quot;dual write problem&quot; - ensuring that database changes and event publishing happen atomically.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Why this matters:&lt;&#x2F;strong&gt; Without the outbox pattern, the configurator service would need to:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Write to MySQL&lt;&#x2F;li&gt;
&lt;li&gt;Publish to Kafka&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If step 2 fails, Kafka consumers won&#x27;t see the change even though MySQL was updated. If step 1 fails but step 2 succeeds, Kafka has an event for a change that never happened in the database.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The solution:&lt;&#x2F;strong&gt; Write both the configuration change AND an event record to an &quot;outbox&quot; table within a single database transaction. Then, Debezium reads from the outbox table and reliably publishes to Kafka. This guarantees that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Events are only published if the database write succeeds&lt;&#x2F;li&gt;
&lt;li&gt;No events are lost if Kafka is temporarily unavailable&lt;&#x2F;li&gt;
&lt;li&gt;Event ordering is preserved&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Why Benthos?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Benthos (now Redpanda Connect) acts as our integration layer because it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Provides declarative stream processing with Bloblang (its query language)&lt;&#x2F;li&gt;
&lt;li&gt;Handles HTTP API integration natively&lt;&#x2F;li&gt;
&lt;li&gt;Supports complex routing and error handling (DLQ)&lt;&#x2F;li&gt;
&lt;li&gt;Requires no custom code - everything is configuration&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Why Kapacitor?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Kapacitor is InfluxDB&#x27;s native alerting engine with:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Built-in deadman checks for detecting stopped data streams&lt;&#x2F;li&gt;
&lt;li&gt;Template support for creating similar alerts at scale&lt;&#x2F;li&gt;
&lt;li&gt;Direct integration with InfluxDB subscriptions&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;a class=&quot;post-anchor&quot; href=&quot;#implementation&quot; aria-label=&quot;Anchor link for: implementation&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;Now let&#x27;s walk through implementing each component.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-1-create-the-outbox-table&quot;&gt;Step 1: Create the Outbox Table&lt;a class=&quot;post-anchor&quot; href=&quot;#step-1-create-the-outbox-table&quot; aria-label=&quot;Anchor link for: step-1-create-the-outbox-table&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 step is creating the Outbox table in MySQL. Follow the guidance in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debezium.io&#x2F;blog&#x2F;2019&#x2F;02&#x2F;19&#x2F;reliable-microservices-data-exchange-with-the-outbox-pattern&#x2F;&quot;&gt;Reliable Microservices Data Exchange With the Outbox Pattern&lt;&#x2F;a&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;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&gt; `&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;outbox&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: #E6DB74;&quot;&gt;  `id`&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; bigint&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; NOT NULL&lt;&#x2F;span&gt;&lt;span&gt; AUTO_INCREMENT,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;  `payload`&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; json DEFAULT NULL&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: #E6DB74;&quot;&gt;  `type`&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; varchar&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;255&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; CHARACTER SET&lt;&#x2F;span&gt;&lt;span&gt; utf8mb4 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;COLLATE&lt;&#x2F;span&gt;&lt;span&gt; utf8mb4_0900_ai_ci &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;NOT NULL&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: #E6DB74;&quot;&gt;  `aggregateid`&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; char&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;36&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; NOT NULL&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: #E6DB74;&quot;&gt;  `aggregatetype`&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; varchar&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; DEFAULT NULL&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;  PRIMARY KEY&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;`id`&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;  UNIQUE KEY&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; `aggregateid`&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;`aggregateid`&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;) ENGINE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;InnoDB AUTO_INCREMENT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;257&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; DEFAULT&lt;&#x2F;span&gt;&lt;span&gt; CHARSET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;utf8mb4 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;COLLATE=&lt;&#x2F;span&gt;&lt;span&gt;utf8mb4_0900_ai_ci;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;&#x2F;strong&gt; Modify your configurator process to write to the outbox table within the same transaction as the configuration change:&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;START TRANSACTION&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: #88846F;&quot;&gt;  -- Update device configuration&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  UPDATE&lt;&#x2F;span&gt;&lt;span&gt; devices &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;SET status =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;enabled&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; WHERE&lt;&#x2F;span&gt;&lt;span&gt; id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&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;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  -- Write event to outbox&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; outbox (payload, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;, aggregateid, aggregatetype) &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  VALUES&lt;&#x2F;span&gt;&lt;span&gt; (?, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;device-enabled&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, ?, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;device&amp;#39;&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;COMMIT&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;step-2-configure-kafka-connect-debezium&quot;&gt;Step 2: Configure Kafka Connect (Debezium)&lt;a class=&quot;post-anchor&quot; href=&quot;#step-2-configure-kafka-connect-debezium&quot; aria-label=&quot;Anchor link for: step-2-configure-kafka-connect-debezium&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 need to configure Kafka Connect on AMQ Streams (OpenShift). This requires three steps:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Build a Docker Image&lt;&#x2F;strong&gt; with necessary plugins (for airgapped environments)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Deploy a Connect Cluster&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Configure the Source Connector&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4 id=&quot;2a-build-custom-docker-image&quot;&gt;2a. Build Custom Docker Image&lt;a class=&quot;post-anchor&quot; href=&quot;#2a-build-custom-docker-image&quot; aria-label=&quot;Anchor link for: 2a-build-custom-docker-image&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;In our airgapped environment, we need all dependencies pre-installed:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;docker&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; registry.redhat.io&#x2F;amq7&#x2F;amq-streams-kafka-31-rhel8:2.1.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;USER&lt;&#x2F;span&gt;&lt;span&gt; root:root&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span&gt; set -ex &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    mkdir -p &#x2F;opt&#x2F;kafka&#x2F;plugins &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cd &#x2F;opt&#x2F;kafka&#x2F;plugins &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;    # Download Debezium MySQL connector&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    curl https:&#x2F;&#x2F;maven.repository.redhat.com&#x2F;ga&#x2F;io&#x2F;debezium&#x2F;debezium-connector-mysql&#x2F;2.7.3.Final-redhat-00003&#x2F;debezium-connector-mysql-2.7.3.Final-redhat-00003-plugin.zip -O &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    unzip debezium-connector-mysql-2.7.3.Final-redhat-00003-plugin.zip &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;    # Download Debezium scripting support&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    curl https:&#x2F;&#x2F;maven.repository.redhat.com&#x2F;ga&#x2F;io&#x2F;debezium&#x2F;debezium-scripting&#x2F;2.7.3.Final-redhat-00003&#x2F;debezium-scripting-2.7.3.Final-redhat-00003.zip -O &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    unzip debezium-scripting-2.7.3.Final-redhat-00003.zip &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;    # Download Groovy dependencies (needed for SMT transformations)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    curl https:&#x2F;&#x2F;repo1.maven.org&#x2F;maven2&#x2F;org&#x2F;apache&#x2F;groovy&#x2F;groovy&#x2F;4.0.24&#x2F;groovy-4.0.24.jar -O &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    curl https:&#x2F;&#x2F;repo1.maven.org&#x2F;maven2&#x2F;org&#x2F;apache&#x2F;groovy&#x2F;groovy-jsr223&#x2F;4.0.24&#x2F;groovy-jsr223-4.0.24.jar -O &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    curl https:&#x2F;&#x2F;repo1.maven.org&#x2F;maven2&#x2F;org&#x2F;apache&#x2F;groovy&#x2F;groovy-json&#x2F;4.0.24&#x2F;groovy-json-4.0.24.jar -O &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;    # Cleanup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rm debezium-connector-mysql-2.7.3.Final-redhat-00003-plugin.zip debezium-scripting-2.7.3.Final-redhat-00003.zip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;USER&lt;&#x2F;span&gt;&lt;span&gt; 1001&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All those dependencies are necessary for Debezium to work. More details: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.redhat.com&#x2F;en&#x2F;documentation&#x2F;red_hat_build_of_debezium&#x2F;2.7.3&#x2F;html-single&#x2F;debezium_user_guide&#x2F;index&quot;&gt;Debezium User Guide&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debezium.io&#x2F;documentation&#x2F;reference&#x2F;stable&#x2F;operations&#x2F;openshift.html&quot;&gt;Debezium OpenShift&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;2b-deploy-kafka-connect-cluster&quot;&gt;2b. Deploy Kafka Connect Cluster&lt;a class=&quot;post-anchor&quot; href=&quot;#2b-deploy-kafka-connect-cluster&quot; aria-label=&quot;Anchor link for: 2b-deploy-kafka-connect-cluster&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&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; kafka.strimzi.io&#x2F;v1beta2&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; KafkaConnect&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;  annotations&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;    strimzi.io&#x2F;use-connector-resources&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;true&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  # Enable KafkaConnector CRD&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; connect-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;  namespace&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;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;  bootstrapServers&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;central-cluster-kafka-bootstrap.&amp;lt;redacted&amp;gt;.svc.cluster.local:9092&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  config&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: #88846F;&quot;&gt;    # Internal topics for Connect cluster state&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    config.storage.replication.factor&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;    config.storage.topic&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; connect-cluster-configs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    group.id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; connect-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;    offset.storage.replication.factor&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;    offset.storage.topic&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; connect-cluster-offsets&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    status.storage.replication.factor&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;    status.storage.topic&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; connect-cluster-status&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;lt;your-custom-image-from-step-2a&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;  replicas&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;  version&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 3.1.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h4 id=&quot;2c-configure-mysql-source-connector&quot;&gt;2c. Configure MySQL Source Connector&lt;a class=&quot;post-anchor&quot; href=&quot;#2c-configure-mysql-source-connector&quot; aria-label=&quot;Anchor link for: 2c-configure-mysql-source-connector&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&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; kafka.strimzi.io&#x2F;v1beta2&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; KafkaConnector&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;  labels&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;    strimzi.io&#x2F;cluster&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; connect-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;  name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; mysql-source-connector&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; &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;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;  class&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; io.debezium.connector.mysql.MySqlConnector&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  config&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: #88846F;&quot;&gt;    # MySQL connection&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    database.hostname&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 192.168.6.38&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    database.port&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 3306&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    database.user&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;    database.password&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;    database.server.id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 184051&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    database.include.list&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;lt;db name redacted&amp;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;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;    # Only watch the outbox table&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    table.include.list&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;lt;db name redacted&amp;gt;.outbox&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: #88846F;&quot;&gt;    # Topic naming&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    topic.prefix&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; connector-mysql&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: #88846F;&quot;&gt;    # Transform outbox records into Cloud Events&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    transforms&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;addMetadataHeaders,outbox&amp;#39;&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: #88846F;&quot;&gt;    # Outbox Event Router - converts outbox table to proper events&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    transforms.outbox.type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; io.debezium.transforms.outbox.EventRouter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    transforms.outbox.table.fields.additional.placement&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;type:header&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    transforms.outbox.table.expand.json.payload&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;true&amp;#39;&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: #88846F;&quot;&gt;    # Add metadata headers for monitoring&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    transforms.addMetadataHeaders.type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; org.apache.kafka.connect.transforms.HeaderFrom$Value&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    transforms.addMetadataHeaders.operation&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; copy&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    transforms.addMetadataHeaders.fields&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;source,op,transaction&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    transforms.addMetadataHeaders.headers&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;source,op,transaction&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    transforms.addMetadataHeaders.negate&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;true&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    transforms.addMetadataHeaders.predicate&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; isHeartbeat&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: #88846F;&quot;&gt;    # Heartbeat predicate&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    predicates&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; isHeartbeat&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    predicates.isHeartbeat.type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; org.apache.kafka.connect.transforms.predicates.TopicNameMatches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    predicates.isHeartbeat.pattern&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; __debezium-heartbeat.*&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: #88846F;&quot;&gt;    # Cloud Events format&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    value.converter&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; io.debezium.converters.CloudEventsConverter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    value.converter.metadata.source&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;header,id:generate,type:header,dataSchemaName:header&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    value.converter.json.schemas.enable&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;false&amp;#39;&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: #88846F;&quot;&gt;    # Headers&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    header.converter&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; org.apache.kafka.connect.json.JsonConverter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    header.converter.schemas.enable&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;true&amp;#39;&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: #88846F;&quot;&gt;    # Don&amp;#39;t create tombstones on delete&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    tombstones.on.delete&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;false&amp;#39;&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: #88846F;&quot;&gt;    # Schema history (required for MySQL connector)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    schema.history.internal.kafka.topic&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; schema-changes.&amp;lt;db name 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;    schema.history.internal.kafka.bootstrap.servers&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;central-cluster-kafka-bootstrap.&amp;lt;redacted&amp;gt;.svc.cluster.local:9092&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    schema.data.name.source.header.enable&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;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;What this connector does:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Watches the &lt;code&gt;outbox&lt;&#x2F;code&gt; table for INSERT operations&lt;&#x2F;li&gt;
&lt;li&gt;Extracts the event from the outbox record&lt;&#x2F;li&gt;
&lt;li&gt;Converts it to CloudEvents format&lt;&#x2F;li&gt;
&lt;li&gt;Publishes to Kafka topics based on the &lt;code&gt;aggregatetype&lt;&#x2F;code&gt; field&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;step-3-deploy-alerting-system-benthos-kapacitor&quot;&gt;Step 3: Deploy Alerting System (Benthos + Kapacitor)&lt;a class=&quot;post-anchor&quot; href=&quot;#step-3-deploy-alerting-system-benthos-kapacitor&quot; aria-label=&quot;Anchor link for: step-3-deploy-alerting-system-benthos-kapacitor&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 created a Helm chart that orchestrates both Benthos and Kapacitor deployment.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;chart-dependencies&quot;&gt;Chart Dependencies&lt;a class=&quot;post-anchor&quot; href=&quot;#chart-dependencies&quot; aria-label=&quot;Anchor link for: chart-dependencies&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&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;dependencies&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; kapacitor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    version&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;1.4.7&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;    condition&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; kapacitor.enabled&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    repository&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;https:&#x2F;&#x2F;helm.influxdata.com&#x2F;&amp;quot;&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; benthos&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    version&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;2.2.0&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;    condition&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; benthos.enabled&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    repository&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; https:&#x2F;&#x2F;benthosdev.github.io&#x2F;charts&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The chart needs to:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Install the template for alert creation using a job&lt;&#x2F;li&gt;
&lt;li&gt;Create the ingress for communication with the InfluxDB instance outside of OpenShift&lt;&#x2F;li&gt;
&lt;li&gt;Create the secret with user credentials&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4 id=&quot;kapacitor-template&quot;&gt;Kapacitor Template&lt;a class=&quot;post-anchor&quot; href=&quot;#kapacitor-template&quot; aria-label=&quot;Anchor link for: kapacitor-template&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;This is the template for creating deadman alerts (more here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.influxdata.com&#x2F;kapacitor&#x2F;v1&#x2F;working&#x2F;template_tasks&#x2F;&quot;&gt;template docs&lt;&#x2F;a&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;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;&#x2F;&#x2F; Template VARS - these are parameterized and provided when creating a task from this template&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; name  string &lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;&#x2F;&#x2F; unique identifier for this alert task&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; db string &lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;&#x2F;&#x2F; InfluxDB database name to query&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; rp string &lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;&#x2F;&#x2F; retention policy to use (e.g., &amp;#39;autogen&amp;#39;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; measurement string &lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;&#x2F;&#x2F; the specific measurement to monitor (maps to device ID)&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: #88846F;&quot;&gt;&#x2F;&#x2F; Variables with default values - can be overridden when creating tasks&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; groupBy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; []&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; tags to group by (empty = treat as single series)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; whereFilter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; lambda: (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;ty&amp;quot;&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;F&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; filter condition - only check &amp;#39;F&amp;#39; type records&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; period&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; 1m&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; how often to check for data (1 minute window)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; message&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; id:{{.ID}} name:{{.Name}} taskname:{{.TaskName}} group:{{.Group}} tags:{{.Tags}} level:{{.Level}} fields:{{.Fields}} time:{{.Time}}  &amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; outputDB&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;chronograf&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; where to store alert states&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; outputRP&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;autogen&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; outputMeasurement&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;alerts&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; measurement name for alert records&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; threshold&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 0.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; deadman threshold - triggers if no points received&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; triggerType&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;deadman&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; idVar&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; use the task name as the alert ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; idTag&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;alertID&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; tag name for alert ID in output&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; levelTag&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;level&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; tag name for alert level (OK, CRITICAL)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; messageField&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;message&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; field name for alert message&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; durationField&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;duration&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; field name for alert duration&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; query&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;SELECT * FROM &amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; db&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;&amp;quot;.&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; rp&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;&amp;quot;.&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; measurement&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;&amp;quot; WHERE &amp;quot;ty&amp;quot; = &lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;\&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;\&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;&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: #88846F;&quot;&gt;&#x2F;&#x2F; Alert definition - batch query approach&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; batch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(query)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; execute the query against InfluxDB&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: #A6E22E;&quot;&gt;period&lt;&#x2F;span&gt;&lt;span&gt;(period)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; check every &amp;#39;period&amp;#39; interval&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: #A6E22E;&quot;&gt;every&lt;&#x2F;span&gt;&lt;span&gt;(period)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;   &#x2F;&#x2F; execute query every &amp;#39;period&amp;#39;&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: #A6E22E;&quot;&gt;groupBy&lt;&#x2F;span&gt;&lt;span&gt;(groupBy)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; group results by specified tags&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: #88846F;&quot;&gt;&#x2F;&#x2F; Deadman trigger - fires when no data points received within period&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; trigger&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;deadman&lt;&#x2F;span&gt;&lt;span&gt;(threshold, period)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; trigger if points &amp;lt;= threshold in period&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: #A6E22E;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;(message)&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: #A6E22E;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;(idVar)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; unique identifier for this alert&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: #A6E22E;&quot;&gt;idTag&lt;&#x2F;span&gt;&lt;span&gt;(idTag)&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: #A6E22E;&quot;&gt;levelTag&lt;&#x2F;span&gt;&lt;span&gt;(levelTag)&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: #A6E22E;&quot;&gt;messageField&lt;&#x2F;span&gt;&lt;span&gt;(messageField)&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: #A6E22E;&quot;&gt;durationField&lt;&#x2F;span&gt;&lt;span&gt;(durationField)&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: #A6E22E;&quot;&gt;stateChangesOnly&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; only trigger when state changes (OK ↔ CRITICAL)&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: #88846F;&quot;&gt;&#x2F;&#x2F; Output 1: Store alert states in InfluxDB for querying&#x2F;dashboards&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;trigger&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;eval&lt;&#x2F;span&gt;&lt;span&gt;(lambda: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;emitted&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; add &amp;#39;emitted&amp;#39; field with static value&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: #A6E22E;&quot;&gt;as&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;value&amp;#39;&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: #A6E22E;&quot;&gt;keep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;value&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, messageField, durationField)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;eval&lt;&#x2F;span&gt;&lt;span&gt;(lambda: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; convert to float for InfluxDB&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: #A6E22E;&quot;&gt;as&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;value&amp;#39;&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: #A6E22E;&quot;&gt;keep&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;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;influxDBOut&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: #A6E22E;&quot;&gt;create&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: #A6E22E;&quot;&gt;database&lt;&#x2F;span&gt;&lt;span&gt;(outputDB)&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: #A6E22E;&quot;&gt;retentionPolicy&lt;&#x2F;span&gt;&lt;span&gt;(outputRP)&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: #A6E22E;&quot;&gt;measurement&lt;&#x2F;span&gt;&lt;span&gt;(outputMeasurement)&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: #A6E22E;&quot;&gt;tag&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;alertName&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, name)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; add alert metadata as tags&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: #A6E22E;&quot;&gt;tag&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;triggerType&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, triggerType)&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: #88846F;&quot;&gt;&#x2F;&#x2F; Output 2: Expose via HTTP endpoint for external monitoring&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;trigger&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;httpOut&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;output&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; make alert data available at &#x2F;kapacitor&#x2F;v1&#x2F;tasks&#x2F;:task_id&#x2F;output&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: #88846F;&quot;&gt;&#x2F;&#x2F; Output 3: POST alert to Benthos for event generation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;trigger&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: #A6E22E;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;output&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; when alert fires, POST to named endpoint&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: #A6E22E;&quot;&gt;endpoint&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;benthos&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  &#x2F;&#x2F; configured in Kapacitor envVars as KAPACITOR_HTTPPOST_0_URL&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h4 id=&quot;template-installation-job&quot;&gt;Template Installation Job&lt;a class=&quot;post-anchor&quot; href=&quot;#template-installation-job&quot; aria-label=&quot;Anchor link for: template-installation-job&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;The template is installed via a Kubernetes Job that runs after Helm install&#x2F;upgrade:&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; batch&#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; Job&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; include &amp;quot;alerting-system.fullname&amp;quot; .&lt;&#x2F;span&gt;&lt;span&gt; }}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;-templates-installer&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  labels&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: #E6DB74;&quot;&gt;include &amp;quot;alerting-system.labels&amp;quot; . | nindent 4&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;  annotations&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: #E6DB74;&quot;&gt;    &amp;quot;helm.sh&#x2F;hook&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; post-install,post-upgrade&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    &amp;quot;helm.sh&#x2F;hook-delete-policy&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; hook-succeeded&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;  template&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;    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;      containers&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; curl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;{{ .Values.templateInstallJob.image.repository }}:{{ .Values.templateInstallJob.image.tag | default &amp;quot;latest&amp;quot; }}&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;        command&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;sh&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;-c&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;span style=&quot;color: #F92672;&quot;&gt;        args&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; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            set -ex&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            base_url=&amp;#39;http:&#x2F;&#x2F;{{ template &amp;quot;kapacitor.fullname&amp;quot; .Subcharts.kapacitor }}:9092&#x2F;kapacitor&#x2F;v1&#x2F;templates&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            script=`cat &#x2F;config&#x2F;measurement_series_not_fed_template`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            # Wait for Kapacitor to be ready&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            sleep 10&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            # Check if template exists&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            response_code=$(curl -s -o &#x2F;dev&#x2F;null -w &amp;quot;%{http_code}&amp;quot; &amp;quot;$base_url&#x2F;measurement_series_not_fed_template&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            if [ &amp;quot;$response_code&amp;quot; -eq 404 ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              # Create new template&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              curl --fail -X POST \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                -H &amp;quot;Content-Type: application&#x2F;json&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                --data-raw &amp;quot;{\&amp;quot;id\&amp;quot;:\&amp;quot;measurement_series_not_fed_template\&amp;quot;,\&amp;quot;type\&amp;quot;:\&amp;quot;batch\&amp;quot;,\&amp;quot;script\&amp;quot;:\&amp;quot;$script\&amp;quot;}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                &amp;quot;$base_url&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            elif [ &amp;quot;$response_code&amp;quot; -eq 200 ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              # Update existing template&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              curl --fail -X PATCH \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                -H &amp;quot;Content-Type: application&#x2F;json&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                --data-raw &amp;quot;{\&amp;quot;id\&amp;quot;:\&amp;quot;measurement_series_not_fed_template\&amp;quot;,\&amp;quot;type\&amp;quot;:\&amp;quot;batch\&amp;quot;,\&amp;quot;script\&amp;quot;:\&amp;quot;$script\&amp;quot;}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                &amp;quot;$base_url&#x2F;measurement_series_not_fed_template&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              echo &amp;quot;Unexpected response code: $response_code&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              exit 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        volumeMounts&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; config-volume&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;          mountPath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &#x2F;config&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      volumes&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; config-volume&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        configMap&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; include &amp;quot;alerting-system.fullname&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;span style=&quot;color: #F92672;&quot;&gt;      restartPolicy&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; Never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  backoffLimit&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 10&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h4 id=&quot;benthos-and-kapacitor-configuration&quot;&gt;Benthos and Kapacitor Configuration&lt;a class=&quot;post-anchor&quot; href=&quot;#benthos-and-kapacitor-configuration&quot; aria-label=&quot;Anchor link for: benthos-and-kapacitor-configuration&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h4&gt;
&lt;p&gt;This is the value for configuring Benthos and Kapacitor in our environment:&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;templateInstallJob&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;  image&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;    repository&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; curlimages&#x2F;curl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    pullPolicy&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;    tag&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;8.11.0&amp;quot;&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;ingress&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;  enabled&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;  className&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;&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;  annotations&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;  hosts&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; host&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; kapacitor.apps.ocp1.&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;      paths&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; path&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &#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;          pathType&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; ImplementationSpecific&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  tls&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;kapacitor&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;  enabled&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&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;  image&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;    repository&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;kapacitor&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;    tag&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;1.7.6-alpine&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;    pullPolicy&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;IfNotPresent&amp;quot;&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;  service&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;    type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; ClusterIP&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;  persistence&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;    enabled&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;    accessMode&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; ReadWriteOnce&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; 8Gi&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;  resources&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;    requests&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;      memory&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; 256Mi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      cpu&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 0.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;    limits&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;      memory&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;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;  envVars&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;    KAPACITOR_HTTP_LOG_ENABLED&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    KAPACITOR_LOGGING_LEVEL&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;ERROR&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;    KAPACITOR_INFLUXDB_0_HTTP_PORT&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;80&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;    # This allows external InfluxDB to connect to Kapacitor via Ingress&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    KAPACITOR_INFLUXDB_0_KAPACITOR_HOSTNAME&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;kapacitor.apps.ocp1.&amp;lt;redacted&amp;gt;&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;    KAPACITOR_HTTPPOST_0_ENDPOINT&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;benthos&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;    KAPACITOR_HTTPPOST_0_URL&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;http:&#x2F;&#x2F;benthos-internal-service&#x2F;post&amp;quot;&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;  influxURL&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; http:&#x2F;&#x2F;192.168.6.39:8086&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: #88846F;&quot;&gt;  # Reference to secret containing influxdb-user and influxdb-password&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  existingSecret&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; influx-auth&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;  affinity&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;  tolerations&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;  rbac&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;    create&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;    namespaced&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  serviceAccount&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;    annotations&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;    create&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;    name&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;  namespaceOverride&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;&amp;quot;&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;benthos&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;  enabled&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&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;  deployment&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;    replicaCount&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  image&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;    repository&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;docker.io&#x2F;jeffail&#x2F;benthos&amp;quot;&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;  resources&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;    limits&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;      cpu&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; 200m&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      memory&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; 128Mi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    requests&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;      cpu&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; 200m&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      memory&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; 128Mi&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;  autoscaling&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;    enabled&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; false&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;  serviceMonitor&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;    enabled&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  metrics&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;    prometheus&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;  logger&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;    level&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; INFO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    static_fields&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: #E6DB74;&quot;&gt;      &amp;#39;@service&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; benthos&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;  config&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    input:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      broker:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        inputs:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;          - kafka:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              addresses:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - &amp;quot;central-cluster-kafka-bootstrap.&amp;lt;redacted&amp;gt;.svc.cluster.local:9092&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              topics: [&amp;quot;&amp;lt;redacted&amp;gt;&amp;quot;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              consumer_group: &amp;quot;kapacitor-alerts-connector&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              start_from_oldest: true&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: #E6DB74;&quot;&gt;          - http_server:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              path: &#x2F;post&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              ws_path: &#x2F;post&#x2F;ws&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              allowed_verbs:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - POST&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              timeout: 10s&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: #E6DB74;&quot;&gt;    pipeline:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      processors:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        - bloblang: &amp;#39;meta original_message = this&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        - log:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            level: DEBUG&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            message: &amp;quot;Received message: ${!json()}&amp;quot;&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: #E6DB74;&quot;&gt;        - try:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;          - group_by:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            - check: this.exists(&amp;quot;message&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              processors:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - mapping: &amp;#39;meta grouping = &amp;quot;historicization-event&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - type: log&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  log:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    level: INFO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    message: |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      Received http post from kapacitor with id: ${!json(&amp;quot;id&amp;quot;)}, time: ${!json(&amp;quot;time&amp;quot;)} and level: ${!json(&amp;quot;level&amp;quot;)} &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - bloblang: |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    root = { &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      &amp;quot;id&amp;quot;: uuid_v4(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      &amp;quot;subject&amp;quot;: this.id.trim_suffix(&amp;quot;_not_fed&amp;quot;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      &amp;quot;source&amp;quot;: &amp;quot;benthos&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      &amp;quot;specversion&amp;quot;: &amp;quot;1.0&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      &amp;quot;time&amp;quot;: this.time,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      &amp;quot;type&amp;quot;: if this.level == &amp;quot;OK&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                        &amp;quot;historicization-restarted&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      } else if this.level == &amp;quot;CRITICAL&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                        &amp;quot;historicization-stopped&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - log:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    level: INFO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    message: &amp;quot;Sending cloudevent message to kafka ${!json()}&amp;quot;&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            - check: this.exists(&amp;quot;type&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              processors:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - mapping: &amp;#39;meta grouping = &amp;quot;input-stream-event&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - type: log&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  log:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    level: INFO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    message: |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      Received cloud event with id: ${!json(&amp;quot;id&amp;quot;)} and type: ${!json(&amp;quot;type&amp;quot;)}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - bloblang: |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    root = this.data.devicescode&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - unarchive:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    format: json_array&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - bloblang: |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    let parsed = meta(&amp;quot;original_message&amp;quot;).parse_json()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    root = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      &amp;quot;devicecode&amp;quot;: this,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      &amp;quot;eventtype&amp;quot;: $parsed.data.eventtype,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - switch:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  - check: this.eventtype == &amp;quot;disabled&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    processors:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      - bloblang: |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                          meta type = &amp;quot;disabled&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                          meta id = this.devicecode + &amp;quot;_not_fed&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                          root = { &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            &amp;quot;status&amp;quot;: &amp;quot;disabled&amp;quot; &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                          }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  - check: this.eventtype == &amp;quot;enabled&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    processors:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      - try:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                        - bloblang: |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            meta type = &amp;quot;enabled&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            meta id = this.devicecode + &amp;quot;_not_fed&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            root = this&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                        - http:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            url: http:&#x2F;&#x2F;{{ template &amp;quot;alerting-system.kapacitorFullname&amp;quot; . }}:9092&#x2F;kapacitor&#x2F;v1&#x2F;tasks&#x2F;${!meta(&amp;quot;id&amp;quot;)}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            verb: GET&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            retries: 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                        - bloblang: |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            root = { &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                              &amp;quot;status&amp;quot;: &amp;quot;enabled&amp;quot; &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      - catch:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                        - log:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            level: WARN&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            message: &amp;quot;Task ${!meta(&amp;quot;id&amp;quot;)} not found. Fallback to created&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                        - bloblang: |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            meta type = &amp;quot;created&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            meta id = this.devicecode + &amp;quot;_not_fed&amp;quot; &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            root = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                              &amp;quot;id&amp;quot;: this.devicecode + &amp;quot;_not_fed&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                              &amp;quot;template-id&amp;quot;: &amp;quot;measurement_series_not_fed_template&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                              &amp;quot;dbrps&amp;quot;: [{&amp;quot;db&amp;quot;: &amp;quot;&amp;lt;redacted&amp;gt;&amp;quot;, &amp;quot;rp&amp;quot;: &amp;quot;autogen&amp;quot;}],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                              &amp;quot;vars&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                &amp;quot;name&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                  &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                  &amp;quot;value&amp;quot;: this.devicecode + &amp;quot;_not_fed&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                &amp;quot;measurement&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                  &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                  &amp;quot;value&amp;quot;: this.devicecode&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                &amp;quot;db&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                  &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                  &amp;quot;value&amp;quot;: &amp;quot;&amp;lt;redacted&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                &amp;quot;rp&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                  &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                  &amp;quot;value&amp;quot;: &amp;quot;autogen&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                                }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                              },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                              &amp;quot;status&amp;quot;: &amp;quot;enabled&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - log:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    level: INFO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    message: &amp;quot;Sending message to kapacitor type \&amp;quot;${!meta(\&amp;quot;type\&amp;quot;)}\&amp;quot; for ${!meta(\&amp;quot;id\&amp;quot;)}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                - log:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    level: DEBUG&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    message: &amp;quot;Sending message to kapacitor ${!meta(\&amp;quot;id\&amp;quot;)} with body ${!json()}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        - catch:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;          - log:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              level: ERROR&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              message: &amp;quot;Unable to creating message due to: ${!error()}. Sending original message to dlq&amp;quot;&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: #E6DB74;&quot;&gt;    output:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      fallback:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        - switch:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            retry_until_success: false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            cases:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - check: &amp;#39;meta(&amp;quot;grouping&amp;quot;) == &amp;quot;input-stream-event&amp;quot; &amp;amp;&amp;amp; meta(&amp;quot;type&amp;quot;) == &amp;quot;created&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                output:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  http_client:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    retries: 10  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    retry_period: &amp;quot;10s&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    url: &amp;quot;http:&#x2F;&#x2F;{{ template &amp;quot;alerting-system.kapacitorFullname&amp;quot; . }}:9092&#x2F;kapacitor&#x2F;v1&#x2F;tasks&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    verb: POST&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    headers:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      Content-Type: &amp;quot;application&#x2F;json&amp;quot;&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: #E6DB74;&quot;&gt;              - check: &amp;#39;meta(&amp;quot;grouping&amp;quot;) == &amp;quot;input-stream-event&amp;quot; &amp;amp;&amp;amp; meta(&amp;quot;type&amp;quot;) == &amp;quot;deleted&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                output:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  http_client:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    retries: 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    url: &amp;quot;http:&#x2F;&#x2F;{{ template &amp;quot;alerting-system.kapacitorFullname&amp;quot; . }}:9092&#x2F;kapacitor&#x2F;v1&#x2F;tasks&#x2F;${!meta(\&amp;quot;id\&amp;quot;)}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    verb: DELETE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    headers:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      Content-Type: &amp;quot;application&#x2F;json&amp;quot;&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: #E6DB74;&quot;&gt;              - check: &amp;#39;meta(&amp;quot;grouping&amp;quot;) == &amp;quot;input-stream-event&amp;quot; &amp;amp;&amp;amp; meta(&amp;quot;type&amp;quot;) == &amp;quot;enabled&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                output:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  http_client:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    retries: 10&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    retry_period: &amp;quot;5s&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    url: &amp;quot;http:&#x2F;&#x2F;{{ template &amp;quot;alerting-system.kapacitorFullname&amp;quot; . }}:9092&#x2F;kapacitor&#x2F;v1&#x2F;tasks&#x2F;${!meta(\&amp;quot;id\&amp;quot;)}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    verb: PATCH&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    headers:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      Content-Type: &amp;quot;application&#x2F;json&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - check: &amp;#39;meta(&amp;quot;grouping&amp;quot;) == &amp;quot;input-stream-event&amp;quot; &amp;amp;&amp;amp; meta(&amp;quot;type&amp;quot;) == &amp;quot;disabled&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                output:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  http_client:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    retries: 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    url: &amp;quot;http:&#x2F;&#x2F;{{ template &amp;quot;alerting-system.kapacitorFullname&amp;quot; . }}:9092&#x2F;kapacitor&#x2F;v1&#x2F;tasks&#x2F;${!meta(\&amp;quot;id\&amp;quot;)}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    verb: PATCH&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    headers:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      Content-Type: &amp;quot;application&#x2F;json&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - check: &amp;#39;meta(&amp;quot;grouping&amp;quot;) == &amp;quot;historicization-event&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                output:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  kafka:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    addresses:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      - &amp;quot;central-cluster-kafka-bootstrap.&amp;lt;redacted&amp;gt;.svc.cluster.local:9092&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    topic: &amp;quot;kapacitor-alerts-connector.events&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    target_version: 2.1.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    key: &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    compression: gzip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    static_headers: {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    metadata:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      exclude_prefixes: []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    max_in_flight: 64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - output:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  processors:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    - bloblang: |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                        root = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                          &amp;quot;original_message&amp;quot;: meta(&amp;quot;original_message&amp;quot;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                          &amp;quot;send_message&amp;quot;: this&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  kafka:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    addresses:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      - &amp;quot;central-cluster-kafka-bootstrap.&amp;lt;redacted&amp;gt;.svc.cluster.local:9092&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    topic: &amp;quot;kapacitor-alerts-connector.dead-letters-queue&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    target_version: 2.1.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    key: &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    compression: gzip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    static_headers: {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    metadata:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                      exclude_prefixes: []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                    max_in_flight: 64&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: #E6DB74;&quot;&gt;        - processors:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            - bloblang: |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                root = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  &amp;quot;original_message&amp;quot;: meta(&amp;quot;original_message&amp;quot;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                  &amp;quot;send_message&amp;quot;: this&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;                }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;          kafka:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            addresses:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              - &amp;quot;central-cluster-kafka-bootstrap.&amp;lt;redacted&amp;gt;.svc.cluster.local:9092&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            topic: &amp;quot;kapacitor-alerts-connector.dead-letters-queue&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            target_version: 2.1.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            key: &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            compression: gzip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            static_headers: {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            metadata:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              exclude_prefixes: []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            max_in_flight: 64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;configuration-considerations&quot;&gt;Configuration Considerations&lt;a class=&quot;post-anchor&quot; href=&quot;#configuration-considerations&quot; aria-label=&quot;Anchor link for: configuration-considerations&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;&lt;strong&gt;Dead Letter Queue (DLQ) Strategy:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We use a DLQ for messages that fail to parse or process successfully. Messages end up in the DLQ (&lt;code&gt;kapacitor-alerts-connector.dead-letters-queue&lt;&#x2F;code&gt;) when:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Message format doesn&#x27;t match expected schema&lt;&#x2F;li&gt;
&lt;li&gt;Kapacitor API calls fail after all retries&lt;&#x2F;li&gt;
&lt;li&gt;Output destinations (Kafka) are unreachable&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;External InfluxDB Connection:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To allow the external InfluxDB instance to connect to Kapacitor deployed on OpenShift and exposed via Ingress, we set &lt;code&gt;KAPACITOR_INFLUXDB_0_KAPACITOR_HOSTNAME&lt;&#x2F;code&gt;. This tells Kapacitor what hostname to advertise to InfluxDB for subscription callbacks. Without this, InfluxDB would try to reach Kapacitor using internal Kubernetes DNS names that aren&#x27;t resolvable externally.&lt;&#x2F;p&gt;
&lt;h2 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;h2&gt;
&lt;p&gt;This event-driven alerting architecture automated alert management for 2000+ IoT devices, replacing a manual approach that didn&#x27;t scale. By leveraging the transactional outbox pattern, Debezium, Kafka, Benthos, and Kapacitor, we created a decoupled, maintainable system that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Automatically creates&#x2F;enables&#x2F;disables alerts based on device lifecycle&lt;&#x2F;li&gt;
&lt;li&gt;Maintains consistency between configuration state and alert state&lt;&#x2F;li&gt;
&lt;li&gt;Publishes alert events for downstream consumption&lt;&#x2F;li&gt;
&lt;li&gt;Handles failures gracefully with dead letter queues&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The key takeaway: &lt;strong&gt;event-driven architectures aren&#x27;t just for microservices&lt;&#x2F;strong&gt; - they&#x27;re equally valuable for integrating monitoring and alerting systems, especially at scale.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;useful-resources&quot;&gt;Useful Resources&lt;a class=&quot;post-anchor&quot; href=&quot;#useful-resources&quot; aria-label=&quot;Anchor link for: useful-resources&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;Below are some of the main resources used. Note that the versions are not always consistent, but newer versions of the documentation often contain information applicable to older versions as well.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=wEhr-mnPOeQ&quot;&gt;Implement the outbox-pattern with Red Hat OpenShift Streams for Apache Kafka and Debezium&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.redhat.com&#x2F;en&#x2F;documentation&#x2F;red_hat_streams_for_apache_kafka&#x2F;2.5&#x2F;html-single&#x2F;amq_streams_api_reference&#x2F;index#con-common-configuration-images-reference&quot;&gt;Red Hat Streams for Apache Kafka 2.5 API Reference&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.redhat.com&#x2F;en&#x2F;documentation&#x2F;red_hat_streams_for_apache_kafka&#x2F;2.1&#x2F;html&#x2F;amq_streams_on_openshift_overview&#x2F;kafka-connect-components_str#connectors&quot;&gt;AMQ Streams on OpenShift Overview&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.redhat.com&#x2F;en&#x2F;documentation&#x2F;red_hat_build_of_debezium&#x2F;2.7.3&#x2F;html-single&#x2F;debezium_user_guide&#x2F;index#deployment-of-debezium-mysql-connectors&quot;&gt;Debezium User Guide 2.7.3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debezium.io&#x2F;documentation&#x2F;reference&#x2F;3.0&#x2F;install.html&quot;&gt;Debezium Installation Reference 3.0&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;maven.repository.redhat.com&#x2F;ga&#x2F;io&#x2F;debezium&#x2F;&quot;&gt;Maven Repository - Debezium&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.redhat.com&#x2F;en&#x2F;documentation&#x2F;red_hat_streams_for_apache_kafka&#x2F;2.7&#x2F;html-single&#x2F;deploying_and_managing_streams_for_apache_kafka_on_openshift&#x2F;index#creating-new-image-from-base-str&quot;&gt;Deploying and Managing Streams for Apache Kafka on OpenShift 2.7&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.redhat.com&#x2F;en&#x2F;documentation&#x2F;red_hat_streams_for_apache_kafka&#x2F;2.7&#x2F;html-single&#x2F;deploying_and_managing_streams_for_apache_kafka_on_openshift&#x2F;index#using-kafka-connect-with-plug-ins-str&quot;&gt;Using Kafka Connect with Plug-ins&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developers.redhat.com&#x2F;articles&#x2F;2023&#x2F;07&#x2F;06&#x2F;how-use-debezium-smt-groovy-filter-routing-events#&quot;&gt;How to Use Debezium SMT Groovy Filter for Routing Events&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debezium.io&#x2F;documentation&#x2F;reference&#x2F;1.9&#x2F;transformations&#x2F;index.html&quot;&gt;Debezium Transformations Reference 1.9&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.redhat.com&#x2F;en&#x2F;documentation&#x2F;red_hat_integration&#x2F;2022.q4&#x2F;html&#x2F;installing_debezium_on_openshift&#x2F;installing-debezium-connectors-debezium#deploying_debezium_with_amq_streams&quot;&gt;Installing Debezium on OpenShift&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developers.redhat.com&#x2F;articles&#x2F;2023&#x2F;07&#x2F;06&#x2F;how-use-debezium-smt-groovy-filter-routing-events#what_is_debezium_smt_&quot;&gt;Debezium SMT Overview&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;section class=&quot;alert note&quot; role=&quot;note&quot; aria-labelledby=&quot;Yo8JWS1N&quot;&gt;
    &lt;div class=&quot;alert-icon alert-icon-info&quot;&gt;&lt;&#x2F;div&gt;
    &lt;div class=&quot;alert-content&quot; role=&quot;presentation&quot;&gt;
        &lt;strong id=&quot;Yo8JWS1N&quot; class=&quot;alert-title&quot; aria-hidden=&quot;true&quot;&gt;License&lt;&#x2F;strong&gt;
        &lt;p&gt;This article is licensed under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;CC BY-SA 4.0 license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

    &lt;&#x2F;div&gt;
&lt;&#x2F;section&gt;
</description>
      </item>
      <item>
          <title>My Journey Through Fuzzing: A Survey of Techniques and Tools</title>
          <pubDate>Tue, 01 Mar 2022 00:00:00 +0000</pubDate>
          <author>Andrea Straforini</author>
          <link>https://strafo.net/blog/survey-fuzz-testing/</link>
          <guid>https://strafo.net/blog/survey-fuzz-testing/</guid>
          <description xml:base="https://strafo.net/blog/survey-fuzz-testing/">&lt;p&gt;I spent months diving deep into the world of fuzzing for my thesis, fuzzing is a very broad topic that encompasses many techniques, tools, and strategies.
Unfortunately, there isn&#x27;t a single resource that covers everything in depth, so I had to piece together knowledge from various papers, blog posts, courses, opensource projects, and what not.&lt;&#x2F;p&gt;
&lt;p&gt;This blog post is my attempt to summarize my thesis findings in a more digestible format (you can find the full thesis in the resources section). After reading this, you should have a mental model of how fuzzing works, the key techniques involved, and some practical insights from my own experiences.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-core-insight-that-makes-fuzzing-work&quot;&gt;The Core Insight That Makes Fuzzing Work&lt;a class=&quot;post-anchor&quot; href=&quot;#the-core-insight-that-makes-fuzzing-work&quot; aria-label=&quot;Anchor link for: the-core-insight-that-makes-fuzzing-work&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;Fuzzing is, simply put, throwing random garbage at a program and watching it break. But smart garbage, guided by feedback.&lt;&#x2F;p&gt;
&lt;p&gt;A &lt;strong&gt;fuzzer&lt;&#x2F;strong&gt; is a tool that automatically generates inputs for a target program, executes it with those inputs, and monitors for crashes, hangs, or other anomalous behavior. The magic happens when the fuzzer learns from each execution and uses that feedback to generate better inputs.&lt;&#x2F;p&gt;
&lt;p&gt;Fuzzers are classified along three dimensions, each representing fundamental trade-offs:&lt;&#x2F;p&gt;
&lt;p&gt;By &lt;strong&gt;internal awareness&lt;&#x2F;strong&gt;,fuzzers divide into three types:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Black-box fuzzers operate completely blind to program internals, seeing only inputs and outputs. This makes them fast and simple to deploy, but they struggle to penetrate beyond surface-level code.&lt;&#x2F;li&gt;
&lt;li&gt;White-box fuzzers employ sophisticated program analysis to deliberately craft inputs that maximize coverage, but they hit computational walls from problems like path explosion.&lt;&#x2F;li&gt;
&lt;li&gt;Gray-box fuzzers occupy the middle ground, using lightweight instrumentation to observe execution state without the heavy cost of full program analysis.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;By &lt;strong&gt;input generation method&lt;&#x2F;strong&gt;, there are two approaches:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Generation-based fuzzers rely on formal specifications or grammars that describe valid input structure, letting them produce well-formed test cases that reach deeper code. However, they require significant upfront effort to define input models.&lt;&#x2F;li&gt;
&lt;li&gt;Mutation-based fuzzers take existing inputs and randomly modify them, trading some effectiveness for the ability to start fuzzing immediately without writing generators.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;By &lt;strong&gt;exploration strategy&lt;&#x2F;strong&gt;, fuzzers split into two camps:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Coverage-based fuzzers attempt to exercise as much program code as possible through broad exploration.&lt;&#x2F;li&gt;
&lt;li&gt;Direct fuzzers instead focus their efforts on specific program paths or components for intensive testing.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
The dominant approach combines gray-box awareness with mutation-based generation and coverage-based exploration.
This combination delivers practical effectiveness without requiring either source code specifications or heavy computational resources.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;So now you know that fuzzing is more than just random input generation.&lt;&#x2F;p&gt;
&lt;p&gt;The key is generating a feedback loop that guides random input generation toward interesting program states.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;instrumentation-and-coverage-guidance&quot;&gt;Instrumentation and Coverage Guidance&lt;a class=&quot;post-anchor&quot; href=&quot;#instrumentation-and-coverage-guidance&quot; aria-label=&quot;Anchor link for: instrumentation-and-coverage-guidance&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;Gray-box fuzzing works by instrumenting the target program to collect lightweight execution feedback—typically code coverage information. This feedback tells the fuzzer which inputs trigger new code paths, allowing it to prioritize mutations that explore uncharted territory.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;instrumentation.f09b804203294350.png&quot; alt=&quot;Instrumentation example&quot;
     width=&quot;1036&quot; height=&quot;730&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;instrumentation.bde8e638c67864d9.png 640w,
             https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;instrumentation.a8c02f92efe189d9.png 784w&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;AFL++ Instrumentation Example&lt;&#x2F;p&gt;
&lt;p&gt;Think of coverage as a map of where your inputs have taken you. Different coverage metrics provide different levels of detail:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Block Coverage: is one of the most basic technique and measures how many code blocks have been visited.&lt;&#x2F;li&gt;
&lt;li&gt;Branch Coverage: the basic unit is the tuple (prev, cur), where prev and cur stand for the previous and current block IDs, respectively.&lt;&#x2F;li&gt;
&lt;li&gt;N-Gram Branch Coverage: sits between full path coverage and branch coverage. N is the parameter that describes how many previous blocks are taken into account.&lt;&#x2F;li&gt;
&lt;li&gt;Full Path Coverage: is infeasible, and for a program of n reachable branches, it will require a 2 ^ n test cases while for the branch coverage 2 · n&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Different fuzzers make different choices. Vuzzer and Honggfuzz use basic block coverage. AFL uses edge coverage. LibFuzzer supports both. The choice depends on the trade-off between precision and performance—more detailed coverage means more overhead.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-fuzzing-process&quot;&gt;The Fuzzing Process&lt;a class=&quot;post-anchor&quot; href=&quot;#the-fuzzing-process&quot; aria-label=&quot;Anchor link for: the-fuzzing-process&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 fuzzer starts with a corpus—a collection of valid or semi-valid inputs (or generated inputs if it&#x27;s a generation-based fuzzer).&lt;&#x2F;p&gt;
&lt;p&gt;The fuzzer takes one of these inputs and mutates it. The mutations aren&#x27;t completely random; they&#x27;re informed by strategies that have proven effective, like flipping individual bits, inserting boundary values like zero or maximum integers, or splicing two inputs together.&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;       ┌────────────────────────────────┐&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ────▶│ Take the first test from the   │◀───────────────────┐ &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │      priority queue            │                    │&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;                       │                                     │&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;       ┌────────────────────────────────┐                    │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │  Mutate it and pass it to the  │                    │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │       target program           │                    │&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;                       │                                     │&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;              ┌─────────────────┐                            │&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;              │ Did it increase │                            │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              │ code coverage?  │                            │&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;              └────┬──────┬─────┘                            │&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;                 Yes      No                                 │&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;                   │      ▼                                  │&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;                   │  │ Reinsert the test case in  │         │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                   │  │ the queue but don&amp;#39;t change │─────────┘&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                   │  │      its priority          │         │&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;                   │                                         │&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;       ┌────────────────────────────────┐                    │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │ Reinsert the test case in the  │                    │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │  queue and increase its        │                    │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │         priority               │                    │&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;                       │                                     │&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;              ┌─────────────────┐                            │&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;              │  Did it cause   │                            │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              │    a crash?     │                            │&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;              └────┬──────┬─────┘                            │&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;                 Yes      No                                 │&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;                   │      └──────────────────────────────────┘&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;                   ▼&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;       │      Save the test case        │&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;       └────────────────────────────────┘&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;the-roadblocks-of-fuzzing&quot;&gt;The Roadblocks of Fuzzing&lt;a class=&quot;post-anchor&quot; href=&quot;#the-roadblocks-of-fuzzing&quot; aria-label=&quot;Anchor link for: the-roadblocks-of-fuzzing&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 biggest challenge in fuzzing is dealing with roadblocks—program logic that prevents random mutations from reaching interesting code paths. These roadblocks come in two primary forms: &lt;strong&gt;checksums&lt;&#x2F;strong&gt; and &lt;strong&gt;magic values&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I discovered how severely these roadblocks hinder fuzzing when I started working on a Phasor Data Concentrator implementing the IEEE C37.118 protocol. The protocol has specific structure:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Every frame starts with a sync word (0xAA), followed by frame type information, then a FRAMESIZE field indicating expected byte count, and ends with a CRC checksum.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Consider what happens when you mutate a valid frame: flip a few bits in the payload, and the data changes. But now the checksum is wrong, so the PDC immediately rejects the packet without ever processing the modified data. The fuzzer is stuck at the front door, unable to explore any interesting parsing logic deeper in the code.&lt;&#x2F;p&gt;
&lt;p&gt;This is what researchers call a roadblock. Various techniques have been developed to overcome these obstacles, and I&#x27;ll show you some of the most effective ones in the following sections.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;sub-instruction-profiling-a-clever-trick&quot;&gt;Sub-instruction profiling: a clever trick&lt;a class=&quot;post-anchor&quot; href=&quot;#sub-instruction-profiling-a-clever-trick&quot; aria-label=&quot;Anchor link for: sub-instruction-profiling-a-clever-trick&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;One technique that really clicked for me was sub-instruction profiling. Consider this code that checks for a magic number:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;&#x2F;&#x2F; Original code: requires exact 4-byte match&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; (input &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;== 0x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;DEADBEEF&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: #88846F;&quot;&gt;    &#x2F;&#x2F; interesting code that might have bugs&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;The probability of a random mutation hitting exactly 0xDEADBEEF is about one in four billion. The fuzzer could run for years without finding it. But what if we transformed this code at compile time into something like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;&#x2F;&#x2F; Transformed code: checks each byte separately&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; ((input &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 24&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; == 0x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;DE&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;    if&lt;&#x2F;span&gt;&lt;span&gt; (((input &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 16&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; &amp;amp; 0x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;FF&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; == 0x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;AD&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;        if&lt;&#x2F;span&gt;&lt;span&gt; (((input &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; &amp;amp; 0x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;FF&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; == 0x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;BE&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;            if&lt;&#x2F;span&gt;&lt;span&gt; ((input &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;amp; 0x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;FF&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; == 0x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;EF&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: #88846F;&quot;&gt;                &#x2F;&#x2F; interesting code&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;        }&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;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now the fuzzer has a much better chance. When it randomly mutates the first byte to 0xDE, the coverage map shows new code was reached—the first if-statement&#x27;s true branch. The fuzzer saves that input and keeps mutating it. Eventually, it might stumble upon 0xAD for the second byte, which triggers another new branch, and so on. The probability hasn&#x27;t changed mathematically, but the feedback signal guides the fuzzer toward the solution.&lt;&#x2F;p&gt;
&lt;p&gt;For example, this transformation happens automatically in AFL++ when you use the laf-intel compiler option.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;input-to-state-correspondence-simple-yet-powerful&quot;&gt;Input-to-state correspondence: simple yet powerful&lt;a class=&quot;post-anchor&quot; href=&quot;#input-to-state-correspondence-simple-yet-powerful&quot; aria-label=&quot;Anchor link for: input-to-state-correspondence-simple-yet-powerful&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 technique that really impressed me during my research was input-to-state correspondence, implemented in a fuzzer called Angora. The insight here is beautifully simple yet powerful.&lt;&#x2F;p&gt;
&lt;p&gt;Most programs don&#x27;t heavily transform input before using it in comparisons.&lt;&#x2F;p&gt;
&lt;p&gt;The technique works by colorization: insert random bytes into different positions of the input and observe which of those random bytes appear in comparison operands at runtime.&lt;&#x2F;p&gt;
&lt;p&gt;This is lighter-weight than full taint tracking, which would need to track every single operation that touches input bytes. As a result, even inputs that pass through unknown library functions, large data-dependent loops, and floating-point instructions do not significantly reduce the quality of the results.&lt;&#x2F;p&gt;
&lt;p&gt;Input-to-state correspondence and sub-instruction profiling both solve the magic value problem, but they&#x27;re not the only weapons in the fuzzer&#x27;s arsenal. Let me briefly cover some other techniques you&#x27;ll encounter in the literature before we discuss how fuzzers actually execute test cases.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;other-notable-fuzzing-techniques&quot;&gt;Other Notable Fuzzing Techniques&lt;a class=&quot;post-anchor&quot; href=&quot;#other-notable-fuzzing-techniques&quot; aria-label=&quot;Anchor link for: other-notable-fuzzing-techniques&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;There are other techniques worth mentioning briefly:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Taint Analysis tracks how input bytes propagate through the program, allowing the fuzzer to focus mutations on bytes that influence critical comparisons.&lt;&#x2F;li&gt;
&lt;li&gt;Symbolic Execution explores program paths by treating inputs as symbolic variables, generating constraints that lead to specific branches. However, it struggles with path explosion in complex programs.&lt;&#x2F;li&gt;
&lt;li&gt;Concolic Execution combines concrete execution with symbolic analysis, running the program with real inputs while also tracking symbolic constraints. This hybrid approach can mitigate some limitations of pure symbolic execution.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;fuzzing-modes&quot;&gt;Fuzzing modes&lt;a class=&quot;post-anchor&quot; href=&quot;#fuzzing-modes&quot; aria-label=&quot;Anchor link for: fuzzing-modes&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;ve covered how fuzzers generate inputs and overcome roadblocks, but there&#x27;s another crucial design decision: how should the fuzzer actually run each test case? This choice dramatically affects both speed and reliability.&lt;&#x2F;p&gt;
&lt;p&gt;Fuzzers execute test cases in three ways, trading speed for stability.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Simple mode&lt;&#x2F;strong&gt; spawns a fresh process for each test case. Perfectly stable but slow due to repeated startup overhead.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Fork-server mode&lt;&#x2F;strong&gt; initializes once, then forks a copy per test case. Balances speed and stability by avoiding startup costs while maintaining process isolation.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Persistent mode&lt;&#x2F;strong&gt; reuses the same process for multiple test cases. Fastest option but risks state corruption causing non-deterministic crashes unless the target perfectly resets between tests.&lt;&#x2F;p&gt;
&lt;p&gt;Most modern fuzzers default to fork-server mode for the best balance. Persistent mode is worth the complexity for performance-critical fuzzing campaigns, but only if you can verify the target properly resets its state.&lt;&#x2F;p&gt;
&lt;p&gt;Now let&#x27;s look at how instrumentation itself has evolved to become more efficient.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;advanced-instrumentation-techniques-afl-lto-an-important-improvement&quot;&gt;Advanced Instrumentation Techniques: AFL++ LTO an important Improvement&lt;a class=&quot;post-anchor&quot; href=&quot;#advanced-instrumentation-techniques-afl-lto-an-important-improvement&quot; aria-label=&quot;Anchor link for: advanced-instrumentation-techniques-afl-lto-an-important-improvement&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;AFL&#x27;s original approach tracks edge coverage by XORing random location IDs and indexing into a 64KB shared memory map. This causes collisions—over 75% of edges collide in large programs like libtorrent with 260K+ edges.
AFL++ LTO fixes this by injecting instrumentation at link time via LLVM passes. Each edge gets a hardcoded, unique shared memory address, eliminating collisions entirely and auto-sizing the map to fit the program&#x27;s actual edge count.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;compiler-sanitizers-essential-for-effective-fuzzing&quot;&gt;Compiler sanitizers: essential for effective fuzzing&lt;a class=&quot;post-anchor&quot; href=&quot;#compiler-sanitizers-essential-for-effective-fuzzing&quot; aria-label=&quot;Anchor link for: compiler-sanitizers-essential-for-effective-fuzzing&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;Compiler sanitizers are runtime instrumentation inserted during compilation that detect bugs which may not cause immediate crashes.
Sanitizers are essential for effective fuzzing. They convert silent bugs into immediate crashes, speeding up vulnerability discovery and ensuring deterministic behavior—the same input always produces the same result, making bugs reproducible.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;binary-only-fuzzing-when-source-code-isn-t-available&quot;&gt;Binary-only fuzzing: when source code isn&#x27;t available&lt;a class=&quot;post-anchor&quot; href=&quot;#binary-only-fuzzing-when-source-code-isn-t-available&quot; aria-label=&quot;Anchor link for: binary-only-fuzzing-when-source-code-isn-t-available&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;Everything I&#x27;ve discussed so far assumes you have source code to instrument. But what happens when you&#x27;re fuzzing closed-source software, proprietary protocols, or legacy systems? This is where binary-only fuzzing enters the picture—and where things get significantly more complicated: instrumentation becomes trickier since you can&#x27;t modify the source.&lt;&#x2F;p&gt;
&lt;p&gt;Four approaches exist:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Black-box fuzzing&lt;&#x2F;strong&gt; skips coverage entirely. Simple but ineffective for complex vulnerabilities.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic binary instrumentation&lt;&#x2F;strong&gt; (e.g., QEMU) emulates execution and injects coverage tracking at runtime. Always works but runs 2-5x slower.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Static binary rewriting&lt;&#x2F;strong&gt; patches the binary to add instrumentation before execution. Near-native speed but doesn&#x27;t always succeed—some binaries can&#x27;t be reliably rewritten.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Hardware-based instrumentation&lt;&#x2F;strong&gt; (e.g., Intel PT) uses CPU features to trace execution. Performance varies.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;em&gt;Dynamic instrumentation remains the most reliable approach for general binary-only fuzzing.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dynamic-binary-instrumentation-using-qemu&quot;&gt;Dynamic Binary Instrumentation using QEMU&lt;a class=&quot;post-anchor&quot; href=&quot;#dynamic-binary-instrumentation-using-qemu&quot; aria-label=&quot;Anchor link for: dynamic-binary-instrumentation-using-qemu&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;QEMU is an open-source machine emulator and virtualizer that can emulate different CPU architectures. It functions like a JIT compiler but without an interpreter component.&lt;&#x2F;p&gt;
&lt;p&gt;Internal Architecture: QEMU is built around the &lt;abbr title=&quot;Tiny Code Generator&quot;&gt;TCG&lt;&#x2F;abbr&gt;, which has three parts:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Frontend: Translates guest architecture code to &lt;abbr title=&quot;Intermediate Representation&quot;&gt;IR&lt;&#x2F;abbr&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Backend: Converts IR to host architecture machine code&lt;&#x2F;li&gt;
&lt;li&gt;Core: Orchestrates the translation process, manages the translation cache, and handles exceptions&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;QEMU dynamically translates guest instructions into host instructions up to the next jump or any instruction that modifies CPU state in ways that can&#x27;t be determined at translation time. These chunks are called translation blocks (TBs), similar to basic blocks.
Once translated, TBs are saved in QEMU&#x27;s translation cache for reuse.&lt;&#x2F;p&gt;
&lt;p&gt;Execution Cycle: QEMU alternates between executing translated blocks and running the TCG. Each TB execution is preceded by a prologue function and followed by an epilogue function. These wrapper functions are fundamental for fuzzing because they can be customized to inject instrumentation and pass coverage information between QEMU and the fuzzer.&lt;&#x2F;p&gt;
&lt;p&gt;Block Chaining Optimization: After a TB executes, QEMU uses the simulated Program Counter and CPU state to find the next TB via hash table lookup. If the next TB is already cached, QEMU patches the current block to jump directly to it instead of returning to the epilogue and main loop. This creates chains of TBs that execute sequentially without overhead, significantly boosting performance closer to native speeds.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;strafo.net&#x2F;processed_images&#x2F;execution-flow.316ae2fe2229750a.png&quot; alt=&quot;Part of QEMU&amp;#x27;s execution flow&quot;
     width=&quot;583&quot; height=&quot;268&quot;
     sizes=&quot;(min-width: 920px) 784px, (min-width: 700px) calc(82vw + 46px), calc(100vw - 40px)&quot; 
     srcset=&quot;&quot;
     loading=&quot;lazy&quot;&gt;
&lt;p&gt;This block chaining optimization has been handled differently by fuzzing tools. For a comparison of fuzzers see the thesis in the resources section.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;static-binary-rewriting&quot;&gt;Static Binary Rewriting&lt;a class=&quot;post-anchor&quot; href=&quot;#static-binary-rewriting&quot; aria-label=&quot;Anchor link for: static-binary-rewriting&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;Static binary rewriting adds instrumentation by modifying compiled binaries before execution. Three fundamental techniques exist.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Recompilation&lt;&#x2F;strong&gt; attempts to generate intermediate representation from the binary, but requires recovering type information—an unsolved problem.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Reassembly&lt;&#x2F;strong&gt; disassembles the binary, adds instrumentation, then reassembles it. Retrowrite uses this approach to enable AFL fuzzing of binaries, but fails on non-PIE binaries and C++ exceptions.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Trampolines&lt;&#x2F;strong&gt; insert indirection jumps to instrumentation code without changing block sizes. E9Patch uses this technique and works on stripped binaries without needing control-flow recovery. However, trampolines increase code size and add overhead from extra jumps. The tool can fail on edge cases like single-byte instructions or address space constraints.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Static rewriting achieves near-native speed when successful but cannot handle all binaries reliably, unlike dynamic instrumentation which always works at the cost of performance.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;so-what-s-the-best-fuzzer&quot;&gt;So What&#x27;s the Best Fuzzer?&lt;a class=&quot;post-anchor&quot; href=&quot;#so-what-s-the-best-fuzzer&quot; aria-label=&quot;Anchor link for: so-what-s-the-best-fuzzer&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 answer depends on your goals. As you&#x27;ve seen, fuzzers make a series of trade-offs between speed, effectiveness, and ease of use.
Also some fuzzers implement specific techniques not available in others.&lt;&#x2F;p&gt;
&lt;p&gt;In my experience only few of them are mature enough to be used in real-world scenarios.&lt;&#x2F;p&gt;
&lt;p&gt;In the thesis I evaluated with a qualitative analysis the most popular fuzzers available today, for various metrics like ease of use, documentation quality, community support, and more.&lt;&#x2F;p&gt;
&lt;p&gt;For a quantitative analysis I referred to the FuzzBench platform, which is a Google open-source project that provides a standardized environment to benchmark fuzzers across a wide range of targets and can be considered the gold standard for fuzzer evaluation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;putting-theory-into-practice-real-world-fuzzing-campaigns&quot;&gt;Putting Theory Into Practice: Real-World Fuzzing Campaigns&lt;a class=&quot;post-anchor&quot; href=&quot;#putting-theory-into-practice-real-world-fuzzing-campaigns&quot; aria-label=&quot;Anchor link for: putting-theory-into-practice-real-world-fuzzing-campaigns&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;I&#x27;ve covered a lot of theory and techniques. Now let me show you what actually happens when you try to fuzz real software. I conducted two major fuzzing campaigns as part of my thesis, each revealing different challenges and insights.&lt;&#x2F;p&gt;
&lt;p&gt;The first target: one of the most battle-tested web servers in existence.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;apache-http-server-fuzzing&quot;&gt;Apache HTTP Server Fuzzing&lt;a class=&quot;post-anchor&quot; href=&quot;#apache-http-server-fuzzing&quot; aria-label=&quot;Anchor link for: apache-http-server-fuzzing&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;I chose Apache 2.4.49 as my target—it serves 24.63% of the world&#x27;s top websites. The main challenge was that Apache expects network input while AFL++ wants to provide stdin or file input. My solution involved modifying Apache&#x27;s source to forward AFL++ input to a localhost socket, then using Linux namespaces to give each fuzzer instance its own pocket dimension where they could all bind to the same port simultaneously without fighting each other.&lt;&#x2F;p&gt;
&lt;p&gt;I ran 8 parallel AFL++ instances with different configurations (ASAN, MSAN, laf-intel, CmpLog) in persistent mode, limiting each process to 1,000 iterations because too much persistence leads to state corruption.
The campaign ran for 8 days on an Intel Core i7-4770 with 32GB RAM and found exactly zero crashes. Apache has been fuzzed so thoroughly by security teams and OSS-Fuzz that finding a bug would be like finding a typo in a dictionary.&lt;&#x2F;p&gt;
&lt;p&gt;As a bonus experiment, I demonstrated detecting CVE-2021-41773, a path traversal vulnerability that doesn&#x27;t crash.
Path traversal bugs are tricky for fuzzers because they&#x27;re logic errors, not memory corruptions.
I used the LD_PRELOAD trick to hook Apache&#x27;s file access functions (xstat() and writev()), essentially teaching the fuzzer to yell &quot;Hey, that&#x27;s not allowed!&quot; and trigger an artificial crash whenever Apache tried accessing files outside its base directory.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-three-approaches-i-used-for-stateful-protocol-fuzzing&quot;&gt;The Three Approaches I Used for Stateful Protocol Fuzzing&lt;a class=&quot;post-anchor&quot; href=&quot;#the-three-approaches-i-used-for-stateful-protocol-fuzzing&quot; aria-label=&quot;Anchor link for: the-three-approaches-i-used-for-stateful-protocol-fuzzing&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;the-pdc-case-study&quot;&gt;The PDC Case Study&lt;a class=&quot;post-anchor&quot; href=&quot;#the-pdc-case-study&quot; aria-label=&quot;Anchor link for: the-pdc-case-study&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 my second campaign, I worked with a local company to fuzz their in-development Phasor Data Concentrator (PDC)—a component of smart grid infrastructure that collects real-time electrical measurements from power distribution systems.&lt;&#x2F;p&gt;
&lt;p&gt;PDCs communicate using the IEEE C37.118 protocol, a binary format with specific structure: every frame starts with a sync word (0xAA), contains frame type information and a FRAMESIZE field indicating total length, and ends with a CRC-16 checksum. Surprisingly, the protocol includes no built-in authentication or encryption—companies rely entirely on VPNs and network isolation for security. This makes the protocol implementation itself a prime target for vulnerabilities.&lt;&#x2F;p&gt;
&lt;p&gt;I attacked this system from three different angles, each revealing different trade-offs in fuzzing approaches.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;approach-1-afl-gray-box-fuzzing&quot;&gt;Approach 1: AFL++ (Gray-box Fuzzing)&lt;a class=&quot;post-anchor&quot; href=&quot;#approach-1-afl-gray-box-fuzzing&quot; aria-label=&quot;Anchor link for: approach-1-afl-gray-box-fuzzing&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;I created a shared library harness that intercepted network calls, feeding AFL++ mutations to the PDC over actual sockets. The setup required modifying the PDC&#x27;s network initialization code to accept AFL&#x27;s input, but once running, it proved immediately effective.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Results:&lt;&#x2F;strong&gt; Found two vulnerabilities:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Buffer overflow from unchecked FRAMESIZE field used directly for pointer arithmetic&lt;&#x2F;li&gt;
&lt;li&gt;Out-of-bounds read from NUM_PMU field used without validation&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;What fascinated me was how AFL++ got &quot;lucky&quot; in a way that wouldn&#x27;t work with sanitizers. The OS zeroes memory pages before allocation, so when the PDC read past buffer boundaries into uninitialized memory, it often found zeros. AFL++ stumbled upon mutations producing checksums that matched those zeros, accidentally bypassing validation and exposing deeper bugs. With MemorySanitizer enabled, every out-of-bounds read crashes immediately—paradoxically slowing discovery of the second vulnerability.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Limitations:&lt;&#x2F;strong&gt; AFL++ struggled with the protocol&#x27;s stateful nature. The PDC only accepts Data Frames after receiving valid Configuration Frames first, but setting this up through AFL++ would require extensive source modifications.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;approach-2-radamsa-pypmu-black-box-fuzzing&quot;&gt;Approach 2: Radamsa + PyPMU (Black-box Fuzzing)&lt;a class=&quot;post-anchor&quot; href=&quot;#approach-2-radamsa-pypmu-black-box-fuzzing&quot; aria-label=&quot;Anchor link for: approach-2-radamsa-pypmu-black-box-fuzzing&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;I switched to Radamsa, a general-purpose mutation fuzzer, combined with PyPMU—an open-source Python implementation of C37.118. PyPMU handled all the protocol handshaking and state management automatically, letting me fuzz Data Frames that the PDC would actually process.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Results:&lt;&#x2F;strong&gt; Found five vulnerabilities total:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The same two AFL++ discovered&lt;&#x2F;li&gt;
&lt;li&gt;Use-after-free on uninitialized pointers&lt;&#x2F;li&gt;
&lt;li&gt;Two state misalignment issues from unexpected frame sequences&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Limitations:&lt;&#x2F;strong&gt; Radamsa is completely blind to program internals. Most mutations broke the CRC checksum, causing the PDC to reject packets immediately at validation. Simple to deploy but inefficient at reaching code beyond initial checks.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;approach-3-scapy-generation-based-fuzzing&quot;&gt;Approach 3: Scapy (Generation-based Fuzzing)&lt;a class=&quot;post-anchor&quot; href=&quot;#approach-3-scapy-generation-based-fuzzing&quot; aria-label=&quot;Anchor link for: approach-3-scapy-generation-based-fuzzing&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;For the most thorough approach, I extended Scapy to fully implement the IEEE C37.118 protocol (code available in my GitHub repository). Scapy lets you define packet structures as typed classes, and crucially, it automatically recalculates checksums and length fields after fuzzing individual fields.&lt;&#x2F;p&gt;
&lt;p&gt;This meant every generated packet had valid structure—bypassing initial validation and reaching actual parsing logic. Scapy also fuzzes intelligently: it knows a ShortField should be tested with boundary values (0, 0xFFFF, -1), while enumerated fields get all valid values plus carefully chosen invalid ones.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Results:&lt;&#x2F;strong&gt; The most thorough testing, finding all previous vulnerabilities plus edge cases from boundary value testing.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Limitations:&lt;&#x2F;strong&gt; Required the upfront investment of implementing the entire protocol specification—several days of work before fuzzing even began.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-vulnerabilities-a-pattern-emerges&quot;&gt;The Vulnerabilities: A Pattern Emerges&lt;a class=&quot;post-anchor&quot; href=&quot;#the-vulnerabilities-a-pattern-emerges&quot; aria-label=&quot;Anchor link for: the-vulnerabilities-a-pattern-emerges&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;All discovered vulnerabilities stemmed from the same mundane pattern: &lt;strong&gt;missing input validation&lt;&#x2F;strong&gt;. The developers trusted FRAMESIZE and NUM_PMU field values from the network and used them directly for:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Calculating memory offsets&lt;&#x2F;li&gt;
&lt;li&gt;Determining loop iteration counts&lt;&#x2F;li&gt;
&lt;li&gt;Sizing buffer operations&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;When these values didn&#x27;t match actual packet contents, the PDC happily read past buffer boundaries until hitting unmapped memory and segfaulting.&lt;&#x2F;p&gt;
&lt;p&gt;Not exotic. Not sophisticated. Just classic bounds-checking failures that attackers love and fuzzers excel at finding—exactly the pattern that validates fuzzing&#x27;s effectiveness against real-world code.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-the-fuzzbench-results-taught-me&quot;&gt;What the FuzzBench Results Taught Me&lt;a class=&quot;post-anchor&quot; href=&quot;#what-the-fuzzbench-results-taught-me&quot; aria-label=&quot;Anchor link for: what-the-fuzzbench-results-taught-me&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;As part of my research, I examined the FuzzBench platform, which Google created to standardize fuzzer evaluation. One finding really stood out: among the top seven fuzzers, there was no statistically significant performance difference.&lt;&#x2F;p&gt;
&lt;p&gt;Think about that. Years of fuzzing research, dozens of academic papers proposing new techniques, and yet AFL++, Honggfuzz, Entropic, Eclipser, AFLplusplus, and several others all performed essentially the same when tested rigorously across 22 benchmarks with 20 trials each.&lt;&#x2F;p&gt;
&lt;p&gt;This tells me something important about the state of fuzzing research: we&#x27;re hitting diminishing returns on general-purpose improvements. The low-hanging fruit has been picked. Modern fuzzers have incorporated the key insights—coverage guidance, interesting mutation strategies, handling of roadblocks—and further improvements require specialization.&lt;&#x2F;p&gt;
&lt;p&gt;Where I think the field is heading is toward domain-specific fuzzing.
The FuzzBench results suggest we need more targeted approaches rather than trying to build one fuzzer to rule them all.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-i-d-go-next&quot;&gt;Where I&#x27;d Go Next&lt;a class=&quot;post-anchor&quot; href=&quot;#where-i-d-go-next&quot; aria-label=&quot;Anchor link for: where-i-d-go-next&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 I continued this research, several directions interest me:&lt;&#x2F;p&gt;
&lt;p&gt;First, I&#x27;d want to explore differential fuzzing more deeply. Instead of just looking for crashes, you run the same input through multiple implementations of the same protocol or specification and look for discrepancies. This can find semantic bugs that don&#x27;t cause crashes—like the path traversal vulnerability in Apache that I had to instrument manually to detect.&lt;&#x2F;p&gt;
&lt;p&gt;Second, the problem of fuzzing stateful systems remains largely unsolved. My PDC fuzzing struggled with this—how do you efficiently explore sequences of operations rather than just individual inputs? Some research has looked at grammar-based fuzzing for this, but it&#x27;s still an open challenge.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, I think there&#x27;s opportunity in making fuzzing more accessible. The tools have improved dramatically, but you still need significant expertise to fuzz something like a web browser or operating system kernel effectively. Better abstractions, better tooling, and better documentation could democratize fuzzing further.&lt;&#x2F;p&gt;
&lt;h2 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;h2&gt;
&lt;p&gt;If I had to distill everything I learned into one insight, it would be this: fuzzing works because most software bugs aren&#x27;t exotic or subtle—they&#x27;re mundane mistakes in handling unexpected inputs. Buffer overflows, integer overflows, use-after-free, null pointer dereferences—these are the bugs that fuzzing finds reliably, and these are the bugs that attackers exploit constantly.&lt;&#x2F;p&gt;
&lt;p&gt;We like to think of security vulnerabilities as requiring sophisticated programming errors or deep architectural flaws. But the reality is simpler and more humbling. Most vulnerabilities are just places where a developer didn&#x27;t validate an input, or made an incorrect assumption about bounds, or forgot to check a return value.&lt;&#x2F;p&gt;
&lt;p&gt;Fuzzing is effective not because it&#x27;s sophisticated, but because software is fragile in simple, predictable ways. By systematically exploring the space of possible inputs, fuzzing finds the places where our assumptions break down.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s both encouraging and sobering. Encouraging because it means we have practical tools to find and fix these issues. Sobering because it suggests we&#x27;re not very good at writing robust software in the first place.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;a class=&quot;post-anchor&quot; href=&quot;#resources&quot; aria-label=&quot;Anchor link for: resources&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;&#x2F;fuzzing&#x2F;tesi_magistrale.pdf&quot;&gt;A survey of fuzz-testing tools for vulnerability discovery&lt;&#x2F;a&gt; - My full thesis document&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;&#x2F;fuzzing&#x2F;Fuzzing101.pdf&quot;&gt;Fuzzing101&lt;&#x2F;a&gt; - Funny and beginner-friendly presentation on fuzzing concepts (In Italian)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Strafo&#x2F;scapy&quot;&gt;Forked Scapy with IEEE C37.118 support&lt;&#x2F;a&gt; - My extended Scapy library for fuzzing IEEE C37.118 protocol (see c37.118 branch)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;section class=&quot;alert note&quot; role=&quot;note&quot; aria-labelledby=&quot;Yo8JWS1N&quot;&gt;
    &lt;div class=&quot;alert-icon alert-icon-info&quot;&gt;&lt;&#x2F;div&gt;
    &lt;div class=&quot;alert-content&quot; role=&quot;presentation&quot;&gt;
        &lt;strong id=&quot;Yo8JWS1N&quot; class=&quot;alert-title&quot; aria-hidden=&quot;true&quot;&gt;License&lt;&#x2F;strong&gt;
        &lt;p&gt;This article is licensed under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;CC BY-SA 4.0 license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

    &lt;&#x2F;div&gt;
&lt;&#x2F;section&gt;
</description>
      </item>
    </channel>
</rss>
