<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://carlosneto.dev</id>
  <title>Carlos Neto</title>
  <updated>2026-08-11T16:26:21.751988+00:00</updated>
  <link href="https://carlosneto.dev"/>
  <link href="https://carlosneto.dev/blog/atom.xml" rel="self"/>
  <generator uri="https://ablog.readthedocs.io/" version="0.11.12">ABlog</generator>
  <subtitle>DevOps, Python, OpenSearch, and Log Pipelines.</subtitle>
  <entry>
    <id>https://carlosneto.dev/blog/2026/2026-07-15-vim-kubernetes-exams/</id>
    <title>Vim for Kubernetes Certification Exams (CKA, CKAD &amp; CKS)</title>
    <updated>2026-07-15T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2026/07/15&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="vim-for-kubernetes-certification-exams-cka-ckad-cks"&gt;

&lt;p&gt;This post focuses on the minimal Vim configuration and editing techniques I use during Kubernetes certification exams from the Linux Foundation. The goal is not to master Vim, but to learn a small set of commands that can improve your editing speed during the exam.&lt;/p&gt;
&lt;section id="vim-less-mouse-more-efficiency"&gt;
&lt;h2&gt;Vim: Less Mouse, More Efficiency&lt;/h2&gt;
&lt;p&gt;The exam workstation includes &lt;em&gt;&lt;a class="reference external" href="https://vscodium.com/"&gt;VSCodium&lt;/a&gt;&lt;/em&gt; (&lt;em&gt;the free and open-source binaries of VS Code&lt;/em&gt;), but it’s not a practical choice during the exam. While &lt;em&gt;VSCodium&lt;/em&gt; is available on the workstation, all exam tasks are performed on remote nodes via the terminal over SSH, where it is unavailable. In addition, the connection to the exam workstation often has noticeable latency, making mouse operations slow and unreliable. The less you rely on the mouse, the faster you’ll be able to edit files. This is where Vim shines.&lt;/p&gt;
&lt;p&gt;You don’t need to become a Vim expert to pass the exam. This guide covers everything you need to efficiently edit Kubernetes YAML manifests under exam conditions. Focus on building muscle memory by practicing these commands in the simulation labs.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="vim-configuration"&gt;
&lt;h2&gt;Vim Configuration&lt;/h2&gt;
&lt;p&gt;This configuration is intentionally minimal and optimized for editing Kubernetes YAML manifests. Two-space indentation helps prevent invalid YAML formatting, line numbers make it easier to jump to specific locations, syntax highlighting improves readability, and cursor highlighting helps you keep track of indentation levels.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;~/.vimrc&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-vim notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;nu&lt;/span&gt;        &lt;span class="c"&gt;&amp;quot; Show line numbers&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;ai&lt;/span&gt;        &lt;span class="c"&gt;&amp;quot; Preserve indentation while editing&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;et&lt;/span&gt;        &lt;span class="c"&gt;&amp;quot; Convert tabs into spaces&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;      &lt;span class="c"&gt;&amp;quot; Display tabs as two spaces&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;      &lt;span class="c"&gt;&amp;quot; Indent and unindent using two spaces&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;sts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;     &lt;span class="c"&gt;&amp;quot; Make Tab and Backspace use two-space indentation&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;hls&lt;/span&gt;       &lt;span class="c"&gt;&amp;quot; Highlight search matches&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;cuc&lt;/span&gt;       &lt;span class="c"&gt;&amp;quot; Highlight the current cursor column&lt;/span&gt;
&lt;span class="nb"&gt;syntax&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;     &lt;span class="c"&gt;&amp;quot; Enable syntax highlighting&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;em&gt;Avoid using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;set&lt;/span&gt; &lt;span class="pre"&gt;mouse=a&lt;/span&gt;&lt;/code&gt; due to the exam workstation’s high connection latency. Also, the clipboard settings &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;clipboard+=unnamedplus,autoselect&lt;/span&gt;&lt;/code&gt; do not work on the exam workstations.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Each exam task is performed on a different remote node. Before starting a task, copy your local &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.vimrc&lt;/span&gt;&lt;/code&gt; to the target question machine:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;scp&lt;span class="w"&gt; &lt;/span&gt;~/.vimrc&lt;span class="w"&gt; &lt;/span&gt;node01:~/.vimrc
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="essential-editing-commands"&gt;
&lt;h2&gt;Essential Editing Commands&lt;/h2&gt;
&lt;p&gt;These are the commands you’ll use most often. They let you navigate, copy, delete, replace, and repeat edits without relying on the mouse.&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;o&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Open a new line below&lt;/span&gt;
Shift+o&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# Open a new line above&lt;/span&gt;
i&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Enter Insert mode&lt;/span&gt;
Shift+a&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# Enter Insert mode at the end of the line&lt;/span&gt;
cW&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# Cut the word under the cursor and enter Insert mode (for word replacement)&lt;/span&gt;
Shift+c&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# Cut to the end of the line and enter Insert mode (for line replacement)&lt;/span&gt;
Ctrl+y&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# Copy the character from the line above&lt;/span&gt;

y&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Yank (copy)&lt;/span&gt;
p&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Paste below&lt;/span&gt;
Shift+p&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# Paste above&lt;/span&gt;
dd&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# Cut the current line&lt;/span&gt;
dG&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# Cut to the end of the file&lt;/span&gt;
dgg&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# Cut to the beginning of the file&lt;/span&gt;

u&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Undo&lt;/span&gt;
Ctrl+r&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# Redo&lt;/span&gt;

Shift+v&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# Enter Visual Line mode (for indentation and copying)&lt;/span&gt;
Ctrl+v&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# Enter Visual Block mode (for commenting lines)&lt;/span&gt;
&amp;gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Indent to the right&lt;/span&gt;
&amp;lt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Indent to the left&lt;/span&gt;
.&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Repeat the last command&lt;/span&gt;
~&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Toggle character case&lt;/span&gt;

:retab&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# Convert tabs to spaces&lt;/span&gt;
:wq&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# Save and quit (or ZZ)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="cursor-movement"&gt;
&lt;h2&gt;Cursor Movement&lt;/h2&gt;
&lt;p&gt;As mentioned previously, I strongly recommend &lt;strong&gt;not&lt;/strong&gt; using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;set&lt;/span&gt; &lt;span class="pre"&gt;mouse=a&lt;/span&gt;&lt;/code&gt; during the exam. The remote exam workstations have higher latency than the Killer Shell simulation environment, making mouse interactions in Vim slow and unreliable. Use the mouse only for scrolling. For navigation, use the following commands:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;/greeting&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# Search for &amp;quot;greeting&amp;quot;&lt;/span&gt;
/foo/bar&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Search for &amp;quot;foo/bar&amp;quot;&lt;/span&gt;
n&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Jump to the next match&lt;/span&gt;
N&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Jump to the previous match&lt;/span&gt;
12gg&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# Jump to line 12&lt;/span&gt;
&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Jump to the beginning of the line&lt;/span&gt;
gg&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# Jump to the beginning of the file&lt;/span&gt;
G&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Jump to the end of the file&lt;/span&gt;
W&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Jump to the next word&lt;/span&gt;
B&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Jump to the previous word&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="yaml-indentation"&gt;
&lt;h2&gt;YAML Indentation&lt;/h2&gt;
&lt;p&gt;Indentation mistakes are one of the most common causes of invalid Kubernetes manifests. Vim makes it easy to indent entire YAML blocks to the right or left while preserving their structure.&lt;/p&gt;
&lt;p&gt;To increase the indentation:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Shift+v&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# 1. Select lines in Visual Line mode (↑↓)&lt;/span&gt;
&amp;gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# 2. Shift indentation to the right&lt;/span&gt;
ESC&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;# 3. Apply the change&lt;/span&gt;
.&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# 4. Repeat the indentation&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you need to reverse the indentation or perform another operation on the same lines, use &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;gv&lt;/span&gt;&lt;/code&gt; to restore the previous Visual selection:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;gv&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# 1. Reselect the previous Visual selection&lt;/span&gt;
&amp;lt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;# 2. Shift indentation to the left&lt;/span&gt;
ESC&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# 3. Apply the change&lt;/span&gt;
.&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;# 4. Repeat the operation&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="commenting-multiple-lines"&gt;
&lt;h2&gt;Commenting Multiple Lines&lt;/h2&gt;
&lt;p&gt;Visual Block mode allows you to insert the same text across multiple lines simultaneously. This is particularly useful for commenting or uncommenting YAML blocks.&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Ctrl+v&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# 1. Select columns across lines in Visual Block mode (↑↓)&lt;/span&gt;
Shift+i&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# 2. Enter Insert mode&lt;/span&gt;
&lt;span class="c1"&gt;#         # 3. Type the comment character&lt;/span&gt;
ESC&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;# 4. Apply the change to all selected lines&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The same technique can also be used to insert identical text across multiple lines, such as prefixes, labels, or environment variables.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="search-and-replace"&gt;
&lt;h2&gt;Search and Replace&lt;/h2&gt;
&lt;p&gt;When renaming resources, labels, namespaces, image names, or environment variables, replacing every occurrence manually is both slow and error-prone.&lt;/p&gt;
&lt;p&gt;To replace every occurrence of a string:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;:%s/foo/bar/g&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="c1"&gt;# Replace &amp;quot;foo&amp;quot; with &amp;quot;bar&amp;quot;&lt;/span&gt;
:%s#/kubernets/#/kubernetes/#g&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Replace &amp;quot;/kubernets/&amp;quot; with &amp;quot;/kubernetes/&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Sometimes, however, you only want to replace one occurrence at a time. In that case, use the following workflow:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;*&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# Search for the word under the cursor (or use &amp;quot;/greeting&amp;quot;)&lt;/span&gt;
cW&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# Replace the current occurrence&lt;/span&gt;
foobar&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Type the replacement text&lt;/span&gt;
n&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# Jump to the next match&lt;/span&gt;
.&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# Repeat the replacement&lt;/span&gt;
n&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# Jump to the next match&lt;/span&gt;
n&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# Skip this match and jump to the next one&lt;/span&gt;
Shift+n&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# Go back to the previous match&lt;/span&gt;
.&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# Repeat the replacement here&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This approach lets you review each occurrence before replacing it, making it safer than a global search-and-replace.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="working-with-yaml-blocks"&gt;
&lt;h2&gt;Working with YAML Blocks&lt;/h2&gt;
&lt;p&gt;Many editing operations can be performed directly on a range of line numbers without entering Visual mode. This is especially useful when moving, copying, deleting, or reindenting large YAML blocks.&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;:%&amp;lt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# Unindent all lines&lt;/span&gt;
:10,15&amp;gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# Indent lines 10-15&lt;/span&gt;
:10,15&amp;lt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# Unindent lines 10-15&lt;/span&gt;
:30,50x&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# Delete lines 30-50&lt;/span&gt;
:30,50t70&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# Copy lines 30-50 and paste below line 70&lt;/span&gt;
:30,50m70&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# Cut lines 30-50 and paste below line 70&lt;/span&gt;
:30,50d&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# Cut lines 30-50&lt;/span&gt;
:10,15y&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# Copy lines 10-15&lt;/span&gt;
:50put&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# Paste lines below line 50&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="working-with-numbers"&gt;
&lt;h2&gt;Working with Numbers&lt;/h2&gt;
&lt;p&gt;Kubernetes manifests frequently contain numeric values such as replica counts, ports, resource requests, limits, and probe timings. Instead of deleting and retyping numbers, Vim can increment or decrement them directly.&lt;/p&gt;
&lt;p&gt;Place the cursor before or on a number, then use &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Ctrl+a&lt;/span&gt;&lt;/code&gt; to increment it or &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Ctrl+x&lt;/span&gt;&lt;/code&gt; to decrement it. You can also provide a count to change the value by a larger amount:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;»&lt;span class="w"&gt; &lt;/span&gt;Ctrl+a&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# Increment the next number by 50&lt;/span&gt;
&lt;span class="m"&gt;40&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;»&lt;span class="w"&gt; &lt;/span&gt;Ctrl+x&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# Decrement the next number by 40&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For example, with the cursor on &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;replicas&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Pressing &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Ctrl+a&lt;/span&gt;&lt;/code&gt; changes it to:&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Likewise, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;10&lt;/span&gt; &lt;span class="pre"&gt;»&lt;/span&gt; &lt;span class="pre"&gt;Ctrl+a&lt;/span&gt;&lt;/code&gt; changes it from &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;3&lt;/span&gt;&lt;/code&gt; to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;13&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="running-shell-commands-in-vim"&gt;
&lt;h2&gt;Running Shell Commands in Vim&lt;/h2&gt;
&lt;p&gt;You can leverage operating system CLI tools to process and edit file content directly inside Vim. For example, you can execute &lt;a class="reference external" href="https://github.com/mikefarah/yq"&gt;yq&lt;/a&gt; on the current buffer to check for syntax errors and format the file:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;:%!yq
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can also apply the same approach to a selection of lines. For example, to sort and remove duplicate lines, select the lines and pipe them through &lt;a class="reference external" href="https://man7.org/linux/man-pages/man1/sort.1.html"&gt;sort&lt;/a&gt; and &lt;a class="reference external" href="https://ss64.com/bash/uniq.html"&gt;uniq&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Shift+v&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# 1. Select lines in Visual Line mode (↑↓)&lt;/span&gt;
:!sort&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;uniq&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# 2. Sort and remove duplicates from the selection&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Vim can also execute commands without modifying the current buffer. This is useful when you need to quickly inspect information from the cluster while editing a manifest. For example, when editing a NetworkPolicy, you can check pod and namespace labels with:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;:!kubectl&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;pods,ns&lt;span class="w"&gt; &lt;/span&gt;--show-labels&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;foobar
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Another useful trick is saving protected files without opening them with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;sudo&lt;/span&gt; &lt;span class="pre"&gt;vim&lt;/span&gt;&lt;/code&gt;. This is particularly handy when you forget to open a file with elevated privileges and have already made several changes. Instead of reopening the file with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;sudo&lt;/span&gt; &lt;span class="pre"&gt;vim&lt;/span&gt;&lt;/code&gt; and repeating your edits, you can write the current buffer using:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;:w&lt;span class="w"&gt; &lt;/span&gt;!sudo&lt;span class="w"&gt; &lt;/span&gt;tee&lt;span class="w"&gt; &lt;/span&gt;%
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;:w&lt;/span&gt;&lt;/code&gt; normally saves the buffer to a file. When followed by &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;!&lt;/span&gt;&lt;/code&gt;, it sends the buffer’s content to an external command’s standard input (STDIN) instead of writing to the file directly.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="learn-more"&gt;
&lt;h2&gt;Learn More&lt;/h2&gt;
&lt;p&gt;Take a look at the complete Vim cheatsheet at &lt;a class="reference external" href="https://vim.rtorr.com/"&gt;https://vim.rtorr.com/&lt;/a&gt; to learn more Vim commands and tricks.&lt;/p&gt;
&lt;p&gt;For more tips on how I configure my Kubernetes exam workstation, including Bash aliases and search history shortcuts, take a look at my previous blog post &lt;a class="reference internal" href="2026/2026-07-09-workstation-setup-kubernetes-exams/"&gt;&lt;span class="std std-doc"&gt;Workstation Setup for Kubernetes Exams (CKA, CKAD &amp;amp; CKS)&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2026/2026-07-15-vim-kubernetes-exams/"/>
    <summary>Blog Post Publish Date: 2026/07/15This post focuses on the minimal Vim configuration and editing techniques I use during Kubernetes certification exams from the Linux Foundation. The goal is not to master Vim, but to learn a small set of commands that can improve your editing speed during the exam.</summary>
    <category term="cka" label="cka"/>
    <category term="ckad" label="ckad"/>
    <category term="cks" label="cks"/>
    <category term="kubernetes" label="kubernetes"/>
    <category term="linux-foundation" label="linux-foundation"/>
    <published>2026-07-15T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2026/2026-07-09-workstation-setup-kubernetes-exams/</id>
    <title>Workstation Setup for Kubernetes Exams (CKA, CKAD &amp; CKS)</title>
    <updated>2026-07-09T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2026/07/09&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="workstation-setup-for-kubernetes-exams-cka-ckad-cks"&gt;

&lt;p&gt;This blog post walks through the workstation optimizations I use to work more efficiently during the exam, including Vim configuration, Bash keybindings, shell aliases, and other tweaks that help reduce friction and save valuable time.&lt;/p&gt;
&lt;p&gt;Time management is one of the most critical factors for passing the Linux Foundation Kubernetes certification exams. The CKA, CKAD, and CKS are hands-on exams conducted on remote Ubuntu instances running the XFCE desktop environment, where network latency and the remote desktop experience can slow you down. Check out the following tips to optimize your setup.&lt;/p&gt;
&lt;section id="configure-the-u-s-keyboard-layout-macbooks"&gt;
&lt;h2&gt;Configure the U.S. Keyboard Layout (MacBooks)&lt;/h2&gt;
&lt;p&gt;If you’re taking the exam on a MacBook, make sure your keyboard layout is set to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;U.S.&lt;/span&gt;&lt;/code&gt;. Otherwise, the remote Linux environment may not recognize certain characters correctly, especially &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;`&lt;/span&gt;&lt;/code&gt;. This is a common issue with layouts such as &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;U.S.&lt;/span&gt; &lt;span class="pre"&gt;International&lt;/span&gt; &lt;span class="pre"&gt;-&lt;/span&gt; &lt;span class="pre"&gt;PC&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Brazilian&lt;/span&gt; &lt;span class="pre"&gt;–&lt;/span&gt; &lt;span class="pre"&gt;ABNT2&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Before the exam, perform the following steps:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Open &lt;em&gt;&lt;strong&gt;System Settings » Keyboard » Input Sources&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;U.S.&lt;/span&gt;&lt;/code&gt; input source.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Remove any other input sources so only &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;U.S.&lt;/span&gt;&lt;/code&gt; remains.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This helps prevent unexpected keyboard mapping issues during the exam.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="disable-mission-control-shortcuts-macbooks"&gt;
&lt;h2&gt;Disable Mission Control Shortcuts (MacBooks)&lt;/h2&gt;
&lt;p&gt;By default, pressing &lt;strong&gt;CTRL + ←→&lt;/strong&gt; switches between macOS Spaces. During the exam, this can be interpreted by the proctor as leaving the exam environment, potentially triggering a warning or even interrupting your session.&lt;/p&gt;
&lt;p&gt;Before the exam, perform the following steps:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Open &lt;em&gt;&lt;strong&gt;System Settings » Keyboard » Keyboard Shortcuts&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select &lt;em&gt;&lt;strong&gt;Mission Control&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Disable all shortcuts under &lt;em&gt;&lt;strong&gt;Mission Control&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This helps prevent accidental desktop switching and reduces the risk of unnecessary proctor alerts or exam suspension on suspicion of consulting unauthorized material.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="disable-window-tiling-apps-macbook"&gt;
&lt;h2&gt;Disable Window Tiling Apps (MacBook)&lt;/h2&gt;
&lt;p&gt;If you use window tiling apps such as &lt;a class="reference external" href="https://rectangleapp.com/"&gt;Rectangle&lt;/a&gt; or &lt;a class="reference external" href="https://magnet.crowdcafe.com/"&gt;Magnet&lt;/a&gt;, it’s strongly recommended to disable/close them before the exam. Their keyboard shortcuts can conflict with the exam workstation, causing unexpected behavior when using shortcuts such as &lt;strong&gt;⌘ + ↑↓←→&lt;/strong&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="history-search-by-prefix"&gt;
&lt;h2&gt;History Search by Prefix&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;This is probably my most valuable tip!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The exam environment uses &lt;strong&gt;Bash&lt;/strong&gt; as the default shell. Behind the scenes, Bash relies on the &lt;a class="reference external" href="https://tiswww.case.edu/php/chet/readline/readline.html"&gt;GNU Readline&lt;/a&gt; library, which provides command-line editing, key bindings, and history navigation. For example, the interactive reverse search that appears when you press &lt;strong&gt;CTRL + R&lt;/strong&gt; is implemented by &lt;a class="reference external" href="https://tiswww.case.edu/php/chet/readline/readline.html"&gt;GNU Readline&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of Readline’s most useful features is history search by prefix, that is disabled by default. The &lt;a class="reference external" href="https://tiswww.case.edu/php/chet/readline/readline.html#index-history_002dsearch_002dbackward-_0028_0029"&gt;history-search-backward&lt;/a&gt; and &lt;a class="reference external" href="https://tiswww.case.edu/php/chet/readline/readline.html#index-history_002dsearch_002dforward-_0028_0029"&gt;history-search-forward&lt;/a&gt; functions search your command history using the text that already exists before the cursor, instead of simply moving to the previous or next command.&lt;/p&gt;
&lt;p&gt;For example, imagine your command history contains:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;pods
kubectl&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;nodes
kubectl&lt;span class="w"&gt; &lt;/span&gt;describe&lt;span class="w"&gt; &lt;/span&gt;pod&lt;span class="w"&gt; &lt;/span&gt;nginx
helm&lt;span class="w"&gt; &lt;/span&gt;list
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you type:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;g
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;and press &lt;strong&gt;↑&lt;/strong&gt;, &lt;a class="reference external" href="https://tiswww.case.edu/php/chet/readline/readline.html#index-history_002dsearch_002dbackward-_0028_0029"&gt;history-search-backward&lt;/a&gt; cycles only through commands that begin with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kubectl&lt;/span&gt; &lt;span class="pre"&gt;g&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;nodes
kubectl&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;pods
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Commands such as &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kubectl&lt;/span&gt; &lt;span class="pre"&gt;describe&lt;/span&gt; &lt;span class="pre"&gt;pod&lt;/span&gt; &lt;span class="pre"&gt;nginx&lt;/span&gt;&lt;/code&gt; or &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;helm&lt;/span&gt; &lt;span class="pre"&gt;list&lt;/span&gt;&lt;/code&gt; are skipped because they do not match the typed prefix.&lt;/p&gt;
&lt;p&gt;This is much faster than the default &lt;strong&gt;↑&lt;/strong&gt; behavior, which walks through every command in your history, and often more convenient than repeatedly using &lt;strong&gt;CTRL + R&lt;/strong&gt; during the exam.&lt;/p&gt;
&lt;p&gt;To enable this behavior, create the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.inputrc&lt;/span&gt;&lt;/code&gt; file and add the following configuration:&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;~/.inputrc&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;\e[A&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;history-search-backward&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# ↑ (previous matching command)&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;\e[B&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;history-search-forward&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# ↓ (next matching command)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The file is automatically loaded whenever a new Bash session starts. If you modify it during an existing session, reload it with:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;~/.inputrc
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="setting-up-vim"&gt;
&lt;h2&gt;Setting Up Vim&lt;/h2&gt;
&lt;p&gt;The exam workstation includes &lt;a class="reference external" href="https://vscodium.com/"&gt;VSCodium&lt;/a&gt;. However, every exam task requires you to connect to a remote node via SSH, and VSCodium is not available on those remote machines. In addition, the exam workstation often has high latency, making mouse operation unreliable, this is where Vim shines.&lt;/p&gt;
&lt;p&gt;The following settings provide a much better editing experience for Kubernetes YAML manifests.&lt;/p&gt;
&lt;p&gt;Create the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.vimrc&lt;/span&gt;&lt;/code&gt; file and add the following configuration:&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;~/.vimrc&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-vim notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;nu&lt;/span&gt;        &lt;span class="c"&gt;&amp;quot; Show line numbers&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;ai&lt;/span&gt;        &lt;span class="c"&gt;&amp;quot; Preserve indentation while editing&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;et&lt;/span&gt;        &lt;span class="c"&gt;&amp;quot; Convert tabs into spaces&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;      &lt;span class="c"&gt;&amp;quot; Display tabs as two spaces&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;      &lt;span class="c"&gt;&amp;quot; Indent and unindent using two spaces&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;sts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;     &lt;span class="c"&gt;&amp;quot; Make Tab and Backspace use two-space indentation&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;hls&lt;/span&gt;       &lt;span class="c"&gt;&amp;quot; Highlight search matches&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;cuc&lt;/span&gt;       &lt;span class="c"&gt;&amp;quot; Highlight the current cursor column&lt;/span&gt;
&lt;span class="nb"&gt;syntax&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;     &lt;span class="c"&gt;&amp;quot; Enable syntax highlighting&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Avoid using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;set&lt;/span&gt; &lt;span class="pre"&gt;mouse=a&lt;/span&gt;&lt;/code&gt; due to the exam workstation’s high connection latency. Also, the clipboard settings with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;clipboard+=unnamedplus,autoselect&lt;/span&gt;&lt;/code&gt; does not work on the exam workstations.&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;em&gt;I have created a blog post &lt;a class="reference internal" href="2026/2026-07-15-vim-kubernetes-exams/"&gt;&lt;span class="std std-doc"&gt;Vim for Kubernetes Certification Exams (CKA, CKAD &amp;amp; CKS)&lt;/span&gt;&lt;/a&gt; with editing techniques I use most frequently during exams.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="kubectl-aliases-and-shortcuts"&gt;
&lt;h2&gt;Kubectl Aliases and Shortcuts&lt;/h2&gt;
&lt;p&gt;Each remote exam node already contains a preconfigured &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.bashrc&lt;/span&gt;&lt;/code&gt; file with several useful settings. A handy trick is to copy it to main workstation, append your own aliases, and then copy it back to the question nodes.&lt;/p&gt;
&lt;p&gt;First, copy the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.bashrc&lt;/span&gt;&lt;/code&gt; from question node:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;scp&lt;span class="w"&gt; &lt;/span&gt;cks1234:~/.bashrc&lt;span class="w"&gt; &lt;/span&gt;~/.bashrc
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Append the following aliases to the end of the file:&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id3"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;~/.bashrc&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;### omitted the .bashrc copied from question node&lt;/span&gt;

&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kgp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;kubectl get pods&amp;quot;&lt;/span&gt;

&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kaf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;kubectl apply -f&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kdel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;kubectl delete --now&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kdelp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;kubectl delete pod --now&amp;quot;&lt;/span&gt;

&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;kubectl config set-context --current --namespace&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kctx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;kubectl config use-context&amp;quot;&lt;/span&gt;

&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;--dry-run=client -oyaml&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;INFO:&lt;/strong&gt; The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;export&lt;/span&gt; &lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; is useful shorthand for generating YAML in imperative kubectl commands such as &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;k&lt;/span&gt; &lt;span class="pre"&gt;run&lt;/span&gt; &lt;span class="pre"&gt;nginx&lt;/span&gt; &lt;span class="pre"&gt;--image&lt;/span&gt; &lt;span class="pre"&gt;nginx:latest&lt;/span&gt; &lt;span class="pre"&gt;$x&lt;/span&gt; &lt;span class="pre"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;nginx.yaml&lt;/span&gt;&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="copy-settings-to-exam-question-node"&gt;
&lt;h2&gt;Copy Settings to Exam Question Node&lt;/h2&gt;
&lt;p&gt;All exam tasks are required to be performed on the remote question node via SSH. A convenient workflow is to create your configuration files once on the main instance and then copy them to the question node before starting the task.&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;scp&lt;span class="w"&gt; &lt;/span&gt;~/.inputrc&lt;span class="w"&gt; &lt;/span&gt;~/.vimrc&lt;span class="w"&gt; &lt;/span&gt;~/.bashrc&lt;span class="w"&gt; &lt;/span&gt;cks1234:~/
ssh&lt;span class="w"&gt; &lt;/span&gt;cks1234
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="references"&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://tiswww.case.edu/php/chet/readline/readline.html#index-history_002dsearch_002dbackward-_0028_0029"&gt;https://tiswww.case.edu/php/chet/readline/readline.html#index-history_002dsearch_002dbackward-_0028_0029&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://vimdoc.sourceforge.net/htmldoc/options.html#'cursorcolumn'"&gt;https://vimdoc.sourceforge.net/htmldoc/options.html#’cursorcolumn’&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://vim.rtorr.com/"&gt;https://vim.rtorr.com/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://kubernetes.io/docs/reference/kubectl/quick-reference/"&gt;https://kubernetes.io/docs/reference/kubectl/quick-reference/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#-em-deployment-em-"&gt;https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#-em-deployment-em-&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2026/2026-07-09-workstation-setup-kubernetes-exams/"/>
    <summary>Blog Post Publish Date: 2026/07/09This blog post walks through the workstation optimizations I use to work more efficiently during the exam, including Vim configuration, Bash keybindings, shell aliases, and other tweaks that help reduce friction and save valuable time.</summary>
    <category term="cka" label="cka"/>
    <category term="ckad" label="ckad"/>
    <category term="cks" label="cks"/>
    <category term="kubernetes" label="kubernetes"/>
    <category term="linux-foundation" label="linux-foundation"/>
    <published>2026-07-09T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2026/2026-02-25-helm-tips-inspect-context/</id>
    <title>Helm Tips: Inspect Your Chart Context with JSON</title>
    <updated>2026-02-25T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2026/02/25&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="helm-tips-inspect-your-chart-context-with-json"&gt;

&lt;p&gt;Debugging Helm charts can be frustrating when starting out, especially if you come from programming languages with debuggers and IDE auto-completion that provide a smoother development experience. It is common to face issues when you create a template that tries to access a parameter defined in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.Values&lt;/span&gt;&lt;/code&gt; files, and for any reason, whether a syntax error or typo, the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;helm&lt;/span&gt; &lt;span class="pre"&gt;template&lt;/span&gt;&lt;/code&gt; command fails, and the stack trace shows confusing messages that don’t point to the root cause. This blog post provides an alternative approach to inspect the Helm Chart context with JSON, helping you understand how to access chart properties for use in your templates.&lt;/p&gt;
&lt;section id="are-there-ides-for-helm-charts"&gt;
&lt;h2&gt;Are There IDEs for Helm Charts?&lt;/h2&gt;
&lt;p&gt;Unfortunately, there is no definitive IDE specifically for Helm. There are plugins for VSCode and JetBrains IDEA that provide some tooling for auto-completion, but they don’t come close to the code experience available with popular programming languages like Python and Java. I use VSCode’s &lt;a class="reference external" href="https://marketplace.visualstudio.com/items?itemName=ms-kubernetes-tools.vscode-kubernetes-tools"&gt;Kubernetes (by Microsoft)&lt;/a&gt; + &lt;a class="reference external" href="https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml"&gt;YAML (by Red Hat)&lt;/a&gt;, which helps avoid typos in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.Values&lt;/span&gt;&lt;/code&gt; usage and checks available Go template functions. However, there are limitations, some variables are omitted in auto-complete, which diminishes the experience. I tried the &lt;a class="reference external" href="https://www.jetbrains.com/help/idea/helm.html"&gt;JetBrains Helm Plugin&lt;/a&gt;, but it doesn’t work as simply as the &lt;em&gt;.gif&lt;/em&gt; present in the Plugin page shows. Additionally, the Kubernetes plugin is not available in IntelliJ IDEA without an Ultimate subscription.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="debug-the-right-way"&gt;
&lt;h2&gt;Debug the Right Way!&lt;/h2&gt;
&lt;p&gt;As an alternative to lacks a robust auto-completion, you can dump the entire Helm Chart context in JSON format to understand the Values references, Chart and Subchart metadata, and available static files.&lt;/p&gt;
&lt;p&gt;Let’s get started. First, create a Helm Chart called &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;foobar&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;helm&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;foobar
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, create a template named &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;debug.yaml&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;vim&lt;span class="w"&gt; &lt;/span&gt;templates/debug.yaml
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;debug.yaml&lt;/span&gt;&lt;/code&gt; file should contain:&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;. | toPrettyJson&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;}}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, render only the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;debug.yaml&lt;/span&gt;&lt;/code&gt; template:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;helm&lt;span class="w"&gt; &lt;/span&gt;template&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;templates/debug.yaml&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tail&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;+3&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jq
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The output shows the Helm Chart context with all variables available in your templates.&lt;/p&gt;
&lt;div class="highlight-json notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Capabilities&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;KubeVersion&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;v1.34.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Major&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Minor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;34&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;APIVersions&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;admissionregistration.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;admissionregistration.k8s.io/v1alpha1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;admissionregistration.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;internal.apiserver.k8s.io/v1alpha1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;apps/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;apps/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;apps/v1beta2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;authentication.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;authentication.k8s.io/v1alpha1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;authentication.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;authorization.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;authorization.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;autoscaling/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;autoscaling/v2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;autoscaling/v2beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;autoscaling/v2beta2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;batch/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;batch/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;certificates.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;certificates.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;certificates.k8s.io/v1alpha1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordination.k8s.io/v1alpha2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordination.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordination.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;discovery.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;discovery.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;events.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;events.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;extensions/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;flowcontrol.apiserver.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;flowcontrol.apiserver.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;flowcontrol.apiserver.k8s.io/v1beta2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;flowcontrol.apiserver.k8s.io/v1beta3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;networking.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;networking.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;node.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;node.k8s.io/v1alpha1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;node.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;policy/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;policy/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rbac.authorization.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rbac.authorization.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rbac.authorization.k8s.io/v1alpha1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;resource.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;resource.k8s.io/v1beta2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;resource.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;resource.k8s.io/v1alpha3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;scheduling.k8s.io/v1alpha1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;scheduling.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;scheduling.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;storage.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;storage.k8s.io/v1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;storage.k8s.io/v1alpha1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;storagemigration.k8s.io/v1alpha1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;apiextensions.k8s.io/v1beta1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;apiextensions.k8s.io/v1&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;HelmVersion&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;v4.0.5&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;git_commit&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1b6053d48b51673c5581973f5ae7e104f627fcf5&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;git_tree_state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;clean&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;go_version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;go1.25.5&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;kube_client_version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;v1.34&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Chart&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;APIVersion&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;v2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Annotations&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;AppVersion&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1.16.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Condition&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Dependencies&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Deprecated&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Description&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A Helm chart for Kubernetes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Home&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Icon&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;IsRoot&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Keywords&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;KubeVersion&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Maintainers&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;foobar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Sources&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Tags&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;application&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;0.1.0&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Files&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;.helmignore&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;IyBQYXR0ZXJucyB0by...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;output.yaml&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;LS0tCiMgU291cmNlOi...&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Release&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;IsInstall&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;IsUpgrade&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;release-name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Namespace&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Revision&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Service&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Helm&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Subcharts&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Template&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;BasePath&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;foobar/templates&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;foobar/templates/debug.yaml&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Values&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;affinity&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;autoscaling&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;enabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;maxReplicas&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;minReplicas&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;targetCPUUtilizationPercentage&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;fullnameOverride&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;httpRoute&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;annotations&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;enabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;hostnames&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;chart-example.local&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;parentRefs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;gateway&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;sectionName&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;rules&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;matches&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PathPrefix&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/headers&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;image&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;pullPolicy&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;IfNotPresent&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;repository&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;nginx&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;tag&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;imagePullSecrets&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;ingress&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;annotations&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;className&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;enabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;hosts&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;host&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;chart-example.local&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;paths&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;pathType&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ImplementationSpecific&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;tls&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;livenessProbe&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;httpGet&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;port&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;nameOverride&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;nodeSelector&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;podAnnotations&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;podLabels&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;podSecurityContext&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;readinessProbe&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;httpGet&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;port&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;replicaCount&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;resources&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;securityContext&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;service&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;port&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ClusterIP&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;serviceAccount&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;annotations&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;automount&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;create&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;tolerations&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;volumeMounts&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;volumes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2026/2026-02-25-helm-tips-inspect-context/"/>
    <summary>Blog Post Publish Date: 2026/02/25Debugging Helm charts can be frustrating when starting out, especially if you come from programming languages with debuggers and IDE auto-completion that provide a smoother development experience. It is common to face issues when you create a template that tries to access a parameter defined in the .Values files, and for any reason, whether a syntax error or typo, the $ helm template command fails, and the stack trace shows confusing messages that don’t point to the root cause. This blog post provides an alternative approach to inspect the Helm Chart context with JSON, helping you understand how to access chart properties for use in your templates.</summary>
    <category term="python" label="python"/>
    <published>2026-02-25T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2025/2025-07-28-python-groupby/</id>
    <title>groupby + attrgetter: The Pythonic Way to Group Objects</title>
    <updated>2025-06-22T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2025/07/28&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="groupby-attrgetter-the-pythonic-way-to-group-objects"&gt;

&lt;p&gt;This blog post explores an approach to group objects by mulitple attribute name powered by capabilities of the built-in modules &lt;a class="reference external" href="https://docs.python.org/3/library/itertools.html#itertools.groupby"&gt;itertools.groupby&lt;/a&gt; and &lt;a class="reference external" href="https://docs.python.org/3/library/operator.html#operator.attrgetter"&gt;operator.attrgetter&lt;/a&gt; module.&lt;/p&gt;
&lt;p&gt;To illustrate this scenario, let’s create a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Band&lt;/span&gt;&lt;/code&gt; class, and a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bands&lt;/span&gt;&lt;/code&gt; variable with a list of the instances. The objective is create groups by attributes &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;category&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;genre&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;country&lt;/span&gt;&lt;/code&gt; and print the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;name&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dataclasses&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="n"&gt;bands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The Beatles&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Queen&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The Rolling Stones&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Led Zeppelin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The Who&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Deep Purple&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Nirvana&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Grunge&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;USA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Temple of the Dog&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Grunge&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;USA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Pearl Jam&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Grunge&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;USA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Soundgarden&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Grunge&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;USA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Alice in Chains&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Grunge&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;USA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Stone Temple Pilots&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Grunge&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;USA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Pink Floyd&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Progressive Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Genesis&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Progressive Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Yes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Progressive Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;King Crimson&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Progressive Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The Smiths&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Indie Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Arctic Monkeys&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Indie Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Pet Shop Boys&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Pop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Synth-pop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Depeche Mode&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Pop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Synth-pop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Erasure&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Pop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Synth-pop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Korine&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Pop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Synth-pop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;USA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Oasis&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Britpop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Blur&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Britpop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Pulp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Britpop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Echo &amp;amp; the Bunnymen&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Post-Punk&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Joy Division&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Post-Punk&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The Cure&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Post-Punk&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The Killers&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Alternative Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;USA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;R.E.M.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Alternative Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;USA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Radiohead&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Alternative Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Band&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;U2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Rock&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Ireland&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The most common approach is to create a temporary dictionary where the key is a tuple of the attributes you want to group by, and the value is a list of objects that share those attributes:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;bands_groupby_genre_country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;band&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;group_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;band&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;band&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;band&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;bands_groupby_genre_country&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;band&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;band_group&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bands_groupby_genre_country&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="si"&gt;=}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="si"&gt;=}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="si"&gt;=}&lt;/span&gt;&lt;span class="s2"&gt;:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;band&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;band_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;- &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;band&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This approach works, but the code readability can cause confusion. Therefore, I present a more pythonic way to perform this operation using &lt;a class="reference external" href="https://docs.python.org/3/library/itertools.html#itertools.groupby"&gt;groupby&lt;/a&gt; and &lt;a class="reference external" href="https://docs.python.org/3/library/operator.html#operator.attrgetter"&gt;attrgetter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://docs.python.org/3/library/itertools.groupby"&gt;groupby&lt;/a&gt; function takes an iterable as its first positional argument and has an optional named argument &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;key&lt;/span&gt;&lt;/code&gt;. The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;key&lt;/span&gt;&lt;/code&gt; works exactly the same way as in &lt;a class="reference external" href="https://docs.python.org/3/howto/sorting.html#key-functions"&gt;sorted&lt;/a&gt;, accepting a function that is called to determine the grouping. In the example below, the function is created using a &lt;a class="reference external" href="https://docs.python.org/2/tutorial/controlflow.html"&gt;lambda&lt;/a&gt;, where the groups are formed based on the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;category&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;genre&lt;/span&gt;&lt;/code&gt;, and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;country&lt;/span&gt;&lt;/code&gt; attributes.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;itertools&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;groupby&lt;/span&gt;

&lt;span class="n"&gt;attribute_group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bands&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attribute_group&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="si"&gt;=}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="si"&gt;=}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="si"&gt;=}&lt;/span&gt;&lt;span class="s2"&gt;:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;band&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;  - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;band&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This approach offers better readability. However, by using the &lt;a class="reference external" href="https://docs.python.org/3/library/operator.html#operator.attrgetter"&gt;attrgetter&lt;/a&gt; function, we can eliminate the &lt;a class="reference external" href="https://docs.python.org/2/tutorial/controlflow.html"&gt;lambda&lt;/a&gt; and make the code more self-explanatory. The &lt;a class="reference external" href="https://docs.python.org/3/library/operator.html#operator.attrgetter"&gt;attrgetter&lt;/a&gt; returns a callable object that fetches the specified attribute from its operand. If more than one attribute is requested, it returns a tuple of attributes. For practical purposes, this solution works the same way as the &lt;a class="reference external" href="https://docs.python.org/2/tutorial/controlflow.html"&gt;lambda&lt;/a&gt;, but the code is clearer and easier to understand.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;operator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;attrgetter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;itertools&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;groupby&lt;/span&gt;

&lt;span class="n"&gt;attribute_group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attrgetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;category&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;genre&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;country&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bands&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attribute_group&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="si"&gt;=}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="si"&gt;=}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="si"&gt;=}&lt;/span&gt;&lt;span class="s2"&gt;:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;band&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;  - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;band&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The script execution output will be:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;UK&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="go"&gt;- The Beatles&lt;/span&gt;
&lt;span class="go"&gt;- Queen&lt;/span&gt;
&lt;span class="go"&gt;- The Rolling Stones&lt;/span&gt;
&lt;span class="go"&gt;- Led Zeppelin&lt;/span&gt;
&lt;span class="go"&gt;- The Who&lt;/span&gt;
&lt;span class="go"&gt;- Deep Purple&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Grunge&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;USA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="go"&gt;- Nirvana&lt;/span&gt;
&lt;span class="go"&gt;- Temple of the Dog&lt;/span&gt;
&lt;span class="go"&gt;- Pearl Jam&lt;/span&gt;
&lt;span class="go"&gt;- Soundgarden&lt;/span&gt;
&lt;span class="go"&gt;- Alice in Chains&lt;/span&gt;
&lt;span class="go"&gt;- Stone Temple Pilots&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Progressive Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;UK&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="go"&gt;- Pink Floyd&lt;/span&gt;
&lt;span class="go"&gt;- Genesis&lt;/span&gt;
&lt;span class="go"&gt;- Yes&lt;/span&gt;
&lt;span class="go"&gt;- King Crimson&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Indie Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;UK&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="go"&gt;- The Smiths&lt;/span&gt;
&lt;span class="go"&gt;- Arctic Monkeys&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Pop&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Synth-pop&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;UK&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="go"&gt;- Pet Shop Boys&lt;/span&gt;
&lt;span class="go"&gt;- Depeche Mode&lt;/span&gt;
&lt;span class="go"&gt;- Erasure&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Pop&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Synth-pop&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;USA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="go"&gt;- Korine&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Britpop&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;UK&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="go"&gt;- Oasis&lt;/span&gt;
&lt;span class="go"&gt;- Blur&lt;/span&gt;
&lt;span class="go"&gt;- Pulp&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Post-Punk&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;UK&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="go"&gt;- Echo &amp;amp; the Bunnymen&lt;/span&gt;
&lt;span class="go"&gt;- Joy Division&lt;/span&gt;
&lt;span class="go"&gt;- The Cure&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Alternative Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;USA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="go"&gt;- The Killers&lt;/span&gt;
&lt;span class="go"&gt;- R.E.M.&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Alternative Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;UK&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="go"&gt;- Radiohead&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Ireland&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="go"&gt;- U2&lt;/span&gt;
&lt;span class="go"&gt;- Thin Lizzy&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the full working example, visit the &lt;a class="reference external" href="https://github.com/c-neto/my-code-playground/blob/main/blog-code-examples/2025-07-28-groupby/python-groupby-example.py"&gt;GitHub repository&lt;/a&gt;. You can also run the code directly in your terminal:&lt;/p&gt;
&lt;div class="highlight-sh notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;https://raw.githubusercontent.com/c-neto/my-code-playground/refs/heads/main/blog-code-examples/2025-07-28-groupby/python-groupby-example.py&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;-
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2025/2025-07-28-python-groupby/"/>
    <summary>Blog Post Publish Date: 2025/07/28This blog post explores an approach to group objects by mulitple attribute name powered by capabilities of the built-in modules itertools.groupby and operator.attrgetter module.</summary>
    <category term="python" label="python"/>
    <published>2025-06-22T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2025/2025-06-22-kubernetes-logs-simplified/</id>
    <title>Kubernetes Logs Simplified: Everything You Need to Know About Pod Logging</title>
    <updated>2025-06-22T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2025/06/22&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="kubernetes-logs-simplified-everything-you-need-to-know-about-pod-logging"&gt;

&lt;p&gt;Have you ever wondered how &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;kubectl&lt;/span&gt; &lt;span class="pre"&gt;logs&lt;/span&gt;&lt;/code&gt; retrieves logs, where they are stored, and how to access both current and past logs? Kubernetes logging can seem complex, but understanding it is key to troubleshooting and monitoring. This blog post outlines how Kubernetes organizes logs, how to send them to external systems, and tips to manage log growth effectively.&lt;/p&gt;
&lt;section id="how-kubernetes-manages-and-stores-logs"&gt;
&lt;h2&gt;How Kubernetes Manages and Stores Logs&lt;/h2&gt;
&lt;p&gt;Kubernetes is essentially composed of containers, which are managed by container runtimes. These container runtimes must implement the &lt;a class="reference external" href="https://kubernetes.io/docs/concepts/architecture/cri/"&gt;Container Runtime Interface (CRI)&lt;/a&gt;, which defines how Kubernetes interacts with the containers. One of the specifications in the &lt;a class="reference external" href="https://kubernetes.io/docs/concepts/architecture/cri/"&gt;CRI&lt;/a&gt; is container log handling, which standardizes details such as log names, directory locations, and formats.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;CRI Logs Desing Proposal: &lt;a class="github reference external" href="https://github.com/kubernetes/design-proposals-archive/blob/main/node/kubelet-cri-logging.md"&gt;kubernetes/design-proposals-archive&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Kubernetes Logging Reference: &lt;a class="reference external" href="https://kubernetes.io/docs/concepts/cluster-administration/logging/"&gt;https://kubernetes.io/docs/concepts/cluster-administration/logging/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each container in a pod writes its logs to a separate file. These logs capture everything the application outputs to &lt;em&gt;stdout&lt;/em&gt; and &lt;em&gt;stderr&lt;/em&gt; (see &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Standard_streams"&gt;standard streams&lt;/a&gt;). The log files are stored on the node where the pod is running at:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# pod container log file pattern&lt;/span&gt;
/var/log/pods/&amp;lt;namespace&amp;gt;_&amp;lt;podname&amp;gt;_&amp;lt;uid&amp;gt;/&amp;lt;container_name&amp;gt;/&amp;lt;execution-id&amp;gt;.log

&lt;span class="c1"&gt;# example&lt;/span&gt;
/var/log/pods/default_nginx-7f5c7d4f9b-abcde_12345/nginx-container/
&lt;span class="w"&gt;  &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.log&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# current container live running log&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.log&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# last container log that exited on failure or was restarted&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.log&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# second to last container log that exited on failure or was restarted&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;...
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;0.log&lt;/span&gt;&lt;/code&gt; file contains the current container’s live running logs. If the container crashes and restarts, Kubernetes renames &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;0.log&lt;/span&gt;&lt;/code&gt; to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;1.log&lt;/span&gt;&lt;/code&gt; and creates a new &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;0.log&lt;/span&gt;&lt;/code&gt; for the new run. This process continues with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;2.log&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;3.log&lt;/span&gt;&lt;/code&gt;, and so on. When you execute &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kubectl&lt;/span&gt; &lt;span class="pre"&gt;logs&lt;/span&gt; &lt;span class="pre"&gt;--previous&lt;/span&gt; &lt;span class="pre"&gt;&amp;lt;pod&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;-c&lt;/span&gt; &lt;span class="pre"&gt;&amp;lt;container&amp;gt;&lt;/span&gt;&lt;/code&gt;, the kubelet searches for logs in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;1.log&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;2.log&lt;/span&gt;&lt;/code&gt;, and subsequent files.&lt;/p&gt;
&lt;p&gt;To make log ingestion easier for third-party tools, the kubelet creates symbolic links to each container’s current log file (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;0.log&lt;/span&gt;&lt;/code&gt;) in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/var/log/containers/&lt;/span&gt;&lt;/code&gt; directory. Each symbolic link is named using the pod name, namespace, container name, and container ID, making it easy for log shippers to identify and process logs for each container.&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# &amp;gt;&amp;gt;&amp;gt; symbolic link log file name pattern&lt;/span&gt;
/var/log/containers/&amp;lt;pod_name&amp;gt;_&amp;lt;namespace&amp;gt;_&amp;lt;container_name&amp;gt;-&amp;lt;container_id&amp;gt;.log

&lt;span class="c1"&gt;# &amp;gt;&amp;gt;&amp;gt; symbolic link log file name example&lt;/span&gt;
/var/log/containers/nginx-7f5c7d4f9b-abcde_default_nginx-container-12345.log

&lt;span class="c1"&gt;# &amp;gt;&amp;gt;&amp;gt; real file that the symbolic link points to&lt;/span&gt;
/var/log/pods/default_nginx-7f5c7d4f9b-abcde_12345/nginx-container/0.log
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;kubectl&lt;/span&gt; &lt;span class="pre"&gt;logs&lt;/span&gt;&lt;/code&gt; command always uses the absolute path of the log file and never the symbolic link path. The symbolic link is a mechanism that the kubelet uses to simplify log ingestion for third-party log shipping tools like &lt;a class="reference external" href="https://docs.fluentbit.io/manual"&gt;Fluent Bit&lt;/a&gt;, &lt;a class="reference external" href="https://vector.dev/"&gt;Vector&lt;/a&gt;, &lt;a class="reference external" href="https://www.elastic.co/pt/beats/filebeat"&gt;Filebeat&lt;/a&gt;, etc. It is important to understand that the files present in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/var/log/containers/&lt;/span&gt;&lt;/code&gt; point to the current container execution log (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;0.log&lt;/span&gt;&lt;/code&gt;) and do not concatenate the files &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;0.log&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;1.log&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;2.log&lt;/span&gt;&lt;/code&gt;, and so on. When a container crashes and a new &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;0.log&lt;/span&gt;&lt;/code&gt; is created, the symbolic link points to the new &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;0.log&lt;/span&gt;&lt;/code&gt; file, so the content is overwritten. The log shipping tool should be prepared to handle this behavior. Fluent Bit, for example, uses inode tracking of the symlinked log file. When the file that the symlink points to is rotated, the inode of the file changes as well, and Fluent Bit understands that it is a new log file and needs to read the log content from the start, even though the file name remains the same. For more details, see &lt;a class="reference external" href="https://docs.fluentbit.io/manual/pipeline/inputs/tail#keep_state"&gt;the Fluent Bit Input Plugin reference&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="understanding-kubernetes-log-format"&gt;
&lt;h2&gt;Understanding Kubernetes Log Format&lt;/h2&gt;
&lt;p&gt;The log file content is saved in a specific pattern defined by the CRI. This log line format is:&lt;/p&gt;
&lt;div class="highlight-log notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;# &amp;gt;&amp;gt;&amp;gt; log line pattern
&amp;lt;timestamp&amp;gt; &amp;lt;stream&amp;gt; &amp;lt;flags&amp;gt; &amp;lt;message&amp;gt;

# &amp;gt;&amp;gt;&amp;gt; log line examples
2023-10-06T00:17:09.669794202Z stdout F Your log message here
2023-10-06T00:17:09.669794202Z stdout P Another log pt 1
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;timestamp&lt;/strong&gt;: The date and time when the log line was written, formatted according to &lt;a class="reference external" href="https://datatracker.ietf.org/doc/html/rfc3339"&gt;RFC3339&lt;/a&gt; with nanosecond precision.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;stream&lt;/strong&gt;: Specifies whether the log line was written to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;stdout&lt;/span&gt;&lt;/code&gt; or &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;stderr&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;flags&lt;/strong&gt;: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;F&lt;/span&gt;&lt;/code&gt; indicates a completed log line, delimited by &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;\n&lt;/span&gt;&lt;/code&gt;, while &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;P&lt;/span&gt;&lt;/code&gt; indicates a partial log line. More details will be explained below.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;log message&lt;/strong&gt;: The container’s log message in its raw format.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;About &lt;strong&gt;flags&lt;/strong&gt;: The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;F&lt;/span&gt;&lt;/code&gt; flag indicates a complete log line (ending with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;\n&lt;/span&gt;&lt;/code&gt;). The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;P&lt;/span&gt;&lt;/code&gt; flag signifies that the application has not finished writing the line yet, so the runtime is writing it in parts. When logs are retrieved using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kubectl&lt;/span&gt; &lt;span class="pre"&gt;logs&lt;/span&gt;&lt;/code&gt;, the tool merges these parts together before displaying them to the user.&lt;/p&gt;
&lt;p&gt;To understand this better, check the following Python application code:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/python&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;part 1 - &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;part 2 - &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;part 3 - &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;log line completed!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The logs are saved in the following format:&lt;/p&gt;
&lt;div class="highlight-log notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;2025-06-21T22:15:10.123456789Z stdout P part 1 - 
2025-06-21T22:15:10.123456790Z stdout P part 2 - 
2025-06-21T22:15:10.123456791Z stdout P part 3 - 
2025-06-21T22:15:10.123456792Z stdout F log line completed!
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;When a pod log is retrieved using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;kubectl&lt;/span&gt; &lt;span class="pre"&gt;logs&lt;/span&gt;&lt;/code&gt;, the output will concatenate partial logs:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;logs&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;python-app

part&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;part&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;part&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;log&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;completed!
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="managing-log-growth-with-log-rotation"&gt;
&lt;h2&gt;Managing Log Growth with Log Rotation&lt;/h2&gt;
&lt;p&gt;Logs can grow uncontrollably, causing disk overflow, which can impact observability in your cluster and degrade application performance. Even if the application generates a low volume of logs, it is a best practice and highly recommended to enforce limits and implement log rotation routines. The best approach to control log size limits is to define the values using container runtime parameters. It is not recommended to use third-party tools like &lt;a class="reference external" href="https://github.com/logrotate/logrotate"&gt;logrotate&lt;/a&gt; for this task, because file operations such as renaming, truncating, and closing file descriptors can corrupt Kubernetes’ log management state. Additionally, the container runtime’s log management architecture may change in future Kubernetes versions, requiring updates to third-party tools to align with the new behavior.&lt;/p&gt;
&lt;p&gt;Thus, the configuration defines the Container Runtime’s log rotation process. The example below demonstrates how to configure containerd to manage the log rotation routine.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;&lt;a class="reference external" href="https://github.com/containerd/containerd/blob/main/docs/man/containerd-config.toml.5.md"&gt;/etc/containerd/config.toml&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-toml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[plugins.&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;io.containerd.grpc.v1.cri&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;.containerd.runtimes.runc.options]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;LogSizeMax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;104857600&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# set log file size limit to 100MB&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;LogFileMax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# set limit 5 log files&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Also, you can introduce this parameters via environment variables to kubelet service. Below as example using systemd, to define the log rotate parameters for kubelet process to inject log rotate parameters to containerd.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;&lt;a class="reference external" href="https://github.com/kubernetes/release/blob/cd53840/cmd/krel/templates/latest/kubelet/kubelet.service"&gt;/etc/systemd/system/kubelet.service&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-ini notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;KUBELET_EXTRA_ARGS=--container-log-max-size=100Mi --container-log-max-files=5&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="strategy-to-ingest-kubernetes-logs-to-external-thirdy-party-system"&gt;
&lt;h2&gt;Strategy to Ingest Kubernetes Logs to External Thirdy Party System&lt;/h2&gt;
&lt;p&gt;For better observability and log search capabilities, it is highly recommended to ingest Kubernetes workload logs into a dedicated log analytics system such as &lt;a class="reference external" href="https://opensearch.org/"&gt;OpenSearch&lt;/a&gt;, &lt;a class="reference external" href="https://www.elastic.co/elasticsearch"&gt;Elasticsearch&lt;/a&gt;, &lt;a class="reference external" href="https://grafana.com/oss/loki/"&gt;Grafana Loki&lt;/a&gt;, &lt;a class="reference external" href="https://www.splunk.com/"&gt;Splunk&lt;/a&gt;, or &lt;a class="reference external" href="https://www.datadoghq.com/"&gt;Datadog&lt;/a&gt;. Within the Kubernetes cluster, specialized tools are optimized for reading, processing, and shipping logs to external systems, such as &lt;a class="reference external" href="https://docs.fluentbit.io/manual"&gt;Fluent Bit&lt;/a&gt;, &lt;a class="reference external" href="https://www.elastic.co/beats/filebeat"&gt;Filebeat&lt;/a&gt;, and &lt;a class="reference external" href="https://vector.dev/"&gt;Vector&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Regardless of which log shipping tool you choose, the deployment pattern remains the same. The log ingestion tool is deployed as a &lt;a class="reference external" href="https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/"&gt;DaemonSet&lt;/a&gt;, which schedules one pod per node in the cluster. The &lt;a class="reference external" href="https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/"&gt;DaemonSet&lt;/a&gt; is configured with a &lt;a class="reference external" href="https://kubernetes.io/docs/concepts/storage/volumes/#hostpath"&gt;hostPath&lt;/a&gt; volume mount, allowing the pod to access files stored on the node’s disk. This enables the log ingestion tool to read log files for all pod containers scheduled on the node at &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/var/log/containers/*.log&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Below is an example &lt;a class="reference external" href="https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/"&gt;DaemonSet&lt;/a&gt; configuration for deploying &lt;a class="reference external" href="https://docs.fluentbit.io/manual"&gt;Fluent Bit&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;apps/v1&lt;/span&gt;
&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;DaemonSet&lt;/span&gt;
&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;fluent-bit&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;default&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;fluent-bit&lt;/span&gt;
&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;matchLabels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;fluent-bit&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;fluent-bit&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;serviceAccountName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;fluent-bit&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;containers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;fluent-bit&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;docker.io/bitnami/fluent-bit:4.0.3-debian-12-r0&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200Mi&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;100m&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;100Mi&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;50m&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;volumeMounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;config&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nt"&gt;mountPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/opt/bitnami/fluent-bit/conf/fluent-bit.conf&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nt"&gt;subPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;fluent-bit.conf&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;# &amp;gt;&amp;gt;&amp;gt; THIS LINE MOUNTS THE NODE&amp;#39;S POD CONTAINERS DIRECTORY INSIDE THE CONTAINER.&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;varlog&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nt"&gt;mountPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/var/log/containers&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;config&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;configMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;fluent-bit-config&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# &amp;gt;&amp;gt;&amp;gt; THIS LINE ALLOWS THE FLUENT BIT POD TO ACCESS LOG FILES OF PODS SCHEDULED ON THE NODE. &lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;varlog&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;hostPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/var/log/containers&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Directory&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Many log collection tools can add extra information to your logs, like pod labels and annotations. To do this, the log collector needs permission to talk to the Kubernetes API. This is usually done by giving the tool a &lt;a class="reference external" href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/"&gt;ClusterRole&lt;/a&gt; with the right permissions and linking it to the service account the tool uses. For example, you can look at &lt;a class="reference external" href="https://github.com/bitnami/charts/blob/main/bitnami/fluent-bit/templates/clusterrole.yaml"&gt;this Fluent Bit ClusterRole&lt;/a&gt; to see how it works. With these permissions, the tool can get metadata from Kubernetes and add it to each log before sending it to your log system. Setting up metadata enrichment with &lt;a class="reference external" href="https://docs.fluentbit.io/manual"&gt;Fluent Bit&lt;/a&gt; will be explained in a future post.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="references"&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="http://kubernetes.io/docs/user-guide/kubectl/kubectl_logs/"&gt;http://kubernetes.io/docs/user-guide/kubectl/kubectl_logs/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/containerd/containerd/blob/main/docs/man/containerd-config.toml.5.md"&gt;containerd/containerd&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/kubernetes/release/blob/cd53840/cmd/krel/templates/latest/kubelet/kubelet.service"&gt;kubernetes/release&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://docs.docker.com/engine/admin/logging/overview/"&gt;https://docs.docker.com/engine/admin/logging/overview/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/kubernetes/design-proposals-archive/blob/main/node/kubelet-cri-logging.md"&gt;kubernetes/design-proposals-archive&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/kubernetes/kubernetes/issues/17183"&gt;kubernetes/kubernetes#17183&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/kubernetes/kubernetes/issues/24677"&gt;kubernetes/kubernetes#24677&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/kubernetes/kubernetes/issues/30709"&gt;kubernetes/kubernetes#30709&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/kubernetes/kubernetes/issues/31459"&gt;kubernetes/kubernetes#31459&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/kubernetes/kubernetes/pull/13010"&gt;kubernetes/kubernetes#13010&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/kubernetes/kubernetes/pull/33111"&gt;kubernetes/kubernetes#33111&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://kubernetes.io/docs/concepts/cluster-administration/logging/"&gt;https://kubernetes.io/docs/concepts/cluster-administration/logging/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Standard_streams"&gt;https://en.wikipedia.org/wiki/Standard_streams&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2025/2025-06-22-kubernetes-logs-simplified/"/>
    <summary>Blog Post Publish Date: 2025/06/22Have you ever wondered how $ kubectl logs retrieves logs, where they are stored, and how to access both current and past logs? Kubernetes logging can seem complex, but understanding it is key to troubleshooting and monitoring. This blog post outlines how Kubernetes organizes logs, how to send them to external systems, and tips to manage log growth effectively.</summary>
    <category term="fluentbit" label="fluentbit"/>
    <category term="logs" label="logs"/>
    <category term="observability" label="observability"/>
    <published>2025-06-22T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2025/2025-06-19-fluentbit-log-dedup/</id>
    <title>Fluent Bit: Generating Log IDs with SHA-256 Hashes for Log Deduplication</title>
    <updated>2025-06-19T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2025/06/19&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="fluent-bit-generating-log-ids-with-sha-256-hashes-for-log-deduplication"&gt;

&lt;p&gt;This post explores the root causes of log duplication, the necessity of generating unique log identifiers, and how to leverage Fluent Bit’s native capabilities to create SHA-256 hashes for effective deduplication without relying on external scripts or plugins.&lt;/p&gt;
&lt;section id="why-do-duplicate-logs-occur-in-fluent-bit"&gt;
&lt;h2&gt;Why Do Duplicate Logs Occur in Fluent Bit?&lt;/h2&gt;
&lt;p&gt;I encountered issues with duplicated logs being indexed in OpenSearch by Fluent Bit. After some research, I concluded that currently, Fluent Bit, when using the &lt;a class="reference external" href="https://docs.fluentbit.io/manual/pipeline/inputs/tail"&gt;Tail&lt;/a&gt; input, offers features designed for the &lt;strong&gt;At Least Once&lt;/strong&gt; delivery strategy. This means that a message may be delivered one or more times, potentially causing duplication. It does not support &lt;strong&gt;Exactly Once&lt;/strong&gt; or &lt;strong&gt;At Most Once&lt;/strong&gt; strategies, where a message is delivered only once or at most once, respectively.&lt;/p&gt;
&lt;p&gt;There are several variables that can contribute to this behavior. For example, when using the &lt;a class="reference external" href="https://docs.fluentbit.io/manual/pipeline/inputs/tail"&gt;Tail&lt;/a&gt; input plugin, even if you use an SQL database with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;DB&lt;/span&gt; &lt;span class="pre"&gt;On&lt;/span&gt;&lt;/code&gt; parameter, you may still experience duplicate logs. This is because the database only persists the file reading offset, not the delivery status for each configured &lt;a class="reference external" href="https://docs.fluentbit.io/manual/pipeline/outputs"&gt;Output&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I plan to write a separate blog post with more details about how Fluent Bit manages log delivery state (&lt;em&gt;chunk, buffering, SQL Lite, Storage Type&lt;/em&gt;). For now, it’s important to understand that in certain scenarios such as output unavailability and Fluent Bit restarts—duplication can occur. This is expected behavior and not necessarily a problem, especially when multiple &lt;a class="reference external" href="https://docs.fluentbit.io/manual/pipeline/outputs"&gt;Output&lt;/a&gt; configurations are used.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-importance-of-unique-log-ids"&gt;
&lt;h2&gt;The Importance of Unique Log IDs&lt;/h2&gt;
&lt;p&gt;If the delivery strategy is &lt;strong&gt;At Least Once&lt;/strong&gt;, generating a unique ID is a hard requirement to avoid duplication at the &lt;a class="reference external" href="https://docs.fluentbit.io/manual/pipeline/outputs"&gt;Output&lt;/a&gt; destination. This unique ID should be created based on the log line content. If you are using Logstash and OpenSearch in your log analytics stack, you can generate IDs using the &lt;a class="reference external" href="https://www.elastic.co/docs/reference/logstash/plugins/plugins-filters-fingerprint"&gt;Logstash Fingerprint filter plugin&lt;/a&gt; or the &lt;a class="reference external" href="https://docs.opensearch.org/docs/latest/ingest-pipelines/processors/fingerprint/"&gt;OpenSearch Fingerprint Ingest Pipeline Processor&lt;/a&gt;, both of which create hashes based on specified log field values. Unfortunately, Fluent Bit does not have a specific native filter plugin to create unique IDs. You can use a Lua script or bash script callback for it, but in both cases, the complexity increases and you become dependent on external crypto libraries. However, don’t worry, I will show you a workaround to generate a hash from the log line content using only Fluent Bit’s native features.&lt;/p&gt;
&lt;p&gt;If you are using Fluent Bit to send logs directly to OpenSearch, you can use the &lt;a class="reference external" href="https://docs.fluentbit.io/manual/pipeline/outputs/opensearch"&gt;OpenSearch Output Plugin&lt;/a&gt; with the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Generate_ID&lt;/span&gt; &lt;span class="pre"&gt;On&lt;/span&gt;&lt;/code&gt; parameter enabled to generate a unique ID for logs. However, &lt;strong&gt;note that the ID generated by this option will be different even if the one ore more log line content is the same&lt;/strong&gt;. The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Generate_ID&lt;/span&gt; &lt;span class="pre"&gt;On&lt;/span&gt;&lt;/code&gt; option generates an ID within Fluent Bit only; it does not create a unique ID based on the log content. To avoid duplicate logs, generate a hash-based ID derived from the log content itself.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="generating-sha-256-hashes-for-log-deduplication"&gt;
&lt;h2&gt;Generating SHA-256 Hashes for Log Deduplication&lt;/h2&gt;
&lt;p&gt;I reviewed the available Fluent Bit filter plugins and did not find any that generate hash values based on a log key. However, I discovered a &lt;a class="reference external" href="https://docs.fluentbit.io/manual/pipeline/processors/content-modifier"&gt;Processor Content Modifier Action&lt;/a&gt; called &lt;a class="reference external" href="https://docs.fluentbit.io/manual/pipeline/processors/content-modifier#hash-example"&gt;hash&lt;/a&gt;. This action takes a log event key as an argument and replaces its value with a SHA-256 hash. While this feature is primarily intended for masking sensitive data such as passwords and PII, it can also be used to generate unique identifiers for log events.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;key insight&lt;/strong&gt; is to copy the log line content to a new key and hash this new field, keeping the original log line content intact and using the hashed field as the log ID. Check example:&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;&lt;a class="reference external" href="https://github.com/c-neto/my-devops-labs/blob/main/blog/2025-06-19/fluent-bit.yaml"&gt;/fluent-bit/fluent-bit.yaml&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# The `pipeline.inputs.processors` feature is only available when using the `yaml` configuration format.  &lt;/span&gt;
&lt;span class="c1"&gt;# It is not supported in the legacy `.conf` configuration format.&lt;/span&gt;

&lt;span class="nt"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;dummy&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;dummy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="no"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;&amp;quot;message&amp;quot;: &amp;quot;2025-06-19T17:02:35.123456789Z stdout F This is a Foobar application log sample running in kubernetes persisted on /var/log/containers/*.log&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="no"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;dummy.foobar&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;processors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# Copy the &amp;#39;message&amp;#39; field to a new field called &amp;#39;_id&amp;#39;.&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;modify&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;message _id&lt;/span&gt;

&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# Hash the &amp;#39;_id&amp;#39; field to anonymize or deduplicate.&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;content_modifier&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;hash&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;_id&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;dummy&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;dummy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="no"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;&amp;quot;message&amp;quot;: &amp;quot;2025-06-19T17:02:35.123456789Z stdout F This is a Greeting application log sample running in kubernetes persisted on /var/log/containers/*.log&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="no"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;dummy.greeting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;processors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# Copy the &amp;#39;message&amp;#39; field to a new field called &amp;#39;_id&amp;#39;.&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;modify&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;message _id&lt;/span&gt;

&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# Hash the &amp;#39;_id&amp;#39; field to anonymize or deduplicate.&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;content_modifier&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;hash&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;_id&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;stdout&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;json_lines&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dummy.*&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The output is like:&lt;/p&gt;
&lt;div class="highlight-json notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;date&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1750366003.411804&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2025-06-19T17:02:35.123456789Z stdout F This is a Foobar application log sample running in kubernetes persisted on /var/log/containers/*.log&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;19a93f55808eb5478c65813c028045f1b354abe12790eb8aee0dd825697aa93e&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;date&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1750366003.415653&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2025-06-19T17:02:35.123456789Z stdout F This is a Greeting application log sample running in kubernetes persisted on /var/log/containers/*.log&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;148917c2efe0114da7f7cef6327bde63f5c9ec5cac5cf05d4a73acefaa69a55c&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;date&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1750366004.412541&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2025-06-19T17:02:35.123456789Z stdout F This is a Foobar application log sample running in kubernetes persisted on /var/log/containers/*.log&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;19a93f55808eb5478c65813c028045f1b354abe12790eb8aee0dd825697aa93e&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;date&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1750366004.413101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2025-06-19T17:02:35.123456789Z stdout F This is a Greeting application log sample running in kubernetes persisted on /var/log/containers/*.log&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;148917c2efe0114da7f7cef6327bde63f5c9ec5cac5cf05d4a73acefaa69a55c&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://docs.fluentbit.io/manual/pipeline/processors/content-modifier#hash-example"&gt;hash&lt;/a&gt; action takes the binary value of the log line, applies the SHA-256 algorithm, and outputs the result as a hexadecimal string. Below is a Python script that performs the same hashing process as Fluent Bit.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;hashlib&lt;/span&gt;

&lt;span class="n"&gt;log_line_in_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;2025-06-19T17:02:35.123456789Z stdout F This is a Foobar application log sample running in kubernetes persisted on /var/log/containers/*.log&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;log_line_in_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log_line_in_string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;log_line_in_sha256_hexa_decimal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_line_in_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_line_in_sha256_hexa_decimal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;19a93f55808eb5478c65813c028045f1b354abe12790eb8aee0dd825697aa93e&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, you can use the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;_id&lt;/span&gt;&lt;/code&gt; as log unique ID field!&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;You can find a complete, working example of this configuration in my Docker Compose lab repository: &lt;i class="fab fa-github"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/c-neto/my-devops-labs/blob/main/blog/2025-06-19/"&gt;github.com/c-neto/my-devops-labs/blog/2025-06-19/docker-compose.yml&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="references"&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Docker-Compose Lab: &lt;a class="github reference external" href="https://github.com/c-neto/my-devops-labs/blob/main/blog/2025-06-19/docker-compose.yml"&gt;c-neto/my-devops-labs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Logstash Fingerprint Filter Plugin: &lt;a class="reference external" href="https://www.elastic.co/docs/reference/logstash/plugins/plugins-filters-fingerprint"&gt;https://www.elastic.co/docs/reference/logstash/plugins/plugins-filters-fingerprint&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;OpenSearch Fingerprint Ingest Pipeline Processor: &lt;a class="reference external" href="https://docs.opensearch.org/docs/latest/ingest-pipelines/processors/fingerprint/"&gt;https://docs.opensearch.org/docs/latest/ingest-pipelines/processors/fingerprint/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fluent Bit Processors Reference: &lt;a class="reference external" href="https://docs.fluentbit.io/manual/pipeline/processors/content-modifier"&gt;https://docs.fluentbit.io/manual/pipeline/processors/content-modifier&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fluent Bit Hash Example: &lt;a class="reference external" href="https://docs.fluentbit.io/manual/pipeline/processors/content-modifier#hash-example"&gt;https://docs.fluentbit.io/manual/pipeline/processors/content-modifier#hash-example&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2025/2025-06-19-fluentbit-log-dedup/"/>
    <summary>Blog Post Publish Date: 2025/06/19This post explores the root causes of log duplication, the necessity of generating unique log identifiers, and how to leverage Fluent Bit’s native capabilities to create SHA-256 hashes for effective deduplication without relying on external scripts or plugins.</summary>
    <category term="fluentbit" label="fluentbit"/>
    <category term="logs" label="logs"/>
    <category term="observability" label="observability"/>
    <published>2025-06-19T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2025/2025-02-25-helm-best-practices/</id>
    <title>Helm Charts: Development Practices from a Programmer’s Perspective</title>
    <updated>2025-02-25T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2025/02/25&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="helm-charts-development-practices-from-a-programmers-perspective"&gt;

&lt;p&gt;In the last quarter of 2024, I delved deep into the world of Helm Charts and encountered several challenges. Initially, creating a new Helm Chart seemed to introduce complexity, and I didn’t see significant advantages over using Kustomize. However, after spending days reflecting, reading more about Helm, and examining existing projects, I gained insights that helped me view Helm as an ally rather than an unnecessarily complex tool. So, I compiled the main insights in this blog post to present Helm chart development tips from a programmer’s perspective.&lt;/p&gt;
&lt;section id="why-helm-charts-are-a-powerful-tool-for-iac"&gt;
&lt;h2&gt;Why Helm Charts Are a Powerful Tool for IaC&lt;/h2&gt;
&lt;p&gt;Before discussing best practices for Helm chart development, it is important to understand why Helm Charts are a powerful tool for Infrastructure as Code (IaC). Helm Charts enable the adoption of IaC by providing a robust templating engine for rendering Kubernetes manifests (powered by Go Templates). They allow the creation of reusable components, abstracting Kubernetes workloads, and offering a high-level parameterization interface.&lt;/p&gt;
&lt;p&gt;One of Helm’s key advantages is its ability to distribute charts as artifacts, similar to programming language packages or system distributions like RPM and DEB. This allows the creation of new charts that use existing ones as dependencies, which increases agility in developing new solutions based on proven components.&lt;/p&gt;
&lt;p&gt;Helm simplifies Kubernetes workload management, streamlining configuration, deployment, and updates. It also supports storing charts as OCI artifacts in registries like AWS Elastic Container Registry (ECR) or as tarball files, ensuring reproducible and seamless deployments. CI/CD tools like Spinnaker and ArgoCD natively support Helm for deployment automation.&lt;/p&gt;
&lt;p&gt;Helm Charts are particularly effective for creating reusable, deployable infrastructure components, especially when managing multiple clusters with environment-specific configurations. This approach reduces manifest duplication in Git repositories, improving maintainability and operational efficiency.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="simple-made-easy-is-helm-chart-overengineering"&gt;
&lt;h2&gt;Simple Made Easy - Is Helm Chart Overengineering?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;“&lt;em&gt;Complexity has to pay for itself. It has to buy you something.&lt;/em&gt;”&lt;br /&gt;
— Simple Made Easy (Rich Hickey, 2011)&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;When I first started working with Helm Charts, my initial thought was: “&lt;em&gt;Oh my God, this is so complex, am I just introducing unnecessary complexity?&lt;/em&gt;” However, like any new tool, Helm has a learning curve that takes time to overcome. The key to embracing Helm lies in understanding the problem it solves. If you think Helm is complex, try maintaining multiple nearly identical Kubernetes manifests, where the only differences are a few parameters per cluster it’s a recipe for chaos.&lt;/p&gt;
&lt;p&gt;In reality, Helm Charts are overengineering only if you’re solving a problem you don’t have. The complexity introduced by Helm justifies itself when it eliminates a bigger problem such as duplicated code across multiple environments. If you’re managing workloads across several clusters, the complexity of Helm is not an overhead but a necessary trade-off to improve maintainability.&lt;/p&gt;
&lt;p&gt;I’ll explore this concept further in the next section.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dry-eliminating-redundancy-in-kubernetes-manifests-with-helm"&gt;
&lt;h2&gt;DRY - Eliminating Redundancy in Kubernetes Manifests with Helm&lt;/h2&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;“&lt;em&gt;DRY (Don’t Repeat Yourself) is about the duplication of knowledge, of intent. It’s about expressing the same thing in two different places, possibly in two totally different ways.&lt;/em&gt;”&lt;br /&gt;
— The Pragmatic Programmer (Andrew Hunt &amp;amp; David Thomas, 1999)&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;To illustrate the DRY (Don’t Repeat Yourself) principle, let’s consider a common scenario in IT corporations: maintaining isolated environments for different stages of development. Typically, companies have a development environment used internally for testing, a staging environment for validation, and a production environment serving real users.&lt;/p&gt;
&lt;p&gt;If you work in this type of setup, you likely manage workloads across multiple Kubernetes clusters, running the same applications with slight variations, such as Ingress settings, resource allocations, and environment-specific variables. A straightforward approach to handling these differences is to create separate Kubernetes manifest files for each environment, maintaining the same structure but modifying specific values. While this method works, it introduces a significant drawback: &lt;strong&gt;code duplication&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When managing duplicated manifests, you increase the risk of errors and inconsistencies. This approach may seem manageable with one or two environments, but as the number of environments grows (e.g., 3 or more), it quickly becomes unscalable and difficult to maintain. For instance, applying a patch to one environment requires updating several separate files, increasing operational overhead.&lt;/p&gt;
&lt;p&gt;This is where Helm Charts excel. Instead of maintaining multiple duplicate manifests, Helm enables template-driven configuration. The base structure of your Kubernetes manifests resides within the Helm Chart, while environment-specific values are dynamically injected through Go Templates.&lt;/p&gt;
&lt;p&gt;Helm achieves this through the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Values.yaml&lt;/span&gt;&lt;/code&gt; file, where each environment has its own configuration file defining runtime parameters. Instead of maintaining duplicated YAML files, you have a single chart that dynamically renders manifests based on input values. This approach ensures consistency, maintainability, and scalability.&lt;/p&gt;
&lt;p&gt;As you can see, templatizing Kubernetes manifests is another way Helm follows the DRY principle. By reducing redundancy, Helm minimizes errors and simplifies the management of multiple environments, making Kubernetes deployments more efficient and maintainable.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="yagni-avoid-overengineering-in-helm-templates"&gt;
&lt;h2&gt;YAGNI - Avoid Overengineering in Helm Templates&lt;/h2&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;em&gt;“Always implement things when you actually need them, never when you just foresee that you will need them.&lt;/em&gt;”&lt;br /&gt;
— Ron Jeffries about XP principle: &lt;em&gt;“You ain’t gonna need it (YAGNI).”&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Templates should only be created when they represent a well-defined and reproducible structure. A common mistake when designing Helm Charts is over-templatization, introducing excessive parameterization that makes templates overly flexible but harder to maintain.&lt;/p&gt;
&lt;p&gt;If you find yourself defining numerous template functions to handle many variations in your &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Values.yaml&lt;/span&gt;&lt;/code&gt;, it’s likely a sign that your template lacks a clear structure. Instead of simplifying deployment, you’re introducing unnecessary complexity that can lead to confusion, harder debugging, and increased maintenance effort.&lt;/p&gt;
&lt;p&gt;Even worse than not having a template is maintaining template functions that will never be used. Every extra function, conditional statement, or parameter adds cognitive overhead. When a team inherits a Helm Chart filled with unused or rarely used logic, they are forced to understand and maintain complexity that provides no real value.&lt;/p&gt;
&lt;p&gt;This follows the YAGNI (You Ain’t Gonna Need It) principle, a fundamental concept in software development that warns against building features or abstractions that are not immediately necessary. Instead of preparing for hypothetical future scenarios, focus on solving real, present problems efficiently.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="library-charts-ensuring-reusable-patterns"&gt;
&lt;h2&gt;Library Charts - Ensuring Reusable Patterns&lt;/h2&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;em&gt;“The design of reusable libraries should provide clear and simple interfaces, allowing users to interact with them without needing to understand their internal details.”&lt;/em&gt;
— Design Patterns: Elements of Reusable Object-Oriented Software (&lt;em&gt;paraphrased from the sections on encapsulation and reusability&lt;/em&gt;)&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;There are two types of Helm Charts: &lt;strong&gt;Application&lt;/strong&gt; and &lt;strong&gt;Library&lt;/strong&gt;. Application charts are complete solutions, specifically designed for deployment. They define all the necessary Kubernetes resources required to deploy an application. On the other hand, &lt;strong&gt;Library&lt;/strong&gt; charts contain reusable resources such as templates and functions, which are shared across other charts but are not directly deployed. These library charts act as an abstraction layer, providing common components that can be reused in multiple contexts.&lt;/p&gt;
&lt;p&gt;A prime example of a well-designed library chart is the Bitnami Common Chart, which consists of reusable functions and patterns that ensure consistency across all Bitnami charts. These patterns are adopted across the entire Bitnami Charts repository, making it easier to maintain and extend their Kubernetes applications. You can find more about the Bitnami Common Library Chart here, and an example of how the common chart is used in practice within Bitnami Fluentbit can be seen here:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/bitnami/charts/tree/main/bitnami/common"&gt;Bitnami Common Library Chart&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/bitnami/charts/blob/main/bitnami/fluent-bit/templates/configmap.yaml#L11"&gt;Bitnami Fluentbit Example of Common Chart Usage&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Incorporating best practices from programming concepts such as DRY, YAGNI, and modular design can significantly improve Helm chart development. By focusing on maintainability, scalability, and reducing unnecessary complexity, you can leverage Helm to create more efficient, reusable Kubernetes components. Helm is not just a tool for templating Kubernetes manifests; it’s a framework for thinking about infrastructure in a modular, reusable way—something that every developer can appreciate.&lt;/p&gt;
&lt;p&gt;These practices allow teams to scale their Kubernetes deployments more efficiently while minimizing overhead and ensuring that the system is easier to maintain in the long run.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="references"&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Anti-pattern"&gt;Anti-patterns - Wikipedia&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://helm.sh/docs/chart_best_practices/"&gt;Helm Chart Best Practices&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://martinfowler.com/bliki/Yagni.html"&gt;Martin Fowler on YAGNI&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://12factor.net/"&gt;The 12-Factor App&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;Don’t Repeat Yourself (DRY) - Wikipedia&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/bitnami/charts/tree/main/bitnami/common"&gt;Bitnami Common Library Chart&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/bitnami/charts/blob/main/bitnami/fluent-bit/templates/configmap.yaml#L11"&gt;Bitnami Fluentbit Example of Common Chart Usage&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2025/2025-02-25-helm-best-practices/"/>
    <summary>Blog Post Publish Date: 2025/02/25In the last quarter of 2024, I delved deep into the world of Helm Charts and encountered several challenges. Initially, creating a new Helm Chart seemed to introduce complexity, and I didn’t see significant advantages over using Kustomize. However, after spending days reflecting, reading more about Helm, and examining existing projects, I gained insights that helped me view Helm as an ally rather than an unnecessarily complex tool. So, I compiled the main insights in this blog post to present Helm chart development tips from a programmer’s perspective.</summary>
    <category term="helm" label="helm"/>
    <published>2025-02-25T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2025/2025-02-16-logstash-db-enrichment/</id>
    <title>Logstash DB Enrichment - Tips and Traps!</title>
    <updated>2025-02-16T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2025/02/16&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="logstash-db-enrichment-tips-and-traps"&gt;

&lt;p&gt;In this blog post, I share my experiences with Logstash log enrichment using a database. I’ll cover some hidden behaviors and the importance of configuring the JDBC connection string parameters to avoid mysterious problems that are hard to replicate and not clearly documented. These tips come from real-world scenarios and aren’t always obvious in the official documentation. I hope they help you save hours of troubleshooting and searching through forums.&lt;/p&gt;
&lt;p&gt;The examples assume you’re running &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/logstash-8-17-2.html"&gt;Logstash OSS 8.17.2&lt;/a&gt; in a Kubernetes cluster with a PostgreSQL database. However, the key considerations apply even if you’re using a single instance or a different relational database. I’ve also created a docker-compose lab for you to explore and test all the tips discussed in this blog post: &lt;i class="fab fa-github fa-fade"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/c-neto/my-devops-labs/tree/main/logstash/db-enrichment"&gt;github.com/c-neto/my-devops-labs/logstash/db-enrichment&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I reserve special thanks to PostgreSQL Database Specialist &lt;a class="reference external" href="https://www.linkedin.com/in/lucio-chiessi"&gt;Lucio Chiessi&lt;/a&gt; for helping me explore the JDBC connection string options that can be beneficial for Logstash.&lt;/p&gt;
&lt;section id="use-prepared-statements"&gt;
&lt;h2&gt;Use Prepared Statements&lt;/h2&gt;
&lt;p&gt;The first tip is to use the &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/8.17/plugins-filters-jdbc_streaming.html#plugins-filters-jdbc_streaming-use_prepared_statements"&gt;use_prepared_statements&lt;/a&gt; option in the &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/8.17/plugins-filters-jdbc_streaming.html"&gt;jdbc_streaming&lt;/a&gt; filter plugin. A Prepared Statement in PostgreSQL is a pre-compiled SQL query that improves performance and security by allowing repeated execution with different parameters while preventing SQL injection. When a query is prepared, PostgreSQL analyzes and optimizes the execution plan once. In subsequent calls, only the parameters are replaced, avoiding recompilation and improving efficiency. Normally, the query in the log ingestion pipeline has a defined structure, changing only the parameters, and it is frequently executed. It is an ideal use case for log enrichment.&lt;/p&gt;
&lt;p&gt;Example of &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/8.17/plugins-filters-jdbc_streaming.html#plugins-filters-jdbc_streaming-use_prepared_statements"&gt;use_prepared_statements&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight-lua notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;jdbc_streaming&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;jdbc_driver_library&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/usr/share/logstash/postgresql.jar&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;jdbc_driver_class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;org.postgresql.Driver&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;jdbc_connection_string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${DB_JDBC_CONNECTION_STRING}&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;jdbc_user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${DB_USER}&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;jdbc_password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${DB_PASSWORD}&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;use_prepared_statements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;prepared_statement_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;logstash_enrich_query&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;prepared_statement_bind_values&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[document][user_id]&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;statement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;  SELECT&lt;/span&gt;
&lt;span class="s2"&gt;    user_name,&lt;/span&gt;
&lt;span class="s2"&gt;    user_email,&lt;/span&gt;
&lt;span class="s2"&gt;    user_group&lt;/span&gt;
&lt;span class="s2"&gt;  FROM table_users&lt;/span&gt;
&lt;span class="s2"&gt;  WHERE id = ?&lt;/span&gt;
&lt;span class="s2"&gt;  &amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sql&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="when-the-database-is-out-your-pipeline-won-t-start"&gt;
&lt;h2&gt;When the Database is Out, Your Pipeline Won’t Start!&lt;/h2&gt;
&lt;p&gt;When you configure a Logstash pipeline that uses log enrichment by &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/8.17/plugins-filters-jdbc_streaming.html"&gt;jdbc_streaming&lt;/a&gt;, Logstash verifies the connection with the database &lt;strong&gt;before starting&lt;/strong&gt; to receive the logs. If the database is out, the pipeline will not be registered, and log ingestion will be removed. Pay attention, Logstash verifies the database connection before starting to receive logs, not when receiving logs that will use the &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/8.17/plugins-filters-jdbc_streaming.html"&gt;jdbc_streaming&lt;/a&gt;. In other words, if your database is out, the log ingestion pipeline that has &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/8.17/plugins-filters-jdbc_streaming.html"&gt;jdbc_streaming&lt;/a&gt; will not start, even if all logs don’t satisfy &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;if&lt;/span&gt;&lt;/code&gt; conditions to be enriched.&lt;/p&gt;
&lt;p&gt;However, there is one detail that causes confusion in the behavior mentioned above: If the database is running when the Logstash pipeline starts, and the database connection is broken, the pipeline will continue working with log enrichment errors only. When the database connection is live again, the enrichment will be re-established and work.&lt;/p&gt;
&lt;p&gt;It is important to understand because when you configure a database log enrichment, indirectly, you are configuring a hard dependency between Logstash new instances and database health.&lt;/p&gt;
&lt;p&gt;It is a problem because if the database is out, it invalidates scaling the Logstash pods, and if Logstash restarts, the log ingestion will be blocked because of the hard dependency on the database.&lt;/p&gt;
&lt;p&gt;Ok, but… How to fix this behavior? I didn’t find any parameter in the Logstash documentation to prevent this behavior… In this case, you can organize distinct pipelines in a way that one pipeline is only for logs that will always be enriched, and another pipeline for logs that will not be enriched. This approach can avoid stopping log ingestion of logs that don’t need to be enriched when the database is out. But, this approach can increase the duplicate code. Thus, my suggestion is to use templates of your log pipeline to have a fallback pipeline without log enrichment to avoid rendering the &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/8.17/plugins-filters-jdbc_streaming.html"&gt;jdbc_streaming&lt;/a&gt; statements. If you are working in Kubernetes, I suggest templating the pipeline using &lt;a class="reference external" href="https://helm.sh/"&gt;Helm&lt;/a&gt;; if not, you can use another template engine like &lt;a class="reference external" href="https://jinja.palletsprojects.com/en/stable/"&gt;Jinja&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="lock-table-lock-log-ingestion"&gt;
&lt;h2&gt;Lock Table == Lock Log Ingestion&lt;/h2&gt;
&lt;p&gt;This is the worst problem that can occur: when issues arise, and service logs are frozen without an explicit indication of the root cause.&lt;/p&gt;
&lt;p&gt;When a log is performing the &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/8.17/plugins-filters-jdbc_streaming.html"&gt;jdbc_streaming&lt;/a&gt; filter plugin, and at that moment, the table defined in the query is locked, all logs will be stopped, even logs that will not perform &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/8.17/plugins-filters-jdbc_streaming.html"&gt;jdbc_streaming&lt;/a&gt;. The worst thing is that Logstash service logs don’t generate logs to explicitly indicate this behavior (&lt;em&gt;at least in the default log level&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;I explored the documentation of the &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/plugins-filters-jdbc_streaming.html"&gt;jdbc_streaming&lt;/a&gt; to check parameters that can help avoid this behavior, and I didn’t find any. At this moment, I realized that I needed to explore the PostgreSQL JDBC connection string. I explored &lt;a class="reference external" href="https://jdbc.postgresql.org/"&gt;https://jdbc.postgresql.org/&lt;/a&gt; and found important things that explain the behavior. By default, if a query execution is on a table that is locked, the timeout for query execution is infinite!&lt;/p&gt;
&lt;p&gt;To solve this problem, it is necessary to explicitly set the parameter &lt;a class="reference external" href="https://www.postgresql.org/docs/current/runtime-config-client.html"&gt;options=-c lock_timeout=1000&lt;/a&gt; in the JDBC connection string to set the maximum time in milliseconds to wait for a lock on a table. If the lock cannot be acquired within 1 second, an error is thrown in Logstash service explicitly indicating that the log was not enriched due to lock timeout, and the log pipeline will continue, and the log will proceed to the next statement.&lt;/p&gt;
&lt;p&gt;To simulate this case, you can execute the following command in PostgreSQL:&lt;/p&gt;
&lt;div class="highlight-sql notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;LOCK&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;table_used_by_logstash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ACCESS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXCLUSIVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;MODE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;At this moment, I paid attention to reviewing all JDBC connection parameters with infinite values by default that could cause problems like the one mentioned, and I will present them in the following topics in this blog post.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="slow-query-slow-log-ingestion"&gt;
&lt;h2&gt;Slow Query == Slow Log Ingestion&lt;/h2&gt;
&lt;p&gt;Another JDBC connection parameter that defaults to infinite is the query execution timeout. If the database is overloaded and facing throttling, your log ingestion will be affected. If an enrichment query execution takes a long time, all logs in this pipeline will be queued, similar to the behavior explained in the previous topic. The default connection parameter in PostgreSQL for query execution timeout is infinite, thus, database overload will impact log ingestion.&lt;/p&gt;
&lt;p&gt;To solve this case, you can explicitly set the JDBC connection parameter &lt;a class="reference external" href="https://www.postgresql.org/docs/current/runtime-config-client.html"&gt;options=-c statement_timeout=5000&lt;/a&gt; to set the maximum time in milliseconds that a query can run before being terminated. In this case, queries running longer than 5 seconds will be aborted, and it will be explicitly logged in Logstash service logs, the pipeline will continue working, and log enrichment will be skipped, adding the tag.&lt;/p&gt;
&lt;p&gt;To simulate a slow query, you can add the function &lt;a class="reference external" href="https://pgpedia.info/p/pg_sleep.html"&gt;pg_sleep&lt;/a&gt;. In the example below, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pg_sleep(10)::text&lt;/span&gt;&lt;/code&gt; is introduced to artificially simulate a slow query:&lt;/p&gt;
&lt;div class="highlight-lua notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;jdbc_streaming&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;jdbc_driver_library&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/usr/share/logstash/postgresql.jar&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;jdbc_driver_class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;org.postgresql.Driver&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;jdbc_connection_string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${DB_JDBC_CONNECTION_STRING}&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;jdbc_user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${DB_USER}&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;jdbc_password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${DB_PASSWORD}&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;use_prepared_statements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;prepared_statement_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;logstash_enrich_query&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;prepared_statement_bind_values&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[document][user_id]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;statement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;    SELECT&lt;/span&gt;
&lt;span class="s2"&gt;      pg_sleep(10)::text,    &lt;/span&gt;
&lt;span class="s2"&gt;      user_name,&lt;/span&gt;
&lt;span class="s2"&gt;      user_email,&lt;/span&gt;
&lt;span class="s2"&gt;      user_group&lt;/span&gt;
&lt;span class="s2"&gt;    FROM table_users&lt;/span&gt;
&lt;span class="s2"&gt;    WHERE id = ?&lt;/span&gt;
&lt;span class="s2"&gt;  &amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sql&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="define-timeouts-for-database-login-and-socket"&gt;
&lt;h2&gt;Define Timeouts for Database Login and Socket&lt;/h2&gt;
&lt;p&gt;In the same context as the previously presented timeout parameters, it is important to define &lt;a class="reference external" href="https://jdbc.postgresql.org/documentation/use/#connection-parameters"&gt;loginTimeout=10&lt;/a&gt; to specify the maximum time in seconds to wait for a connection to be established. If the connection cannot be established within this time, an error is thrown, and &lt;a class="reference external" href="https://jdbc.postgresql.org/documentation/use/#connection-parameters"&gt;socketTimeout=10&lt;/a&gt; to set the maximum time in seconds for reading data from the database. If the database does not respond within this time, the connection is closed.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="ensure-db-connection-identification"&gt;
&lt;h2&gt;Ensure DB Connection Identification&lt;/h2&gt;
&lt;p&gt;The Logstash enrichment queries can be offensive to the database. In this case, it is necessary to tag the connections to make it easier to identify in the aspect of database administration. To define the connection identification, use the JDBC Connection String parameter &lt;a class="reference external" href="https://jdbc.postgresql.org/documentation/use/#connection-parameters"&gt;ApplicationName=logstash&lt;/a&gt; to set the application name to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;logstash&lt;/span&gt;&lt;/code&gt; for easier identification in PostgreSQL logs.&lt;/p&gt;
&lt;p&gt;Thus, you can identify the connection from Logstash by the following PostgreSQL query:&lt;/p&gt;
&lt;div class="highlight-sql notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;application_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;usename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client_addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;state&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_stat_activity&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;application_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;logstash&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="sequel-opts-database-connection-parameters"&gt;
&lt;h2&gt;Sequel Opts != Database Connection Parameters&lt;/h2&gt;
&lt;p&gt;Don’t be confused, the &lt;a class="reference external" href="https://github.com/jeremyevans/sequel/blob/master/doc/opening_databases.rdoc#label-postgres"&gt;jdbc_streaming::sequel_opts&lt;/a&gt; parameters and &lt;a class="reference external" href="https://jdbc.postgresql.org/documentation/use/"&gt;JDBC Connection String&lt;/a&gt; performing distinct roles. Logstash connects to databases using &lt;strong&gt;JDBC&lt;/strong&gt;, but it internally manages the connection through the &lt;strong&gt;Sequel&lt;/strong&gt; library in JRuby, allowing for additional configuration via &lt;a class="reference external" href="https://github.com/jeremyevans/sequel/blob/master/doc/opening_databases.rdoc#label-postgres"&gt;sequel_opts&lt;/a&gt;. Thus, the JDBC connection string defines behavior with the database, and Sequel defines connection characteristics to manage this connection.&lt;/p&gt;
&lt;p&gt;The example below is ideal to explain it. The &lt;a class="reference external" href="https://github.com/jeremyevans/sequel/blob/master/doc/opening_databases.rdoc#label-postgres"&gt;sequel_opts::pool_timeout&lt;/a&gt; defines a timeout for Logstash to wait for an available pool connection to use, which is not related to the timeout of query execution, which is the parameter related to the database, defined in the &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/8.17/plugins-filters-jdbc_streaming.html#plugins-filters-jdbc_streaming-jdbc_connection_string"&gt;jdbc_connection_string&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight-lua notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;jdbc_streaming&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;sequel_opts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;max_connections&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;pool_timeout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;jdbc_connection_string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;jdbc:postgresql://postgres:5432/db?options=-c%20statement_timeout=1000%20&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When working with Logstash, I initially assumed that its behaviors were straightforward and default. However, Logstash has many nuances that can impact log ingestion and even halt it.&lt;/p&gt;
&lt;p&gt;As requirements grow, so do the challenges. Nothing should be taken for granted, and everything needs to be validated. I highly recommend setting up a docker-compose lab to test features in various contexts. This ensures reliability, helps plan workarounds, and prevents unexpected issues, especially late on a Friday night.&lt;/p&gt;
&lt;p&gt;Don’t forget to check out the docker-compose lab: &lt;i class="fab fa-github fa-fade"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/c-neto/my-devops-labs/tree/main/logstash/db-enrichment"&gt;github.com/c-neto/my-devops-labs/logstash/db-enrichment&lt;/a&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="references"&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/c-neto/my-devops-labs/tree/main/logstash/db-enrichment"&gt;c-neto/my-devops-labs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/jeremyevans/sequel/blob/master/doc/opening_databases.rdoc#label-postgres"&gt;jeremyevans/sequel&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://jdbc.postgresql.org/documentation/use/"&gt;https://jdbc.postgresql.org/documentation/use/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/plugins-filters-jdbc_streaming.html"&gt;https://www.elastic.co/guide/en/logstash/current/plugins-filters-jdbc_streaming.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.postgresql.org/docs/current/runtime-config-client.html"&gt;https://www.postgresql.org/docs/current/runtime-config-client.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2025/2025-02-16-logstash-db-enrichment/"/>
    <summary>Blog Post Publish Date: 2025/02/16In this blog post, I share my experiences with Logstash log enrichment using a database. I’ll cover some hidden behaviors and the importance of configuring the JDBC connection string parameters to avoid mysterious problems that are hard to replicate and not clearly documented. These tips come from real-world scenarios and aren’t always obvious in the official documentation. I hope they help you save hours of troubleshooting and searching through forums.</summary>
    <category term="logstash" label="logstash"/>
    <published>2025-02-16T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2025/2025-01-05-opensearch-concepts-index-and-shards/</id>
    <title>OpenSearch Concepts - Index and Shards</title>
    <updated>2025-01-05T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2025/01/05&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="opensearch-concepts-index-and-shards"&gt;

&lt;p&gt;OpenSearch is a powerful platform, but having a solid understanding of its basic concepts is essential for a seamless experience with the stack. In this blog post, we’ll cover one of the most fundamental aspects of OpenSearch administration: &lt;strong&gt;Indices and Shards&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This blog post provides a high-level explanation of these concepts and includes useful commands for retrieving information about indices and shards helpful during troubleshooting.&lt;/p&gt;
&lt;section id="understanding-indices-and-shards"&gt;
&lt;h2&gt;Understanding Indices and Shards&lt;/h2&gt;
&lt;p&gt;An &lt;strong&gt;index&lt;/strong&gt; is a logical namespace that organizes a collection of documents along with their associated characteristics, such as field types, life cycle policies, and storage layer state. These documents are stored on disk within logical partitions called &lt;strong&gt;shards&lt;/strong&gt;. Shards can be classified as &lt;strong&gt;primary&lt;/strong&gt; or &lt;strong&gt;replica&lt;/strong&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Primary shards&lt;/strong&gt; handle write operations and can also serve read requests when needed;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Replica shards&lt;/strong&gt; are exact copies of primary shards, designed to ensure fault tolerance and improve query performance by handling read operations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An index can consist of one or more shards distributed across multiple nodes. This distributed nature enables OpenSearch to form clusters, allowing indices to span across different machines for improved scalability and reliability.&lt;/p&gt;
&lt;p&gt;Increasing the number of primary shards improves throughput and indexing performance by distributing data across multiple partitions. While increasing the number of replica shards enhances fault tolerance and boosts query performance by providing additional nodes to handle read operations.&lt;/p&gt;
&lt;p&gt;The number of primary shards can only be defined during the creation of an index. Carefully consider the number of shards needed to meet your current requirements, factoring in query demands and, most importantly, write performance. Replica shards, however, can be adjusted on demand.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;A primary shard and its corresponding replica cannot reside on the same node within a cluster, ensuring fault tolerance in case of node failure.&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="useful-commands-for-managing-indices-and-shards-using-the-opensearch-api"&gt;
&lt;h2&gt;Useful Commands for Managing Indices and Shards Using the OpenSearch API&lt;/h2&gt;
&lt;p&gt;The following commands are especially useful for troubleshooting and monitoring the status of indices and shards. They can be executed in &lt;em&gt;OpenSearch Dashboards » Dev Tools&lt;/em&gt; or via an HTTP client like &lt;em&gt;cURL&lt;/em&gt; or &lt;em&gt;Postman&lt;/em&gt;.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;List all indices, sorted by name:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GET&lt;span class="w"&gt; &lt;/span&gt;_cat/indices?v&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;index
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;List the shards composing a specific index:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GET&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;INDEX-NAME&amp;gt;/_search_shards
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Get index metadata (e.g., number of shards, replicas, creation time):&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GET&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;INDEX-NAME&amp;gt;/_settings
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Retrieve field types defined in the index mapping:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GET&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;INDEX-NAME&amp;gt;/_mapping
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;List the ISM Policy attached to an index during creation:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GET&lt;span class="w"&gt; &lt;/span&gt;_plugins/ism/explain/&amp;lt;INDEX-NAME&amp;gt;?show_policy&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;List shards not currently allocated to any node (status: &lt;em&gt;UNASSIGNED&lt;/em&gt;):&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GET&lt;span class="w"&gt; &lt;/span&gt;_cat/shards?v&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;h&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;index,shard,prirep,state,unassigned.reason&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;state
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Check shard allocation across cluster nodes:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GET&lt;span class="w"&gt; &lt;/span&gt;_cat/allocation?v
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Monitor shard recovery or relocation progress:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GET&lt;span class="w"&gt; &lt;/span&gt;_cat/recovery/&amp;lt;INDEX-NAME&amp;gt;?v&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stage:desc
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;List all shards, sorted by index:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GET&lt;span class="w"&gt; &lt;/span&gt;_cat/shards?v&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;index
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;List indices, sorted by size:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GET&lt;span class="w"&gt; &lt;/span&gt;_cat/indices?v&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;h&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;index,store.size&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;store.size:desc
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With these commands you can efficiently get useful information about your OpenSearch clusters and streamline troubleshooting processes. If you have any questions or need further insights into OpenSearch, feel free to leave a comment below!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2025/2025-01-05-opensearch-concepts-index-and-shards/"/>
    <summary>Blog Post Publish Date: 2025/01/05OpenSearch is a powerful platform, but having a solid understanding of its basic concepts is essential for a seamless experience with the stack. In this blog post, we’ll cover one of the most fundamental aspects of OpenSearch administration: Indices and Shards.</summary>
    <category term="opensearch" label="opensearch"/>
    <published>2025-01-05T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2024/2024-07-21-dynaconf/</id>
    <title>Dynaconf: The Python Config Silver Bullet!</title>
    <updated>2024-07-21T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2024/07/21&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="dynaconf-the-python-config-silver-bullet"&gt;

&lt;p&gt;This blog post outlines a brief overview of best practices for application configuration, explains the main features of Dynaconf, and provides a hands-on example of how to extend it to retrieve parameters from AWS Parameter Store.&lt;/p&gt;
&lt;section id="twelve-factor-app-good-practices-in-application-config"&gt;
&lt;h2&gt;Twelve-Factor App: Good Practices in Application Config&lt;/h2&gt;
&lt;p&gt;Before starting to talk about Dynaconf or any code implementation for configuration, it’s necessary to understand the best practices used behind the scenes in systems with portable running requirements.&lt;/p&gt;
&lt;p&gt;One of the main best practices references is the &lt;a class="reference external" href="https://12factor.net/"&gt;Twelve-Factor App&lt;/a&gt;, which is a methodology with a bundle of good practices for building modern applications with portable requirements, such as applications running on SaaS platforms and Kubernetes clusters. There is a chapter dedicated to application configuration: &lt;a class="reference external" href="https://12factor.net/config"&gt;12 Factor » Config&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In summary, the config chapter explains what (and what not) categorizes an application parameter, and emphasizes the separation of configuration from code.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="python-config-libraries"&gt;
&lt;h2&gt;Python Config Libraries&lt;/h2&gt;
&lt;p&gt;Like almost all recurring generic needs and well-defined best practices, there are modules and frameworks to help us implement them. In Python, several tools can help achieve these best practices. I have used some nice tools like &lt;a class="reference external" href="https://github.com/theskumar/python-dotenv"&gt;python-dotenv&lt;/a&gt;, which focuses on reading simple key-value pairs from a &lt;em&gt;.env&lt;/em&gt; file and can set them as environment variables, and &lt;a class="reference external" href="https://github.com/HBNetwork/python-decouple"&gt;python-decouple&lt;/a&gt;, which provides a dynamic settings interface to help change parameters without having to redeploy your app (useful for web applications). Additionally, there are modules for parsing structured files that can be used as config files, such as &lt;a class="reference external" href="https://pyyaml.org/wiki/PyYAMLDocumentation"&gt;PyYAML&lt;/a&gt;, &lt;a class="reference external" href="https://docs.python.org/3/library/json.html#module-json"&gt;json&lt;/a&gt;, &lt;a class="reference external" href="https://docs.python.org/3/library/tomllib.html#module-tomllib"&gt;tomllib&lt;/a&gt;, &lt;a class="reference external" href="https://docs.python.org/3/library/shlex.html#module-shlex"&gt;shlex&lt;/a&gt;, and &lt;a class="reference external" href="https://docs.python.org/3/library/configparser.html"&gt;configparser&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These modules are really nice and have specific use cases, especially when you can use a native approach and simple programs like routine scripts. But, with the provocative blog post title, I will present Dynaconf.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dynaconf"&gt;
&lt;h2&gt;Dynaconf!&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/dynaconf/dynaconf"&gt;Dynaconf&lt;/a&gt; is a robust configuration management for Python, designed to follow &lt;a class="reference external" href="https://12factor.net/config"&gt;Twelve-Factor App&lt;/a&gt; config principles. It serves as an abstraction layer between code and settings, providing a simple interface to retrieve parameters and secrets from files or external system sources.&lt;/p&gt;
&lt;p&gt;The highlight features include support for multiple config files in various formats (&lt;em&gt;toml, yaml, json, ini&lt;/em&gt;), parameters via environment variables, and multi-hierarchical parameter set profiles support (e.g., &lt;em&gt;default, development, production&lt;/em&gt;). Additionally, there is a CLI that is useful for debugging.&lt;/p&gt;
&lt;p&gt;A notable feature is the capability to retrieve parameters from external parameter/secrets storage systems. There is built-in support for HashiCorp Vault and Redis, but it is also possible to extend it to retrieve parameters from any source based on your needs. This feature is really awesome! I will show an example of how to use it to retrieve parameters from AWS Parameter Store.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="talk-is-cheap-show-me-the-code"&gt;
&lt;h2&gt;Talk is cheap, Show me the code!&lt;/h2&gt;
&lt;p&gt;To exemplify how to use Dynaconf, I will show a step-by-step guide on how to configure and use it from scratch. The example covers how to set up multiple settings files and how to extend the parameter source to retrieve parameters from the AWS Parameter Store service (simulated by &lt;a class="reference external" href="https://github.com/localstack/localstack"&gt;LocalStack&lt;/a&gt; in a Docker container).&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;All code is in the following GitHub repository: &lt;i class="fab fa-github"&gt;&lt;/i&gt; &lt;em&gt;&lt;a class="reference external" href="https://github.com/c-neto/my-code-playground/tree/main/blog-code-examples/2024-07-21-dynaconf-loaders"&gt;github.com/c-neto/my-code-playground/blog-code-examples/2024-07-21-dynaconf-loaders&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="step-by-step"&gt;
&lt;h3&gt;Step-by-step&lt;/h3&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Create a virtual environment and install dependencies:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;venv&lt;span class="w"&gt; &lt;/span&gt;venv
venv/bin/pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;dynaconf&lt;span class="w"&gt; &lt;/span&gt;boto3&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;venv/bin/activate
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="2"&gt;
&lt;li&gt;&lt;p&gt;Use the dynaconf-cli to speed up the settings and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;config.py&lt;/span&gt;&lt;/code&gt; files creation:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;dynaconf&lt;span class="w"&gt; &lt;/span&gt;init&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARAMETER_FROM_SETTINGS_TOML_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;foobar from settings.toml&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARAMETER_FROM_SECRETS_TOML_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;foobar from .secrets.toml&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# » output&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# ├── config.py       » python module with dynaconf instance&lt;/span&gt;
&lt;span class="c1"&gt;# ├── .secrets.toml   » settings files with: PARAMETER_FROM_SECRETS_TOML_FILE&lt;/span&gt;
&lt;span class="c1"&gt;# └── settings.toml   » settings files with: PARAMETER_FROM_SETTINGS_TOML_FILE&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;config.py&lt;/span&gt;&lt;/code&gt; source code created by &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dynaconf-cli&lt;/span&gt;&lt;/code&gt; looks like this:&lt;/em&gt;&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;config.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dynaconf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dynaconf&lt;/span&gt;

&lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dynaconf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;envvar_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;DYNACONF&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;settings_files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;settings.toml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.secrets.toml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;You can use the dynaconf-cli to list parameters based on the configuration defined in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;config.py&lt;/span&gt;&lt;/code&gt;:&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;dynaconf&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;config.settings&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;

&lt;span class="c1"&gt;# » output&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# PARAMETER_FROM_SETTINGS_TOML_FILE&amp;lt;str&amp;gt; &amp;#39;foobar from settings.toml&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# PARAMETER_FROM_SECRETS_TOML_FILE&amp;lt;str&amp;gt; &amp;#39;foobar from .secrets.toml&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="3"&gt;
&lt;li&gt;&lt;p&gt;Run LocalStack in a container to simulate the AWS Parameter Store service:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;-it&lt;span class="w"&gt; &lt;/span&gt;--detach&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;localstack&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4566&lt;/span&gt;:4566&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SERVICES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ssm,sts&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DEFAULT_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east-1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;localstack/localstack:latest
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="4"&gt;
&lt;li&gt;&lt;p&gt;With LocalStack running, export the AWS environment variables to access the LocalStack API.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AWS_ENDPOINT_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east-1
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="5"&gt;
&lt;li&gt;&lt;p&gt;Create two parameters prefixed by &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/my-application/&lt;/span&gt;&lt;/code&gt; named &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;PARAMETER_FROM_AWS_PARAMETER_STORE_1&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;PARAMETER_FROM_AWS_PARAMETER_STORE_2&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;aws&lt;span class="w"&gt; &lt;/span&gt;ssm&lt;span class="w"&gt; &lt;/span&gt;put-parameter&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/my-application/PARAMETER_FROM_AWS_PARAMETER_STORE_1&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--value&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SecureString&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--endpoint-url&lt;span class="w"&gt; &lt;/span&gt;http://localhost:4566

aws&lt;span class="w"&gt; &lt;/span&gt;ssm&lt;span class="w"&gt; &lt;/span&gt;put-parameter&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/my-application/PARAMETER_FROM_AWS_PARAMETER_STORE_2&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--value&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SecureString&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--endpoint-url&lt;span class="w"&gt; &lt;/span&gt;http://localhost:4566
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="6"&gt;
&lt;li&gt;&lt;p&gt;Edit the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;conf.py&lt;/span&gt;&lt;/code&gt; to add the custom loaders to retrieve the parameters from AWS Parameter Store. All parameters within the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/my-application/&lt;/span&gt;&lt;/code&gt; namespace will be retrieved&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;config.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;logging&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;botocore.exceptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ClientError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BotoCoreError&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dynaconf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dynaconf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LazySettings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dynaconf.loaders.base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SourceMetadata&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;PARAMETERS_PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/my-application/&amp;quot;&lt;/span&gt;


&lt;span class="c1"&gt;# Retrieve parameters from AWS Parameter Store recursively by prefix&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;_aws_parameter_store_loader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parameters_prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ssm_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ssm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;paginator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssm_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_paginator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;get_parameters_by_path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paginator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;parameters_prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Recursive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WithDecryption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;parameters_from_aws_raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Parameters&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;name_full&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;parameters_from_aws_raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name_full&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

    &lt;span class="n"&gt;parameters_from_aws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name_full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;parameters_from_aws_raw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name_full&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Parameter name pattern outside of expected format, skipping: &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name_full&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;parameters_from_aws&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parameters_from_aws&lt;/span&gt;


&lt;span class="c1"&gt;# Dynaconf callback - used in this case for retrieve parameters from AWS Parameter Store&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LazySettings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;silent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;parameters_from_aws&lt;/span&gt;

    &lt;span class="n"&gt;source_metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SourceMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;aws-parameter-store&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PARAMETERS_PREFIX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parameters_from_aws&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loader_identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;source_metadata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Parameters loaded from AWS Parameter Store: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parameters_from_aws&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# Attempt to load parameters from AWS Parameter Store and update Dynaconf loaders&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parameters_from_aws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_aws_parameter_store_loader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PARAMETERS_PREFIX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BotoCoreError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ClientError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;LOADERS_FOR_DYNACONF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;dynaconf.loaders.env_loader&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;AWS active session not detected, skipping load parameters from AWS Parameter Store&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# __name__ is module name that have &amp;quot;load()&amp;quot; callback. It could be another module...&lt;/span&gt;
    &lt;span class="n"&gt;LOADERS_FOR_DYNACONF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;dynaconf.loaders.env_loader&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="c1"&gt;# Initialize Dynaconf settings with specified files and loaders&lt;/span&gt;
&lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dynaconf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;settings_files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;settings.toml&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;.secrets.toml&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;envvar_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;MY_APP&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LOADERS_FOR_DYNACONF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;LOADERS_FOR_DYNACONF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="7"&gt;
&lt;li&gt;&lt;p&gt;Check the variables and note the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;PARAMETER_FROM_AWS_PARAMETER_STORE_1&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;PARAMETER_FROM_AWS_PARAMETER_STORE_2&lt;/span&gt;&lt;/code&gt; that were retrieved from AWS Parameter Store.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;dynaconf&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;config.settings&lt;span class="w"&gt; &lt;/span&gt;list

&lt;span class="c1"&gt;# » output&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# PARAMETER_FROM_SETTINGS_TOML_FILE&amp;lt;str&amp;gt; &amp;#39;foobar from settings.toml&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# PARAMETER_FROM_SECRETS_TOML_FILE&amp;lt;str&amp;gt; &amp;#39;foobar from .secrets.toml&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# PARAMETER_FROM_AWS_PARAMETER_STORE_1&amp;lt;str&amp;gt; &amp;#39;foo&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# PARAMETER_FROM_AWS_PARAMETER_STORE_2&amp;lt;str&amp;gt; &amp;#39;bar&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="8"&gt;
&lt;li&gt;&lt;p&gt;The environment variables take precedence over other settings sources, following the Unix philosophy. To test this behavior, export &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;MY_APP_PARAMETER_FROM_AWS_PARAMETER_STORE_2&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MY_APP_PARAMETER_FROM_AWS_PARAMETER_STORE_2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;param from environment variable&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;Dynaconf will retrieve only variables that are prefixed by the value defined in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;envvar_prefix&lt;/span&gt;&lt;/code&gt; instance argument; in this case, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;MY_APP&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Now, when &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;PARAMETER_FROM_AWS_PARAMETER_STORE_2&lt;/span&gt;&lt;/code&gt; is accessed, the value will be the one defined in the environment variable.&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;dynaconf&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;config.settings&lt;span class="w"&gt; &lt;/span&gt;list

&lt;span class="c1"&gt;# » output&lt;/span&gt;
&lt;span class="c1"&gt;# &lt;/span&gt;
&lt;span class="c1"&gt;# PARAMETER_FROM_SETTINGS_TOML_FILE&amp;lt;str&amp;gt; &amp;#39;foobar from settings.toml&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# PARAMETER_FROM_SECRETS_TOML_FILE&amp;lt;str&amp;gt; &amp;#39;foobar from .secrets.toml&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# PARAMETER_FROM_AWS_PARAMETER_STORE_1&amp;lt;str&amp;gt; &amp;#39;foo&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# PARAMETER_FROM_AWS_PARAMETER_STORE_2&amp;lt;str&amp;gt; &amp;#39;param from environment variable&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;In addition, you can peform inspect action to check the parameters to ensure source of each them.&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;dynaconf&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;config.settings&lt;span class="w"&gt; &lt;/span&gt;inspect

&lt;span class="c1"&gt;# » output&lt;/span&gt;
&lt;span class="c1"&gt;# &lt;/span&gt;
&lt;span class="c1"&gt;# {&lt;/span&gt;
&lt;span class="c1"&gt;#   &amp;quot;header&amp;quot;: {&lt;/span&gt;
&lt;span class="c1"&gt;#     &amp;quot;env_filter&amp;quot;: &amp;quot;None&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#     &amp;quot;key_filter&amp;quot;: &amp;quot;None&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#     &amp;quot;new_first&amp;quot;: &amp;quot;True&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#     &amp;quot;history_limit&amp;quot;: &amp;quot;None&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#     &amp;quot;include_internal&amp;quot;: &amp;quot;False&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#   },&lt;/span&gt;
&lt;span class="c1"&gt;#   &amp;quot;current&amp;quot;: {&lt;/span&gt;
&lt;span class="c1"&gt;#     &amp;quot;PARAMETER_FROM_SETTINGS_TOML_FILE&amp;quot;: &amp;quot;foobar from settings.toml&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#     &amp;quot;PARAMETER_FROM_SECRETS_TOML_FILE&amp;quot;: &amp;quot;foobar from .secrets.toml&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#     &amp;quot;PARAMETER_FROM_AWS_PARAMETER_STORE_1&amp;quot;: &amp;quot;foo&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#     &amp;quot;PARAMETER_FROM_AWS_PARAMETER_STORE_2&amp;quot;: &amp;quot;bar&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#   },&lt;/span&gt;
&lt;span class="c1"&gt;#   &amp;quot;history&amp;quot;: [&lt;/span&gt;
&lt;span class="c1"&gt;#     {&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;loader&amp;quot;: &amp;quot;aws-parameter-store&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;identifier&amp;quot;: &amp;quot;/my-application/&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;env&amp;quot;: &amp;quot;global&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;merged&amp;quot;: false,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;value&amp;quot;: {&lt;/span&gt;
&lt;span class="c1"&gt;#         &amp;quot;PARAMETER_FROM_AWS_PARAMETER_STORE_1&amp;quot;: &amp;quot;foo&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#         &amp;quot;PARAMETER_FROM_AWS_PARAMETER_STORE_2&amp;quot;: &amp;quot;bar&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#       }&lt;/span&gt;
&lt;span class="c1"&gt;#     },&lt;/span&gt;
&lt;span class="c1"&gt;#     {&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;loader&amp;quot;: &amp;quot;toml&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;identifier&amp;quot;: &amp;quot;.secrets.toml&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;env&amp;quot;: &amp;quot;default&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;merged&amp;quot;: false,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;value&amp;quot;: {&lt;/span&gt;
&lt;span class="c1"&gt;#         &amp;quot;PARAMETER_FROM_SECRETS_TOML_FILE&amp;quot;: &amp;quot;foobar from .secrets.toml&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#       }&lt;/span&gt;
&lt;span class="c1"&gt;#     },&lt;/span&gt;
&lt;span class="c1"&gt;#     {&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;loader&amp;quot;: &amp;quot;toml&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;identifier&amp;quot;: &amp;quot;settings.toml&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;env&amp;quot;: &amp;quot;default&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;merged&amp;quot;: false,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;value&amp;quot;: {&lt;/span&gt;
&lt;span class="c1"&gt;#         &amp;quot;PARAMETER_FROM_SETTINGS_TOML_FILE&amp;quot;: &amp;quot;foobar from settings.toml&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#       }&lt;/span&gt;
&lt;span class="c1"&gt;#     },&lt;/span&gt;
&lt;span class="c1"&gt;#     {&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;loader&amp;quot;: &amp;quot;set_method&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;identifier&amp;quot;: &amp;quot;settings_module_method&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;env&amp;quot;: &amp;quot;global&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;merged&amp;quot;: false,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;value&amp;quot;: {&lt;/span&gt;
&lt;span class="c1"&gt;#         &amp;quot;SETTINGS_MODULE&amp;quot;: [&lt;/span&gt;
&lt;span class="c1"&gt;#           &amp;quot;settings.toml&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#           &amp;quot;.secrets.toml&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#         ]&lt;/span&gt;
&lt;span class="c1"&gt;#       }&lt;/span&gt;
&lt;span class="c1"&gt;#     },&lt;/span&gt;
&lt;span class="c1"&gt;#     {&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;loader&amp;quot;: &amp;quot;set_method&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;identifier&amp;quot;: &amp;quot;init_kwargs&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;env&amp;quot;: &amp;quot;global&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;merged&amp;quot;: false,&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;value&amp;quot;: {&lt;/span&gt;
&lt;span class="c1"&gt;#         &amp;quot;LOADERS_FOR_DYNACONF&amp;quot;: [&lt;/span&gt;
&lt;span class="c1"&gt;#           &amp;quot;config&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#           &amp;quot;dynaconf.loaders.env_loader&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#         ],&lt;/span&gt;
&lt;span class="c1"&gt;#         &amp;quot;SETTINGS_FILE_FOR_DYNACONF&amp;quot;: [&lt;/span&gt;
&lt;span class="c1"&gt;#           &amp;quot;settings.toml&amp;quot;,&lt;/span&gt;
&lt;span class="c1"&gt;#           &amp;quot;.secrets.toml&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#         ],&lt;/span&gt;
&lt;span class="c1"&gt;#         &amp;quot;ENVVAR_PREFIX_FOR_DYNACONF&amp;quot;: &amp;quot;MY_APP&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#       }&lt;/span&gt;
&lt;span class="c1"&gt;#     }&lt;/span&gt;
&lt;span class="c1"&gt;#   ]&lt;/span&gt;
&lt;span class="c1"&gt;# }&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="conclusion-author-opinion"&gt;
&lt;h2&gt;Conclusion (Author Opinion)&lt;/h2&gt;
&lt;p&gt;I have been using Dynaconf around 4 years, and I really appreciate how the module is designed to address recurring challenges in a simple and straightforward manner. Previously, I used &lt;a class="reference external" href="https://pyyaml.org/wiki/PyYAMLDocumentation"&gt;PyYAML&lt;/a&gt;, but I frequently created repetitive code when reading configurations and secrets from distinct files. When a mix of input parameter sources was required, such as YAML and environment variables, the code complexity increased, diverting attention from the core problem my systems aimed to solve.&lt;/p&gt;
&lt;p&gt;When I first tried Dynaconf, I was impressed by its ability to manage multiple config files while merging the parameters and sub-parameters structure. This feature is very useful to me, especially for separating sensitive information into specific files.&lt;/p&gt;
&lt;p&gt;Now, I only avoid using Dynaconf in cases where there is a Software Requirement to use only built-in modules. Otherwise, I don’t see a reason not to use it, especially in applications designed to run in Kubernetes.&lt;/p&gt;
&lt;p&gt;I really like the module and suggest you give it a try!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="links"&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://12factor.net"&gt;https://12factor.net&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://12factor.net/config"&gt;https://12factor.net/config&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://docs.python.org/3/library/json.html"&gt;https://docs.python.org/3/library/json.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://docs.python.org/3/library/shlex.html"&gt;https://docs.python.org/3/library/shlex.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://docs.python.org/3/library/tomllib.html"&gt;https://docs.python.org/3/library/tomllib.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/c-neto/my-code-playground/tree/main/blog-code-examples/2024-07-20-dynaconf-loaders"&gt;c-neto/my-code-playground&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/HBNetwork/python-decouple"&gt;HBNetwork/python-decouple&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/theskumar/python-dotenv"&gt;theskumar/python-dotenv&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2024/2024-07-21-dynaconf/"/>
    <summary>Blog Post Publish Date: 2024/07/21This blog post outlines a brief overview of best practices for application configuration, explains the main features of Dynaconf, and provides a hands-on example of how to extend it to retrieve parameters from AWS Parameter Store.</summary>
    <category term="dynaconf" label="dynaconf"/>
    <category term="python" label="python"/>
    <published>2024-07-21T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2024/2024-06-08-logstash-sniffing/</id>
    <title>Logstash Sniffing: Improving Log Ingestion in OpenSearch Scalable Clusters</title>
    <updated>2024-06-08T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2024/06/08&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="logstash-sniffing-improving-log-ingestion-in-opensearch-scalable-clusters"&gt;

&lt;p&gt;This blog post outlines an optimize log ingestion in OpenSearch using Logstash. It will address a common issue related to shipping logs to scalable OpenSearch clusters and provide an effective solution to enhance resource utilization.&lt;/p&gt;
&lt;section id="the-problem-unbalanced-node-utilization-in-opensearch-cluster"&gt;
&lt;h2&gt;The Problem - Unbalanced Node Utilization in OpenSearch Cluster&lt;/h2&gt;
&lt;p&gt;When managing an OpenSearch cluster with scalable &lt;a class="reference external" href="https://opensearch.org/docs/2.14/tuning-your-cluster/"&gt;Ingest Nodes&lt;/a&gt; accessed through a load balancer and using Logstash to ingest logs, the Elasticsearch/OpenSearch output plugin requires customization to optimally utilize the available OpenSearch resources.&lt;/p&gt;
&lt;p&gt;Although log shipping to OpenSearch is stateless over the HTTP protocol, Logstash establishes a fixed connection with a specific OpenSearch ingest node. When a load balancer selects a node, Logstash will keep to send all logs to this node as long as the connection remains active. This can overwhelm the selected node, leaving other Ingest Nodes underutilized. Additionally, if you use average resource metrics for scaling Ingest Nodes with Horizontal Pod Autoscaling (HPA), this imbalance can impact performance.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-solution-utilizing-opensearch-api-for-dynamic-hosts-registration"&gt;
&lt;h2&gt;The Solution - Utilizing OpenSearch API for Dynamic Hosts Registration&lt;/h2&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/plugins-outputs-elasticsearch.html#plugins-outputs-elasticsearch-hosts"&gt;hosts&lt;/a&gt; parameter in the OpenSearch output plugin can be configured with a single address or multiple addresses. Using multiple addresses allows Logstash to manage and balance log shipping across declared hosts. For non-scalable Ingest Nodes, specifying multiple hosts is a solution of the issue described above. However, for scalable Ingest Nodes, this static approach is insufficient due to the dynamic nature of node scaling.&lt;/p&gt;
&lt;p&gt;To address this, the OpenSearch output plugin offers &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/plugins-outputs-elasticsearch.html#plugins-outputs-elasticsearch-sniffing"&gt;sniffing parameters&lt;/a&gt; to dynamically retrieve the current OpenSearch nodes addresses from the &lt;a class="reference external" href="https://opensearch.org/docs/latest/api-reference/nodes-apis/nodes-info/"&gt;OpenSearch API&lt;/a&gt; and update the hosts configuration accordingly.&lt;/p&gt;
&lt;p&gt;Consider the following example:&lt;/p&gt;
&lt;div class="highlight-js notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;opensearch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;hosts&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;https://opensearch-load-balancer:9200&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sniffing&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sniffing_delay&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;30&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sniffing_path&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/_nodes/ingest:true&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;When Logstash starts, it sends a request to the OpenSearch API endpoint &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/_nodes/ingest:true&lt;/span&gt;&lt;/code&gt; to retrieve the addresses of all Ingest Nodes. These addresses are dynamically added and removed from the hosts list. Every &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;30&lt;/span&gt;&lt;/code&gt; seconds, the node pool addresses are updated. The Load Balancer address &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;https://opensearch-load-balancer:9200&lt;/span&gt;&lt;/code&gt; is used only to fetch the IP addresses.&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-circle-info"&gt;&lt;/i&gt; You can specify other OpenSearch node types using the &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/plugins-outputs-elasticsearch.html#plugins-outputs-elasticsearch-sniffing_path"&gt;sniffing_path&lt;/a&gt; parameter. For instance, to use &lt;a class="reference external" href="https://opensearch.org/docs/2.14/tuning-your-cluster/"&gt;Data nodes&lt;/a&gt;, set the parameter to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/_nodes/data:true&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="conclusion-author-opinion"&gt;
&lt;h2&gt;Conclusion (Author Opinion)&lt;/h2&gt;
&lt;p&gt;This approach ensures better traffic distribution to OpenSearch, significantly reduces load balancer traffic, eliminates an extra network hop, and enhances resource usage, thereby improving Horizontal Pod Autoscaling in OpenSearch.&lt;/p&gt;
&lt;p&gt;This solution is so effective and straightforward that I’m beginning to miss its presence in other log shipping tools like Fluentbit.&lt;/p&gt;
&lt;p&gt;I have been using this approach and the results are really nice!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="links"&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Logstash Output ElasticSearch/OpenSearch: &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/plugins-outputs-elasticsearch.html#plugins-outputs-elasticsearch-sniffing"&gt;https://www.elastic.co/guide/en/logstash/current/plugins-outputs-elasticsearch.html#plugins-outputs-elasticsearch-sniffing&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;OpenSearch Cluster Formation (Node Types): &lt;a class="reference external" href="https://opensearch.org/docs/2.14/tuning-your-cluster/"&gt;https://opensearch.org/docs/2.14/tuning-your-cluster/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;OpenSearch Nodes API: &lt;a class="reference external" href="https://opensearch.org/docs/latest/api-reference/nodes-apis/nodes-info/"&gt;https://opensearch.org/docs/latest/api-reference/nodes-apis/nodes-info/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2024/2024-06-08-logstash-sniffing/"/>
    <summary>Blog Post Publish Date: 2024/06/08This blog post outlines an optimize log ingestion in OpenSearch using Logstash. It will address a common issue related to shipping logs to scalable OpenSearch clusters and provide an effective solution to enhance resource utilization.</summary>
    <category term="logstash" label="logstash"/>
    <category term="opensearch" label="opensearch"/>
    <published>2024-06-08T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2024/2024-04-21-fzf/</id>
    <title>fzf: Life is Too Short for Pipe Grep</title>
    <updated>2024-04-21T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2024/04/28&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="fzf-life-is-too-short-for-pipe-grep"&gt;

&lt;p&gt;This blog post outlines the advantages of the &lt;a class="reference external" href="https://github.com/junegunn/fzf"&gt;fzf&lt;/a&gt; (Fuzzy Finder CLI) and explains how to configure its &lt;a class="reference external" href="https://www.zsh.org/"&gt;Zsh&lt;/a&gt; widgets.&lt;/p&gt;
&lt;section id="searching-in-the-terminal-tedious-task"&gt;
&lt;h2&gt;Searching in the Terminal - Tedious Task&lt;/h2&gt;
&lt;p&gt;Searching for items in the terminal is a trivial activity, but can be a boring and tedious task. I will list three cases to exemplify it: searching command history; files and directories; and command output.&lt;/p&gt;
&lt;p&gt;Usually, users press the arrow up/down to recall last commands executed. The most used shells implemented an interactive search tool called by &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CTRL&lt;/span&gt;&lt;/code&gt; + &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;R&lt;/span&gt;&lt;/code&gt; shortcut. In Zsh, it is implemented as &lt;em&gt;reverse-i-search&lt;/em&gt;, which works well, but you need to press the shortcut again to see the next results. This behavior can be a problem if you need to recall commands executed multiple times with different arguments.&lt;/p&gt;
&lt;p&gt;When it is necessary to find some file or directory, the most common method adopted is to use &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;find&lt;/span&gt;&lt;/code&gt;. It works well when you know exactly the item that you need to find, but not so good when you only know some partials of the path.&lt;/p&gt;
&lt;p&gt;Lastly, I can mention searching elements in command outputs. The most adopted method for this case is to use matching expressions within a command output using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;command&lt;/span&gt; &lt;span class="pre"&gt;|&lt;/span&gt; &lt;span class="pre"&gt;grep&lt;/span&gt;&lt;/code&gt;. When the same problem of finding files and directories previously described is true for this. Depending on the case, it is necessary to execute grep multiple times until you find the elements that you need.&lt;/p&gt;
&lt;p&gt;This tedious task prompted me to research better alternatives for interactive searching.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="fuzzy-finders-the-solution"&gt;
&lt;h2&gt;Fuzzy Finders - The Solution&lt;/h2&gt;
&lt;p&gt;After a few days of research, I found an amazing project that solves my problems: &lt;a class="reference external" href="https://github.com/junegunn/fzf"&gt;fzf — command-line fuzzy finder&lt;/a&gt;. It is a cross-platform Fuzzy Finder command-line written in Go. But, what is Fuzzy Finder?&lt;/p&gt;
&lt;p&gt;Fuzzy Finder is a search tool category for providing a quickly and flexibly way to find files, directories, or other elements, even when they don’t remember the precise names. It employs fuzzy matching to find results based on partial words, erratic characters, or typographical errors.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/junegunn/fzf"&gt;fzf&lt;/a&gt; filters the list of items as you type. This makes your searching much more efficient, avoiding &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;command&lt;/span&gt; &lt;span class="pre"&gt;|&lt;/span&gt; &lt;span class="pre"&gt;grep&lt;/span&gt;&lt;/code&gt; execution. It can receive output of the other commands for interative searching, for instance you can execute &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;kgp&lt;/span&gt; &lt;span class="pre"&gt;-A&lt;/span&gt; &lt;span class="pre"&gt;|&lt;/span&gt; &lt;span class="pre"&gt;fzf&lt;/span&gt;&lt;/code&gt; to find pods in a Kubernetes cluster. In addition, it has some predefined shells widgets to search files, directories, and search history commands.&lt;/p&gt;
&lt;p&gt;The next section will explain how to configure these widgets in Zsh.&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-circle-info"&gt;&lt;/i&gt; There are widgets available for Fish, Bash, Zsh. You can check the available implementations in &lt;a class="reference external" href="https://github.com/junegunn/fzf/tree/master/shell"&gt;github.com/junegunn/fzf/shell&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="how-to-configure-fzf-widgets-in-zsh"&gt;
&lt;h2&gt;How to Configure fzf Widgets in Zsh&lt;/h2&gt;
&lt;p&gt;The first step is to install fzf. There are some distinct ways to install it described in &lt;a class="reference external" href="https://github.com/junegunn/fzf/tree/master?tab=readme-ov-file#installation"&gt;fzf — installation section&lt;/a&gt;. Depending on the package manager on your workstation, the version available can be older. Thus, I will download and install the latest version available in GitHub Releases section.&lt;/p&gt;
&lt;div class="highlight-zsh notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;setopt&lt;span class="w"&gt; &lt;/span&gt;INTERACTIVE_COMMENTS

&lt;span class="c1"&gt;# download the latest version (in a moment of the blog publish date, the latest version is 0.50.0)&lt;/span&gt;
curl&lt;span class="w"&gt; &lt;/span&gt;-L&lt;span class="w"&gt; &lt;/span&gt;https://github.com/junegunn/fzf/releases/latest/download/fzf-0.50.0-linux_amd64.tar.gz&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;fzf-0.50.0-linux_amd64.tar.gz

&lt;span class="c1"&gt;# uncompress the donwloaded file to access the fzf binary&lt;/span&gt;
tar&lt;span class="w"&gt; &lt;/span&gt;-xzvf&lt;span class="w"&gt; &lt;/span&gt;fzf-0.50.0-linux_amd64.tar.gz

&lt;span class="c1"&gt;# check fzf binary execution&lt;/span&gt;
./fzf&lt;span class="w"&gt; &lt;/span&gt;--help

&lt;span class="c1"&gt;# move the binary to a folder present in your path&lt;/span&gt;
mv&lt;span class="w"&gt; &lt;/span&gt;fzf&lt;span class="w"&gt; &lt;/span&gt;/usr/local/bin/
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The fzf use the command &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;find&lt;/span&gt;&lt;/code&gt; behind the scenes as engine to search directories and files. You can change it. In this example, I will install and configure the &lt;a class="reference external" href="https://github.com/sharkdp/fd"&gt;fd-find&lt;/a&gt;, a fast and user-friendly alternative to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;find&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-zsh notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# In Linux workstation with dnf package manager&lt;/span&gt;
dnf&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;fd-find

&lt;span class="c1"&gt;# In MacOs workstation with homebrew package manager&lt;/span&gt;
brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;fd
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For file content preview, I will install and configure &lt;a class="reference external" href="https://github.com/sharkdp/bat"&gt;bat&lt;/a&gt;, a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;cat&lt;/span&gt;&lt;/code&gt; clone with syntax highlighting.&lt;/p&gt;
&lt;div class="highlight-zsh notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# In Linux workstation with dnf package manager&lt;/span&gt;
dnf&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;bat

&lt;span class="c1"&gt;# In MacOs workstation with homebrew package manager&lt;/span&gt;
brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;bat
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For directory and sub directories preview, I will install and configure &lt;a class="reference external" href="https://www.cyberciti.biz/faq/linux-show-directory-structure-command-line/"&gt;tree&lt;/a&gt;, a utility which recursively displays the contents of directories in a tree-like format.&lt;/p&gt;
&lt;div class="highlight-zsh notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# In Linux workstation with dnf package manager&lt;/span&gt;
dnf&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;tree

&lt;span class="c1"&gt;# In MacOs workstation with homebrew package manager&lt;/span&gt;
brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;tree
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, it is necessary load the Shell widgets. For this, you can get the widget setup source-code given the shell name as argument (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--zsh&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--fish&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--bash&lt;/span&gt;&lt;/code&gt;). In this case, I will setup the widgets for Zsh.&lt;/p&gt;
&lt;div class="highlight-zsh notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Set up fzf key bindings and fuzzy completion (only available in 0.48.0 or later)&lt;/span&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;fzf&lt;span class="w"&gt; &lt;/span&gt;--zsh&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Run the following command to check the keybindings are correctly loaded.&lt;/p&gt;
&lt;div class="highlight-zsh notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;bindkey&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;fzf

&lt;span class="c1"&gt;# output expected:&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;quot;^R&amp;quot; fzf-history-widget  # CTRL + R: reverse history search&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;quot;^T&amp;quot; fzf-file-widget     # CTRL + T: search files&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;quot;^[c&amp;quot; fzf-cd-widget      # ALT  + C: search directories&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can customize the widgets fzf parameters through environment variables described in &lt;a class="reference external" href="https://github.com/junegunn/fzf?tab=readme-ov-file#environment-variables--aliases"&gt;fzf — environment variables&lt;/a&gt;. Each widget has its own environments variables you can customize based on your needs. Take a look at following code block for an example of the how to parametrize fzf widgets.&lt;/p&gt;
&lt;div class="highlight-zsh notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# fzf parameters used in all widgets - configure layout and wrapped the preview results (useful in large command rendering)&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FZF_DEFAULT_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;--height 100% --layout reverse --preview-window=wrap&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# CTRL + R: put the selected history command in the preview window - &amp;quot;{}&amp;quot; will be replaced by item selected in fzf execution runtime&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FZF_CTRL_R_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;--preview &amp;#39;echo {}&amp;#39;&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# ALT + C: set &amp;quot;fd-find&amp;quot; as directory search engine instead of &amp;quot;find&amp;quot; and exclude &amp;quot;venv|virtualenv|.git&amp;quot; of the results during searching&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FZF_ALT_C_COMMAND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fd --type directory --exclude venv --exclude virtualenv --exclude .git&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# ALT + C: put the tree command output based on item selected&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FZF_ALT_C_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;--preview &amp;#39;tree -C {}&amp;#39;&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# CTRL + T: set &amp;quot;fd-find&amp;quot; as search engine instead of &amp;quot;find&amp;quot; and exclude &amp;quot;venv|virtualenv|.git&amp;quot; for the results&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FZF_CTRL_T_COMMAND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fd --exclude venv --exclude virtualenv --exclude .git&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# CTRL + T: put the file content if item select is a file, or put tree command output if item selected is directory&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FZF_CTRL_T_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;--preview &amp;#39;[ -d {} ] &amp;amp;&amp;amp; tree -C {} || bat --color=always --style=numbers {}&amp;#39;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The next section will present widgets executions preview.&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;You can check my &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.zshrc&lt;/span&gt;&lt;/code&gt; file content in the following link. It contains my personal fzf parametrization:
&lt;a class="github reference external" href="https://github.com/c-neto/ansible-configure-fedora/blob/main/files/dotfiles/.zshrc"&gt;c-neto/ansible-configure-fedora&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="results"&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;fzf-history-widget&lt;/em&gt; executed by &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CTRL&lt;/span&gt;&lt;/code&gt; + &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;R&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="fzf-history-widget execution print" src="https://carlosneto.dev/_images/results-1.png" /&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;fzf-cd-widget&lt;/em&gt; executed by &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ALT&lt;/span&gt;&lt;/code&gt; + &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;C&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="fzf-cd-widget execution print" src="https://carlosneto.dev/_images/results-2.png" /&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;fzf-file-widget&lt;/em&gt; executed by &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CTRL&lt;/span&gt;&lt;/code&gt; + &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;T&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="fzf-file-widget execution print" src="https://carlosneto.dev/_images/results-3.png" /&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Searching pods in Kubernetes Cluster with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;kgp&lt;/span&gt; &lt;span class="pre"&gt;-A&lt;/span&gt; &lt;span class="pre"&gt;|&lt;/span&gt; &lt;span class="pre"&gt;fzf&lt;/span&gt;&lt;/code&gt; execution.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="find pods with fzf print" src="https://carlosneto.dev/_images/results-4.png" /&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusion-author-opinion"&gt;
&lt;h2&gt;Conclusion (Author Opinion)&lt;/h2&gt;
&lt;p&gt;My research for improvements in searching history commands has yielded results that exceed my expectations. fzf opened my mind to understand what is Fuzzy Finder and it purpose. It not limited to be command history searching only, it is a tool that you can search anything. It is really nice!&lt;/p&gt;
&lt;p&gt;Certainly, I think it is a much better alternative to command history search widget default in Zsh, Bash, and Fish (&lt;em&gt;which already has a good history search widget&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;The interactive searching are blazingly fast and customization expanding the possibilities based on your needs. The productivity which fzf provides is really awesome! In a fact, that its justifies more than &lt;strong&gt;59K stars&lt;/strong&gt; in your GitHub repo.&lt;/p&gt;
&lt;p&gt;I approve and recommend!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="links"&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;bat&lt;/span&gt;&lt;/code&gt; reference: &lt;a class="github reference external" href="https://github.com/sharkdp/bat"&gt;sharkdp/bat&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;fd&lt;/span&gt;&lt;/code&gt; reference: &lt;a class="github reference external" href="https://github.com/sharkdp/fd"&gt;sharkdp/fd&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;fzf&lt;/span&gt;&lt;/code&gt; reference: &lt;a class="github reference external" href="https://github.com/junegunn/fzf/"&gt;junegunn/fzf&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;tree&lt;/span&gt;&lt;/code&gt; reference: &lt;a class="reference external" href="https://www.cyberciti.biz/faq/linux-show-directory-structure-command-line/"&gt;https://www.cyberciti.biz/faq/linux-show-directory-structure-command-line/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;zsh&lt;/span&gt;&lt;/code&gt; reference: &lt;a class="reference external" href="https://www.zsh.org/"&gt;https://www.zsh.org/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;My custom fzf configuration in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.zshrc&lt;/span&gt;&lt;/code&gt;: &lt;a class="github reference external" href="https://github.com/c-neto/ansible-configure-fedora/tree/main/files/dotfiles/.zshrc"&gt;c-neto/ansible-configure-fedora&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;extra&lt;/strong&gt;: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;skim&lt;/span&gt;&lt;/code&gt;, a fzf implemented in rust: &lt;a class="github reference external" href="https://github.com/lotabout/skim"&gt;lotabout/skim&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2024/2024-04-21-fzf/"/>
    <summary>Blog Post Publish Date: 2024/04/28This blog post outlines the advantages of the fzf (Fuzzy Finder CLI) and explains how to configure its Zsh widgets.</summary>
    <category term="fzf" label="fzf"/>
    <category term="zsh" label="zsh"/>
    <published>2024-04-21T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2024/2024-02-08-starship-zsh/</id>
    <title>ZSH + Starship: A Productivity Masterpiece</title>
    <updated>2024-02-08T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2024/02/08&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="zsh-starship-a-productivity-masterpiece"&gt;

&lt;p&gt;This blog post covers my prompt customization experience, favorite &lt;a class="reference external" href="https://www.zsh.org/"&gt;ZSH&lt;/a&gt; Plugins, &lt;a class="reference external" href="https://zsh.sourceforge.io/Doc/Release/Options.html"&gt;ZSH options&lt;/a&gt;, and &lt;a class="reference external" href="https://starship.rs/"&gt;Starship&lt;/a&gt; configuration. Finally, I introduce a simple guide to configure my custom theme from scratch.&lt;/p&gt;
&lt;section id="my-prompts-customization-experiences"&gt;
&lt;h2&gt;My Prompts Customization Experiences&lt;/h2&gt;
&lt;p&gt;Productivity is a topic that I definitely like. For long time, I used the raw Terminal over Bash, It is force me to memorize the commands, but the productivity is not so good. I felt that I need to improve my Terminal setup to focus in productivity.&lt;/p&gt;
&lt;section id="xonsh-python-bash-advantages-and-disadvantages"&gt;
&lt;h3&gt;&lt;strong&gt;xonsh&lt;/strong&gt;: Python + Bash Advantages (&lt;em&gt;and Disadvantages&lt;/em&gt;)&lt;/h3&gt;
&lt;p&gt;Due to my expertise in Python, I have been testing &lt;a class="reference external" href="https://xon.sh/"&gt;xonsh&lt;/a&gt; for approximately six months. The xonsh is a superset of Python that includes additional shell primitives familiar to Bash users. The combination of Bash and Python enhances productivity by blending the simplicity of Python syntax with the versatility of Bash. However, debugging can be challenging (&lt;em&gt;imagine a list comprehension in Python syntax combined with dictionaries and environment variables in Bash syntax&lt;/em&gt;).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="zsh-extensible-and-product-ready-if-customized"&gt;
&lt;h3&gt;&lt;strong&gt;zsh&lt;/strong&gt;: Extensible and Product Ready (&lt;em&gt;If Customized&lt;/em&gt;)&lt;/h3&gt;
&lt;p&gt;I needed come back the real world again, then I gave a chance for ZSH, a advanced and highly customizable command-line interface with enhanced productivity features and a large Plugins. It greatly appealed to me, mainly when I tested the &lt;a class="reference external" href="https://github.com/spaceship-prompt/spaceship-prompt"&gt;Spaceship&lt;/a&gt; a &lt;em&gt;“minimalistic, powerful and extremely customizable ZSH prompt”&lt;/em&gt;, and the &lt;a class="reference external" href="https://ohmyz.sh/"&gt;oh-my-zsh&lt;/a&gt; framework that provides a simple way to manage Plugins and Themes. I believed I had reached a stable terminal configuration with a good balance between productivity and simplicity, but in the MacOs workstation, the input and startup delay of the commands got on my nerves.&lt;/p&gt;
&lt;p&gt;I searched improvements tips in the Blog Posts and Youtube videos, but the result don’t satisfied myself. Then, I opted to install the Plugins manually avoiding the &lt;em&gt;oh-my-zsh&lt;/em&gt;, and I tested the &lt;a class="reference external" href="https://starship.rs/"&gt;Starship&lt;/a&gt; (in my words: “&lt;em&gt;a Spaceship alternative, in Rust, blazing-fast, with many stars in the GitHub&lt;/em&gt;”).&lt;/p&gt;
&lt;p&gt;The results were astonishing. I didn’t have to put in any effort to transition my theme configuration from Spaceship to Starship, and the performance is exactly as I expected.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="fish-ready-to-use-but-non-posix-compliant"&gt;
&lt;h3&gt;&lt;strong&gt;fish&lt;/strong&gt;: Ready to Use (&lt;em&gt;But non-POSIX Compliant&lt;/em&gt;)&lt;/h3&gt;
&lt;p&gt;In the past few days, I tried out &lt;a class="reference external" href="https://fishshell.com/"&gt;Fish&lt;/a&gt;, described as “&lt;em&gt;a smart and user-friendly command line shell&lt;/em&gt;”. I was impressed by its performance and the built-in features in the default setup. &lt;em&gt;Starship&lt;/em&gt; also supports &lt;em&gt;Fish&lt;/em&gt;, earning some positive points from me. I noticed better performance compared to ZSH, and the default configuration already includes features like dynamic syntax checking with color indicators, dynamic autocompletion based on the history file, and menu selection for command options and arguments. It’s simple and &lt;strong&gt;Ready to Use&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;However, there’s a drawback: Fish is not compliant with the &lt;a class="reference external" href="http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html"&gt;POSIX sh definition&lt;/a&gt;. For instance, declaring variables using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;FOOBAR=123&lt;/span&gt;&lt;/code&gt; won’t work, instead, you should use the correct syntax like &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;set&lt;/span&gt; &lt;span class="pre"&gt;FOOBAR&lt;/span&gt; &lt;span class="pre"&gt;123&lt;/span&gt;&lt;/code&gt;. This lack of compliance with POSIX is a downside for me, as it rules out the possibility of using &lt;em&gt;Fish&lt;/em&gt; in my daily work. Nevertheless, if ZSH is not an option, I’d still be willing to use Fish without any issues.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="my-zsh-favorite-plugins"&gt;
&lt;h2&gt;My ZSH Favorite Plugins&lt;/h2&gt;
&lt;p&gt;There are numerous plugins available for ZSH. Typically, these plugins are installed and managed by &lt;a class="reference external" href="https://ohmyz.sh/"&gt;oh-my-zsh&lt;/a&gt;, but you can install them directly, once these plugins are essentially script files with a predefined set of functions and routines.&lt;/p&gt;
&lt;p&gt;I will present in the following sub-topics, my indispensable ZSH Plugins.&lt;/p&gt;
&lt;section id="zsh-autosuggestions-dynamic-assistant-to-remember-commands"&gt;
&lt;h3&gt;&lt;strong&gt;zsh-autosuggestions&lt;/strong&gt;: Dynamic Assistant to Remember Commands&lt;/h3&gt;
&lt;p&gt;No doubt, it is my favorite plugin. This plugin emulates the behavior of the &lt;em&gt;Fish&lt;/em&gt; shell, suggesting command completions dynamically based on history.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://carlosneto.dev/_images/image-1.png" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-link"&gt;&lt;/i&gt; More Details: &lt;a class="github reference external" href="https://github.com/zsh-users/zsh-autosuggestions"&gt;zsh-users/zsh-autosuggestions&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="zsh-syntax-highlighting-valid-commands-by-color"&gt;
&lt;h3&gt;&lt;strong&gt;zsh-syntax-highlighting&lt;/strong&gt;: Valid Commands by Color&lt;/h3&gt;
&lt;p&gt;Another Plugin which emulate the &lt;em&gt;Fish&lt;/em&gt; shell. When a command is correctly typed, the color turns green. Otherwise, it changes to red. This feature is handy for detecting typos in a dynamic way&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://carlosneto.dev/_images/image-2.png" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-link"&gt;&lt;/i&gt; More Details: &lt;a class="github reference external" href="https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/"&gt;zsh-users/zsh-syntax-highlighting&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="kubectl-k-alias-for-kubectl"&gt;
&lt;h3&gt;&lt;strong&gt;kubectl&lt;/strong&gt;: k alias for kubectl&lt;/h3&gt;
&lt;p&gt;This plugin adds completion for the Kubernetes, as well as some aliases for common kubectl commands.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# shortcut for: kubectl get pods&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;kgp

&lt;span class="c1"&gt;# shortcut for: kubectl delete cm foobar-config-map&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;kdelcm&lt;span class="w"&gt; &lt;/span&gt;foobar-config-map

&lt;span class="c1"&gt;# shortcut for: kubectl get cronjob&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;k&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;cj
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-link"&gt;&lt;/i&gt; More Details: &lt;i class="fab fa-github"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/kubectl"&gt;ohmyzsh/ohmyzsh/plugins/kubectl&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="my-starship-configuration"&gt;
&lt;h2&gt;My Starship Configuration&lt;/h2&gt;
&lt;p&gt;The simplicity is elegance. I customized my Starship prompt with only attributes of the tools important for me.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://starship.rs/config/#aws"&gt;aws&lt;/a&gt;: Only the profile name of the my AWS session;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://starship.rs/config/#git-branch"&gt;git&lt;/a&gt;: Only the Branch, no more info;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://starship.rs/config/#python"&gt;python&lt;/a&gt;: The current activated virtualenv;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://starship.rs/config/#kubernetes"&gt;kubernetes&lt;/a&gt;: The Kubernetes context name. Based on the name, I applied the different styles to explicit the study, tests, and production workloads.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;Check my &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.config/starship.toml&lt;/span&gt;&lt;/code&gt; entire content: &lt;i class="fab fa-github"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/c-neto/ansible-configure-fedora/tree/main/files/dotfiles/starship.toml"&gt;c-neto/ansible-configure-fedora/files/dotfiles/starship.toml&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="my-favorite-zsh-options-setopt"&gt;
&lt;h2&gt;My favorite ZSH options (&lt;em&gt;setopt&lt;/em&gt;)&lt;/h2&gt;
&lt;p&gt;Some usually features (like comments, history file) from other shells like &lt;em&gt;Bash&lt;/em&gt; or &lt;em&gt;Fish&lt;/em&gt; must be explicit enabled in ZSH. These features are enabled by &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;setopt&lt;/span&gt;&lt;/code&gt; command in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.zshrc&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can get all options available in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;zle&lt;/span&gt; &lt;span class="pre"&gt;-al&lt;/span&gt;&lt;/code&gt; command execution or in the following &lt;a class="reference external" href="https://zsh.sourceforge.io/Doc/Release/Options.html"&gt;ZSH Options Reference&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Check the &lt;em&gt;ZSH Options&lt;/em&gt; I have enabled:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;INTERACTIVE_COMMENTS&lt;/span&gt;&lt;/code&gt;: Enable comments “#” expressions in the prompt shell;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;APPEND_HISTORY&lt;/span&gt;&lt;/code&gt;: Append new history entries to the history file;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;INC_APPEND_HISTORY&lt;/span&gt;&lt;/code&gt;: Save each command to the history file as soon as it is executed;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;HIST_IGNORE_DUPS&lt;/span&gt;&lt;/code&gt;: Ignore recording duplicate consecutive commands in the history (improve the performance of the &lt;em&gt;zsh-autosuggestions&lt;/em&gt;);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;HIST_IGNORE_SPACE&lt;/span&gt;&lt;/code&gt;: Ignore commands that start with a space in the history;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;Check my &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.zshrc&lt;/span&gt;&lt;/code&gt; entire content: &lt;i class="fab fa-github"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/c-neto/ansible-configure-fedora/tree/main/files/dotfiles/.zshrc"&gt;c-neto/ansible-configure-fedora/files/dotfiles/.zshrc&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="how-to-setup-my-zsh-starship-from-scratch"&gt;
&lt;h2&gt;How to Setup My ZSH + Starship From Scratch&lt;/h2&gt;
&lt;p&gt;You can check my dotfiles in my GitHub: &lt;i class="fab fa-github"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/c-neto/ansible-configure-fedora/tree/main/files/dotfiles/"&gt;c-neto/ansible-configure-fedora/files/dotfiles/&lt;/a&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;1&lt;/strong&gt; &lt;em&gt;optional&lt;/em&gt;: Save a backup of your current dotfiles (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.zshrc&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.zsh_history&lt;/span&gt;&lt;/code&gt;):&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.zshrc&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.zshrc.backup&amp;quot;&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.zsh_history&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.zsh_history.backup&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2&lt;/strong&gt;: Install the &lt;a class="reference external" href="https://www.zsh.org/"&gt;ZSH&lt;/a&gt; with your package manager.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="sd-container-fluid sd-sphinx-override sd-mb-4 docutils"&gt;
&lt;div class="sd-row docutils"&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# if Linux (Fedora)&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;dnf&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;zsh
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# if MacOS&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;zsh
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;3&lt;/strong&gt;: Install the &lt;a class="reference external" href="https://starship.rs/"&gt;Starship&lt;/a&gt;:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sS&lt;span class="w"&gt; &lt;/span&gt;https://starship.rs/install.sh&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sh
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-circle-info"&gt;&lt;/i&gt; &lt;strong&gt;About Nerd Fonts&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.config/starship.toml&lt;/span&gt;&lt;/code&gt; file (which will be downloaded in step &lt;strong&gt;5&lt;/strong&gt;) does not necessitate &lt;a class="reference external" href="https://www.nerdfonts.com/"&gt;Nerd Fonts&lt;/a&gt;. However, if you encounter issues with emoji rendering, consider the installation.&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;4&lt;/strong&gt;: Create a directory in your home to save the ZSH plugins and the Starship configuration:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.config/&amp;quot;&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.my-custom-zsh/&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;5&lt;/strong&gt;: Download my &lt;i class="fab fa-github"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/c-neto/ansible-configure-fedora/tree/main/files/dotfiles/starship.toml"&gt;~/.config/starship.toml&lt;/a&gt;:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://raw.githubusercontent.com/c-neto/ansible-configure-fedora/main/files/dotfiles/starship.toml&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.config/starship.toml&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;6&lt;/strong&gt;: Install the plugins manually:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# k alias&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/plugins/kubectl/kubectl.plugin.zsh&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.my-custom-zsh/kubectl.plugin.zsh&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# zsh-syntax-highlighting&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;--depth&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://github.com/zsh-users/zsh-syntax-highlighting.git&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.my-custom-zsh/zsh-syntax-highlighting&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# zsh-autosuggestions&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;--depth&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://github.com/zsh-users/zsh-autosuggestions&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.my-custom-zsh/zsh-autosuggestions&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;7&lt;/strong&gt;: Download my &lt;i class="fab fa-github"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/c-neto/ansible-configure-fedora/tree/main/files/dotfiles/.zshrc"&gt;~/.zshrc&lt;/a&gt;:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://raw.githubusercontent.com/c-neto/ansible-configure-fedora/main/files/dotfiles/.zshrc&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.zshrc&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-circle-info"&gt;&lt;/i&gt; &lt;strong&gt;Key Binding Tip&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can customize the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bindkey&lt;/span&gt;&lt;/code&gt; values according to your preferences. To find the code for your keys, run &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;cat&lt;/span&gt; &lt;span class="pre"&gt;-v&lt;/span&gt;&lt;/code&gt; and press the desired key; the code will be displayed in your shell. This way, you can modify the functions like &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;forward-word&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;backward-word&lt;/span&gt;&lt;/code&gt; based on key code displayed.&lt;/p&gt;
&lt;p&gt;More details about &lt;em&gt;bindkey&lt;/em&gt; options (&lt;em&gt;Standard Widgets&lt;/em&gt;): &lt;a class="reference external" href="https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets"&gt;https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;8&lt;/strong&gt;: Restart your terminal, the results will be like this:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="" src="https://carlosneto.dev/_images/image-3.png" /&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusion-author-opinion"&gt;
&lt;h2&gt;Conclusion (Author Opinion)&lt;/h2&gt;
&lt;p&gt;Productivity is a dynamic goal, and tools must fit your current needs and continually may be changed to enhance processes based on demands.&lt;/p&gt;
&lt;p&gt;Currently, for my DevOps routine, the combination of ZSH and Starship proves to be a balanced cross-platform approach, offering a blend of performance, productivity, and extendability.&lt;/p&gt;
&lt;p&gt;Ultimately, I reserve an honorable mention for &lt;em&gt;Fish&lt;/em&gt; which your default configuration is a source of inspiration for the plugins I use in ZSH.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="links"&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;My dotfiles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.zshrc&lt;/span&gt;&lt;/code&gt;: &lt;i class="fab fa-github"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/c-neto/ansible-configure-fedora/tree/main/files/dotfiles/.zshrc"&gt;c-neto/ansible-configure-fedora/files/dotfiles/.zshrc&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/.config/starship.toml&lt;/span&gt;&lt;/code&gt;: &lt;i class="fab fa-github"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/c-neto/ansible-configure-fedora/tree/main/files/dotfiles/starship.toml"&gt;c-neto/ansible-configure-fedora/files/dotfiles/starship.toml&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ZSH Reference:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.zsh.org/"&gt;https://www.zsh.org/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://zsh.sourceforge.io/Doc/Release/Options.html"&gt;https://zsh.sourceforge.io/Doc/Release/Options.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets"&gt;https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ZSH Plugins:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/kubectl"&gt;ohmyzsh/ohmyzsh&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/zsh-users/zsh-autosuggestions"&gt;zsh-users/zsh-autosuggestions&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/zsh-users/zsh-syntax-highlighting"&gt;zsh-users/zsh-syntax-highlighting&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Starship:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://starship.rs/"&gt;https://starship.rs/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/starship/starship"&gt;starship/starship&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Other links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://xon.sh/"&gt;https://xon.sh/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://ohmyz.sh/"&gt;https://ohmyz.sh/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/spaceship-prompt/spaceship-prompt"&gt;spaceship-prompt/spaceship-prompt&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html"&gt;POSIX sh definition&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2024/2024-02-08-starship-zsh/"/>
    <summary>Blog Post Publish Date: 2024/02/08This blog post covers my prompt customization experience, favorite ZSH Plugins, ZSH options, and Starship configuration. Finally, I introduce a simple guide to configure my custom theme from scratch.</summary>
    <category term="zsh" label="zsh"/>
    <published>2024-02-08T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2023/2023-11-19-systemd-override-parameters/</id>
    <title>Properties Overriding: A Smart Way to Customize Systemd Unit Properties</title>
    <updated>2023-11-19T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2023/11/19&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="properties-overriding-a-smart-way-to-customize-systemd-unit-properties"&gt;

&lt;p&gt;This blog post covers an smart alternative to customize systemd unit process parameters.&lt;/p&gt;
&lt;p&gt;This blog post delivers a explanation of the systemd &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;override.conf&lt;/span&gt;&lt;/code&gt; file. It elucidates the drawbacks of directly altering unit service configurations, introduces the overriding approach as a solution, presents a practical, hands-on example, and culminates with my perspective on the effectiveness of this structural methodology.&lt;/p&gt;
&lt;section id="drawbacks-of-direct-unit-service-editing"&gt;
&lt;h2&gt;Drawbacks of Direct Unit Service Editing&lt;/h2&gt;
&lt;p&gt;At times, there’s a need to modify certain properties within a particular systemd unit service, such as adjusting the value of an environment variable to enhance debug levels for troubleshooting. Traditionally, this involves directly altering the unit service file. For instance, if you wish to modify properties of Docker, you’d edit &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/etc/systemd/system/docker.service&lt;/span&gt;&lt;/code&gt;, implement the changes, refresh systemd with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;systemctl&lt;/span&gt; &lt;span class="pre"&gt;daemon-reload&lt;/span&gt;&lt;/code&gt;, and then restart the Docker process using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;systemctl&lt;/span&gt; &lt;span class="pre"&gt;restart&lt;/span&gt; &lt;span class="pre"&gt;docker&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;While this approach functions adequately, it introduces certain behaviors that may pose issues depending on your use case.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Modifying default unit services elevates the risk of configuring settings incorrectly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Custom changes can be challenging to discern if they lack proper comments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unit service files might be overridden based on the installation method. For instance, the unit service could be replaced according to package update definitions of a package manager like &lt;a class="reference external" href="https://rpm.org/"&gt;rpm&lt;/a&gt; or &lt;a class="reference external" href="https://wiki.debian.org/Apt"&gt;apt&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the following section, I will elucidate an elegant workaround for these behaviors.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-solution-parameters-overriding"&gt;
&lt;h2&gt;The Solution: Parameters Overriding&lt;/h2&gt;
&lt;p&gt;A sophisticated approach to customizing unit service parameters involves overriding them.&lt;/p&gt;
&lt;p&gt;This method enables you to make changes to systemd unit properties while preserving the default file without any modifications. This is possible because the method reads custom properties from another file and merges them with the default properties.&lt;/p&gt;
&lt;p&gt;Override parameters are declared in the following file pattern:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;systemd&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UNIT&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;SERVICE&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;em&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;lt;UNIT-SERVICE-NAME&amp;gt;&lt;/span&gt;&lt;/code&gt; is a placeholder.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;All unit service properties defined in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;override.conf&lt;/span&gt;&lt;/code&gt; will take precedence over those defined in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;lt;UNIT-SERVICE-NAME&amp;gt;.service&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Consider the following example, where both default and override unit properties are illustrated.&lt;/p&gt;
&lt;div class="sd-container-fluid sd-sphinx-override sd-mb-4 docutils"&gt;
&lt;div class="sd-row docutils"&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Default Properties&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;/etc/systemd/system/my-script.service&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-toml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;[Description]&lt;/span&gt;
&lt;span class="c1"&gt;# collapsed parameters...&lt;/span&gt;

&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;span class="n"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;my-script.sh --prod&lt;/span&gt;

&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;span class="c1"&gt;# collapsed parameters...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Override Properties&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;/etc/systemd/system/my-script.service.d/override.conf&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-toml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;span class="n"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;my-script.sh --dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;When the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;my-script.service&lt;/span&gt;&lt;/code&gt; be executed, the &lt;em&gt;ExecStart&lt;/em&gt; command will be &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;my-script.sh&lt;/span&gt; &lt;span class="pre"&gt;--dev&lt;/span&gt;&lt;/code&gt; because it were declared in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;override.conf&lt;/span&gt;&lt;/code&gt; that replace the &lt;em&gt;ExecStart&lt;/em&gt; defined in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;my-script.service&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;An efficient shortcut to execute this action is through the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;systemctl&lt;/span&gt; &lt;span class="pre"&gt;edit&lt;/span&gt; &lt;span class="pre"&gt;&amp;lt;UNIT-SERVICE-NAME&amp;gt;&lt;/span&gt;&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;This command creates a directory and file at &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/etc/systemd/system/&amp;lt;UNIT-SERVICE-NAME&amp;gt;.service.d/override.conf&lt;/span&gt;&lt;/code&gt;, opens your text editor for making the necessary changes, and upon closing it, automatically executes &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;systemctl&lt;/span&gt; &lt;span class="pre"&gt;daemon-reload&lt;/span&gt;&lt;/code&gt; behind the scenes. The only additional step is to execute &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;systemctl&lt;/span&gt; &lt;span class="pre"&gt;restart&lt;/span&gt; &lt;span class="pre"&gt;&amp;lt;UNIT-SERVICE-NAME&amp;gt;&lt;/span&gt;&lt;/code&gt; when necessary to apply the changes.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="hands-on"&gt;
&lt;h2&gt;Hands-On&lt;/h2&gt;
&lt;p&gt;Let’s get hands-on! This section will delve into a detailed example of the override method.&lt;/p&gt;
&lt;p&gt;We will create a systemd unit service named &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;my-script&lt;/span&gt;&lt;/code&gt;. To achieve this, we need to create the following files:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;/tmp/my-script/env_custom.env&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# env vars source of the my-script process&lt;/span&gt;
/tmp/my-script/env_default.env&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# other env vars source of the my-script process&lt;/span&gt;
/tmp/my-script/my-script.sh&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="c1"&gt;# script that my-script process run&lt;/span&gt;
/etc/systemd/system/my-script.service&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# unit service file definition&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;em&gt;&lt;i class="fa-solid fa-link"&gt;&lt;/i&gt; The files can be found on my GitHub: &lt;i class="fa-brands fa-github"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/c-neto/my-devops-labs/tree/main/blog/2023-11-19/"&gt;c-neto/my-devops-labs/blog/2023-11-19&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Initially, let’s create the working directory for the script.&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;/tmp/my-script/
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Following that, we’ll generate the source code for the script.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id3"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;$ vim /tmp/my-script/my-script.sh&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;true&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="nv"&gt;$MY_CUSTOM_ENV_VAR&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;sleep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Assign the source code to the execution file parameter.&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;+x&lt;span class="w"&gt; &lt;/span&gt;/tmp/my-script/my-script.sh
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;We will create two files that will serve as the source for environment variables in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;my-script&lt;/span&gt;&lt;/code&gt; unit service.&lt;/p&gt;
&lt;div class="sd-container-fluid sd-sphinx-override sd-mb-4 docutils"&gt;
&lt;div class="sd-row docutils"&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;div class="literal-block-wrapper docutils container" id="id4"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;$ vim /tmp/my-script/env_default.env&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;APP_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;My Awesome Script&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;MY_CUSTOM_ENV_VAR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Irish Blood&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;div class="literal-block-wrapper docutils container" id="id5"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;$ vim /tmp/my-script/env_custom.env&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;MY_CUSTOM_ENV_VAR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;English Heart&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;Next, we will create the systemd unit service file.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id6"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;$ vim /etc/systemd/system/my-script.service&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;Service&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/my-script/
&lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-script.sh
&lt;span class="nv"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/my-script/env_default.env

&lt;span class="o"&gt;[&lt;/span&gt;Install&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;multi-user.target
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, let’s reload the systemd daemon so that &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;my-script&lt;/span&gt;&lt;/code&gt; can be managed as a service by systemd, and then start it.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ systemctl daemon-reload
$ systemctl start my-script
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To inspect the log of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;my-script&lt;/span&gt;&lt;/code&gt;, you can verify the value of the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/tmp/my-script/env_default.env&lt;/span&gt;&lt;/code&gt; file.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id7"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;$ journalctl -u my-script -f&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Nov&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;19&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;:11:29&lt;span class="w"&gt; &lt;/span&gt;fedora&lt;span class="w"&gt; &lt;/span&gt;my-script.sh&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;33631&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;My&lt;span class="w"&gt; &lt;/span&gt;Awesome&lt;span class="w"&gt; &lt;/span&gt;Script&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;Irish&lt;span class="w"&gt; &lt;/span&gt;Blood
Nov&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;19&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;:11:30&lt;span class="w"&gt; &lt;/span&gt;fedora&lt;span class="w"&gt; &lt;/span&gt;my-script.sh&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;33631&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;My&lt;span class="w"&gt; &lt;/span&gt;Awesome&lt;span class="w"&gt; &lt;/span&gt;Script&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;Irish&lt;span class="w"&gt; &lt;/span&gt;Blood
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Next, we will override the source file for environment variables.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ mkdir /etc/systemd/system/my-script.service.d/
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;We will override the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;EnvironmentFile&lt;/span&gt;&lt;/code&gt; parameter.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id8"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;$ vim /etc/systemd/system/my-script.service.d/override.conf&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;Service&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/my-script/env_custom.env
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This time, variables from the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;env_custom.env&lt;/span&gt;&lt;/code&gt; file will replace those present in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;env_default.env&lt;/span&gt;&lt;/code&gt; file.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id9"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;$ journalctl -u my-script -f&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Nov&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;19&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;:21:42&lt;span class="w"&gt; &lt;/span&gt;fedora&lt;span class="w"&gt; &lt;/span&gt;my-script.sh&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;58573&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;My&lt;span class="w"&gt; &lt;/span&gt;Awesome&lt;span class="w"&gt; &lt;/span&gt;Script&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;English&lt;span class="w"&gt; &lt;/span&gt;Heart
Nov&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;19&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;:21:43&lt;span class="w"&gt; &lt;/span&gt;fedora&lt;span class="w"&gt; &lt;/span&gt;my-script.sh&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;58573&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;My&lt;span class="w"&gt; &lt;/span&gt;Awesome&lt;span class="w"&gt; &lt;/span&gt;Script&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;English&lt;span class="w"&gt; &lt;/span&gt;Heart
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="conclusion-author-opinion"&gt;
&lt;h2&gt;Conclusion (Author Opinion)&lt;/h2&gt;
&lt;p&gt;In conclusion, leveraging systemd override parameters emerges as an invaluable tool in proactively addressing and preventing recurring issues. By preserving the integrity of the original unit service file while explicitly specifying custom parameters, this method significantly enhances troubleshooting capabilities. Another notable advantage lies in safeguarding against the loss of custom configurations that may be tied to the installation manager used to create a unit service (dnf, apt, brew).&lt;/p&gt;
&lt;p&gt;The seamless flexibility provided by the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$&lt;/span&gt; &lt;span class="pre"&gt;systemctl&lt;/span&gt; &lt;span class="pre"&gt;edit&lt;/span&gt;&lt;/code&gt; command empowers users to swiftly and efficiently modify process properties, ensuring an agile and responsive approach to system management. In essence, incorporating systemd override parameters not only fortifies system stability but also streamlines the process of adapting and optimizing configurations to meet evolving needs. As a dynamic solution in the realm of systemd, it stands as an essential practice for maintaining a resilient and adaptable system environment.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="links"&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/c-neto/my-devops-labs/tree/main/blog/2023-11-19/"&gt;c-neto/my-devops-labs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://systemd.io/"&gt;https://systemd.io/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://access.redhat.com/sites/default/files/attachments/12052018_systemd_6.pdf"&gt;https://access.redhat.com/sites/default/files/attachments/12052018_systemd_6.pdf&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2023/2023-11-19-systemd-override-parameters/"/>
    <summary>Blog Post Publish Date: 2023/11/19This blog post covers an smart alternative to customize systemd unit process parameters.</summary>
    <category term="systemd" label="systemd"/>
    <published>2023-11-19T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2023/2023-11-02-logstash-modular-pipelines/</id>
    <title>Logstash Modular Pipelines: An Elegant Structure for Code Reusability and Duplication Avoiding</title>
    <updated>2023-11-12T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2023/11/12&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="logstash-modular-pipelines-an-elegant-structure-for-code-reusability-and-duplication-avoiding"&gt;

&lt;p&gt;This blog post explores a Logstash pipelines structure to mitigate code duplicated and presents an elegant method for reusing code section across multiple pipelines.&lt;/p&gt;
&lt;p&gt;The post provides a clear explanation of the Logstash configuration structure, outlining the problem addressed by modular pipelines. It includes an example configuration and a Docker-compose lab for hands-on testing and exploration of the possibilities. Finally, the post concludes with my opinion on the effectiveness of this structure.&lt;/p&gt;
&lt;section id="a-little-about-logstash-and-pipelines-structure"&gt;
&lt;h2&gt;A Little About Logstash and Pipelines Structure&lt;/h2&gt;
&lt;p&gt;The Logstash is an amazing tool for crafting robust log pipelines. Several plugins empower the ingestion, process, enrich, and output integration with external stacks. The pipelines are created using Logstash Configuration DSL (&lt;em&gt;Domain-Specific Language&lt;/em&gt;), a high-level configuration language designed to be efficient and flexible, and focused on Log Pipeline needs.&lt;/p&gt;
&lt;p&gt;The Logstash pipeline configuration is composed of three main sections:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;input&lt;/span&gt;&lt;/code&gt;: Define the log ingestion source.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;filter&lt;/span&gt;&lt;/code&gt;: Define the process, parser, and enrich routines.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;output&lt;/span&gt;&lt;/code&gt;: Define the forward routines with external stacks.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;em&gt;&lt;i class="fa-solid fa-link"&gt;&lt;/i&gt; More Details: &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/8.11/configuration-file-structure.html#configuration-file-structure"&gt;Logstash - Configuration File Structure&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="the-problem-scaling-up-pipelines-growing-code-duplication"&gt;
&lt;h2&gt;The Problem: Scaling Up Pipelines == Growing Code Duplication&lt;/h2&gt;
&lt;p&gt;As the number of log pipelines increases, so does the complexity and tendency for code duplication. For straightforward application observability needs, having pipelines with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;input&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;filter&lt;/span&gt;&lt;/code&gt;, and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;output&lt;/span&gt;&lt;/code&gt; configured together in the same file might suffice. However, in complex scenarios involving large applications, such as distributed applications deployed on Kubernetes, various challenges emerge. Among these challenges, one of the most significant is avoiding code duplication.&lt;/p&gt;
&lt;p&gt;When dealing with multiple applications requiring distinct logic for log processing, one approach is to create a single pipeline with logical conditions determining the specific processing statements based on fields such as Tags, which identify the module generating the event. While this method is advantageous in avoiding duplication of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;input&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;output&lt;/span&gt;&lt;/code&gt; configurations, it has drawbacks. It introduces overhead in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;filter&lt;/span&gt;&lt;/code&gt; section and complicates troubleshooting in the Logstash API metrics for identifying application process issues.&lt;/p&gt;
&lt;p&gt;Alternatively, another approach is to establish separate pipelines, each with its own dedicated configuration files for individual applications. While this resolves the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;filter&lt;/span&gt;&lt;/code&gt; overhead issue, it comes at the cost of duplicating both &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;input&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;output&lt;/span&gt;&lt;/code&gt; configurations.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-solution-modularize-the-pipeline"&gt;
&lt;h2&gt;The Solution: Modularize the Pipeline&lt;/h2&gt;
&lt;p&gt;A good way to solve the problem mentioned before is &lt;strong&gt;modularize the Logstash Pipelines sections &lt;em&gt;(input, filter, output)&lt;/em&gt; in isolated files, and load them in dedicated Pipelines based on their needs&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For this, it is necessary to create separate files with only one Pipeline section definition (&lt;em&gt;input, filter, output&lt;/em&gt;), create dedicated pipelines for each Application, and configure them using &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/glob-support.html"&gt;
Glob Pattern Support&lt;/a&gt; and &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/environment-variables.html"&gt;Environment Variable&lt;/a&gt; notation.&lt;/p&gt;
&lt;p&gt;The following sub topic will explain with more details about how to do it.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="configuration-example"&gt;
&lt;h2&gt;Configuration Example&lt;/h2&gt;
&lt;p&gt;First step, is to create the modules, that is, the code of the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;input&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;filter&lt;/span&gt;&lt;/code&gt;, and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;output&lt;/span&gt;&lt;/code&gt; in separate files, each one focused on their specifics and atomic needs.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;$ tree /usr/share/logstash/pipeline/&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;filter-app-a.cfg&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# filter of the application-a logic parser&lt;/span&gt;
├──&lt;span class="w"&gt; &lt;/span&gt;filter-app-b.cfg&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# filter of the application-b logic parser&lt;/span&gt;
├──&lt;span class="w"&gt; &lt;/span&gt;input-http.cfg&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# input to receive logs from http protocol&lt;/span&gt;
├──&lt;span class="w"&gt; &lt;/span&gt;input-rabbitmq.cfg&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# input to receive logs from rabbitmq queue&lt;/span&gt;
├──&lt;span class="w"&gt; &lt;/span&gt;output-opensearch.cfg&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# output to forward logs processed to opensearch server &lt;/span&gt;
└──&lt;span class="w"&gt; &lt;/span&gt;output-http.cfg&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# output to forward logs processed to http server&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Next, for each application, configure them with &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/environment-variables.html"&gt;Environment Variable&lt;/a&gt; Logstash notation:&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;$ cat /usr/share/logstash/config/pipeline.yml&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pipeline.id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;application-a-pipeline&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;path.config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/usr/share/logstash/pipeline/${LOGSTASH_PIPELINE_APPLICATION_A}.cfg&amp;quot;&lt;/span&gt;

&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pipeline.id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;application-b-pipeline&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;path.config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/usr/share/logstash/pipeline/${LOGSTASH_PIPELINE_APPLICATION_B}.cfg&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Finally, configure the composition of the pipeline with &lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/glob-support.html"&gt;Glob Pattern Support&lt;/a&gt; notation. Consider the following example:&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id3"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;$ env&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;LOGSTASH_PIPELINE_APPLICATION_A&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{input-http,input-rabbitmq,filter-app-a,output-opensearch,output-http}&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;LOGSTASH_PIPELINE_APPLICATION_B&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{input-http,filter-app-b,output-opensearch}&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LOGSTASH_PIPELINE_APPLICATION_A&lt;/strong&gt;: Composes the Logstash pipeline &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;application-a-pipeline&lt;/span&gt;&lt;/code&gt; to receive logs from HTTP and RabbitMQ, processed them with parse logic defined in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;filter-app-a.cfg&lt;/span&gt;&lt;/code&gt; and forwarded to OpenSearch and HTTP server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LOGSTASH_PIPELINE_APPLICATION_B&lt;/strong&gt;: Composes the Logstash pipeline &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;application-b-pipeline&lt;/span&gt;&lt;/code&gt; to receive logs from HTTP, process them with the logic defined in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;filter-app-b.cfg&lt;/span&gt;&lt;/code&gt; and forwarded to OpenSearch only.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;section id="main-advantages"&gt;
&lt;h3&gt;Main Advantages:&lt;/h3&gt;
&lt;p&gt;This approach brings the following advantages:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoid Code Duplication&lt;/strong&gt;: Allows to use modules across multiple pipelines (for example, configure the same &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;input&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;output&lt;/span&gt;&lt;/code&gt; module for all pipelines).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reusable Code&lt;/strong&gt;: Allows a simple configurable way to use more than one module of the same type in specific pipelines (for example, two &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;output&lt;/span&gt;&lt;/code&gt;, and three &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;input&lt;/span&gt;&lt;/code&gt; in one specific pipeline).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decrease Complexity&lt;/strong&gt;: The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;filter&lt;/span&gt;&lt;/code&gt; code is composed of only the application parser logic, avoiding conditional logical based on source event tags to identify how the logic need to be used.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Troubleshooting&lt;/strong&gt;: Preserve the traceable of the pipelines in the logstash metric API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tests&lt;/strong&gt;: Make it easier to test &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;filter&lt;/span&gt;&lt;/code&gt; code because the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;input&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;output&lt;/span&gt;&lt;/code&gt; modules is simple to change (environment variables), allowing a configuration of the &lt;em&gt;mock&lt;/em&gt; log source as &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;input&lt;/span&gt;&lt;/code&gt; and &lt;em&gt;stdout&lt;/em&gt; as a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;output&lt;/span&gt;&lt;/code&gt; to check if the logic is working as expected.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="docker-compose-lab"&gt;
&lt;h3&gt;Docker Compose Lab&lt;/h3&gt;
&lt;p&gt;I created a docker-compose lab to test and explore the possibilities of the content presented in this post.&lt;/p&gt;
&lt;p&gt;Check in my GitHub: &lt;i class="fa-brands fa-github"&gt;&lt;/i&gt; &lt;a class="reference external" href="https://github.com/c-neto/my-devops-labs/tree/main/blog/2023-11-12/"&gt;c-neto/my-devops-lab/blog/2023-11-12/docker-compose.yaml&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="conclusion-author-opinion"&gt;
&lt;h2&gt;Conclusion (Author Opinion)&lt;/h2&gt;
&lt;p&gt;Logstash is a versatile tool for log processing, providing well-configured solutions for both small and large workloads to meet Observability needs. While the Logstash Module structure offers a straightforward approach to mitigate recurring issues in pipeline creation, it is not the exclusive remedy for this challenge.&lt;/p&gt;
&lt;p&gt;Understanding Observability requirements is essential for effectively harnessing the tool’s features. Occasionally, the Logstash Module Structure may introduce unnecessary complexity, especially in scenarios involving a limited number of applications.&lt;/p&gt;
&lt;p&gt;I have implemented this structure and witnessed significant improvements in maintainability, particularly in the &lt;strong&gt;Tests&lt;/strong&gt; advantages mentioned, where I can easily test my &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;filter&lt;/span&gt;&lt;/code&gt; logics with a simple a environment variable value changing.&lt;/p&gt;
&lt;p&gt;I genuinely appreciate this structure because my primary goal is to simplify and make it easier the maintainability, but it’s important to recognize that &lt;strong&gt;every case is unique, and each context presents its own complexity and challenges&lt;/strong&gt;. Therefore, I recommend adopting this structure when the maintenance of pipelines becomes complex, code duplication is on the rise, and the configuration of Logstash becomes prone to errors, ultimately rendering it difficult to maintain.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="references"&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/8.11/configuration-file-structure.html#configuration-file-structure"&gt;https://www.elastic.co/guide/en/logstash/8.11/configuration-file-structure.html#configuration-file-structure&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/glob-support.html"&gt;https://www.elastic.co/guide/en/logstash/current/glob-support.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/environment-variables.html"&gt;https://www.elastic.co/guide/en/logstash/current/environment-variables.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="github reference external" href="https://github.com/c-neto/my-devops-labs/tree/main/blog/2023-11-12/"&gt;c-neto/my-devops-labs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2023/2023-11-02-logstash-modular-pipelines/"/>
    <summary>Blog Post Publish Date: 2023/11/12This blog post explores a Logstash pipelines structure to mitigate code duplicated and presents an elegant method for reusing code section across multiple pipelines.</summary>
    <category term="logstash" label="logstash"/>
    <published>2023-11-12T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2023/2023-10-21-cloud-concepts/</id>
    <title>Cloud Computing: Overview and Concepts</title>
    <updated>2023-10-21T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2023/10/21&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="cloud-computing-overview-and-concepts"&gt;

&lt;p&gt;This blog post explores the impact of Cloud Computing, essential concepts, primary Cloud models, and my opinion about its adoption.&lt;/p&gt;
&lt;section id="the-impact-of-cloud-computing"&gt;
&lt;h2&gt;The Impact of Cloud Computing&lt;/h2&gt;
&lt;p&gt;Historically, Development teams emphasized agility in contributions, while Operations/Infrastructure teams focused more on stability due to hardware interactions. However, with the advent of technologies like virtualization, manual infrastructure tasks became obsolete, and infrastructure components started being provided as a service by Cloud providers like AWS, Google, Microsoft, and others.&lt;/p&gt;
&lt;p&gt;Now, agility has become a crucial aspect for Operations teams as well. The maintenance of infrastructure as a service by Cloud providers alleviates concerns about electricity, hardware health, and other infrastructure requirements. This shift allows businesses to concentrate on their core purposes rather than infrastructure necessities. Outsourcing maintenance tasks to specialized teams enhances reliability and security, with various services tailored for infrastructure contexts and business cases. Additionally, hardware security is vital to address concerns like natural disasters and physical threats.&lt;/p&gt;
&lt;p&gt;A significant keyword in the Cloud landscape is possibilities. The Cloud offers possibilities to access high computing performance components as needed, scale components on demand, and pay only for the time used. These possibilities can lead to reduced costs if your application or infrastructure is scalable, allowing you to pay only for the resources needed for application functionality.&lt;/p&gt;
&lt;p&gt;However, it’s important to note that Cloud Computing isn’t a one-size-fits-all solution for every IT infrastructure case. It is one of the options to deploy your infrastructure or application. Before adopting the Cloud for your infrastructure, it’s crucial to ask yourself, “What problem am I trying to solve?” If your application aligns with the Cloud characteristics mentioned above, it might be the best choice for your needs.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="concepts-and-characteristics"&gt;
&lt;h2&gt;Concepts and Characteristics&lt;/h2&gt;
&lt;p&gt;Cloud Computing is a &lt;strong&gt;model for delivering computing services over the internet, providing a platform to manage, process, and store data on remote servers.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The Cloud Computing main characteristics are:&lt;/p&gt;
&lt;div class="sd-container-fluid sd-sphinx-override sd-mb-4 docutils"&gt;
&lt;div class="sd-row docutils"&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;Less concern about energy and hardware maintenance processes;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;Business focuses more on IT technologies.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;p class="sd-card-text"&gt;&lt;strong&gt;Reliability&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;Specialization in determining tools;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;High availability possibilities.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;p class="sd-card-text"&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;Utilization of specific security tools;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;Software and hardware security solutions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;Hardware components available as a service;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;Performance enhancements possible when needed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;p class="sd-card-text"&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;Scalability on demand;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;Minimal IT resource requirements to run applications.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;p class="sd-card-text"&gt;&lt;strong&gt;Cost and Elasticity&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;Cost management options;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;Both soft and hard cost considerations with available possibilities.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-link"&gt;&lt;/i&gt; See also: &lt;a class="reference external" href="https://aws.amazon.com/what-is-Cloud-computing/?nc1=h_ls"&gt;AWS - What is Cloud Computing&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="cloud-main-models"&gt;
&lt;h2&gt;Cloud Main Models&lt;/h2&gt;
&lt;p&gt;The cloud is not limited to AWS, Google Cloud Platform, or Oracle Cloud. These mentioned cloud providers belong to the category of &lt;strong&gt;Public Cloud&lt;/strong&gt;. However, it’s essential to understand that there are other ways to implement cloud solutions.&lt;/p&gt;
&lt;div class="sd-container-fluid sd-sphinx-override sd-mb-4 docutils"&gt;
&lt;div class="sd-row docutils"&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;strong&gt;Public Cloud&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p class="sd-card-text"&gt;Resources are shared and can be used by anyone (examples: AWS, GCP, Oracle Cloud).&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;hr class="docutils" /&gt;
&lt;p class="sd-card-text"&gt;&lt;strong&gt;Private Cloud&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p class="sd-card-text"&gt;Often referred to as &lt;em&gt;Bare Metal Cloud&lt;/em&gt; owned by a single entity, and resources are not shared with other corporations (examples: OpenStack, VMWare, and XenServer).&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;strong&gt;Hybrid Cloud&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p class="sd-card-text"&gt;A combination of both Private Cloud and Public Cloud components.&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;hr class="docutils" /&gt;
&lt;p class="sd-card-text"&gt;&lt;strong&gt;MultiCloud&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p class="sd-card-text"&gt;Cloud architecture is not dependent on a specific Cloud model and can run in both Private and Public Cloud environments.&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;hr class="docutils" /&gt;
&lt;p class="sd-card-text"&gt;&lt;strong&gt;Community Cloud&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p class="sd-card-text"&gt;Custom Cloud architecture created by corporations to provide clients with the flexibility of multiple Public and Private Cloud features.&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-link"&gt;&lt;/i&gt; More Details: &lt;a class="reference external" href="https://csrc.nist.gov/publications/detail/sp/800-145/final"&gt;NIST - Cloud Computing Definition&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="conclusion-author-opinion"&gt;
&lt;h2&gt;Conclusion (Author Opinion)&lt;/h2&gt;
&lt;p&gt;Cloud computing is a &lt;strong&gt;powerful&lt;/strong&gt; tool that can greatly benefit businesses. Cloud providers offer &lt;strong&gt;fantastic solutions&lt;/strong&gt; and services to address the challenges and product requirements, mainly to &lt;strong&gt;large enterprises&lt;/strong&gt;. Undoubtedly, Cloud computing has contributed to improvements in the IT product experience and the creation of new products, primarily due to the &lt;em&gt;Business focusing more on IT technologies&lt;/em&gt; characteristic, which allows engineers to concentrate their efforts on the product rather than infrastructure challenges.&lt;/p&gt;
&lt;p&gt;However, it is important to understand that &lt;strong&gt;it is not a one-size-fits-all solution&lt;/strong&gt;. Its effectiveness &lt;strong&gt;depends on your specific needs&lt;/strong&gt; and use cases. While it offers scalability and flexibility, it &lt;strong&gt;can also be more expensive&lt;/strong&gt;, especially if not optimized for your requirements. Migrating to the Cloud &lt;strong&gt;requires specialized expertise&lt;/strong&gt; to ensure a smooth transition and efficient management. Additionally, it’s important to note that the Cloud is not a guarantee of security; it requires a diligent implementation of security measures to safeguard your data and applications, further emphasizing the need for specialized expertise. Ultimately, the decision to embrace Cloud computing should be carefully evaluated, taking into account the unique demands and goals of your organization.&lt;/p&gt;
&lt;/section&gt;
&lt;hr class="docutils" /&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2023/2023-10-21-cloud-concepts/"/>
    <summary>Blog Post Publish Date: 2023/10/21This blog post explores the impact of Cloud Computing, essential concepts, primary Cloud models, and my opinion about its adoption.</summary>
    <category term="aws" label="aws"/>
    <category term="azure" label="azure"/>
    <category term="gcp" label="gcp"/>
    <category term="oracle" label="oracle"/>
    <published>2023-10-21T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://carlosneto.dev/blog/2023/2023-10-19-git-cli-gui/</id>
    <title>Git Clients Benchmarking: CLI vs. Graphical Interfaces</title>
    <updated>2023-10-19T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Blog Post Publish Date:&lt;/strong&gt; 2023/10/19&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;section id="git-clients-benchmarking-cli-vs-graphical-interfaces"&gt;

&lt;p&gt;This blog post is aimed at helping you understand the pros and cons of the most popular Git repository clients.&lt;/p&gt;
&lt;p&gt;The content was created with a focus on everyday use for fundamental tasks.&lt;/p&gt;
&lt;p&gt;I presented the solutions pros/cons and my particular conclusion and suggestions.&lt;/p&gt;
&lt;section id="command-line-interface-cli"&gt;
&lt;h2&gt;1. Command Line Interface (CLI)&lt;/h2&gt;
&lt;p&gt;It is the official method for interacting with your repository. You can perform all the functions that the Git system provides using the Git CLI. It also offers comprehensive documentation, and official resources typically use Git CLI in their examples.&lt;/p&gt;
&lt;div class="sd-container-fluid sd-sphinx-override sd-mb-4 docutils"&gt;
&lt;div class="sd-row docutils"&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;i class="fa-solid fa-face-smile"&gt;&lt;/i&gt; Key Pros:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Full Functionality&lt;/strong&gt;: Git CLI offers all functionalities and capabilities that Git provides, ensuring nothing is compromised.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Scripting and Automation&lt;/strong&gt;: Easily integrate Git commands into scripts and automate repetitive tasks.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;i class="fa-solid fa-face-frown"&gt;&lt;/i&gt; Key Cons:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Learning Curve&lt;/strong&gt;: For those unfamiliar with command-line interfaces, there may be a learning curve to grasp the commands and their usage effectively.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Less Visual Representation&lt;/strong&gt;: Git CLI provides data primarily through text output, which can be less intuitive for some users.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Productivity&lt;/strong&gt;: Repetitive and extensive use of fundamental operations can become unproductive.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-link"&gt;&lt;/i&gt; More Details: &lt;a class="reference external" href="https://git-scm.com/doc"&gt;Git Official Reference&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="gitkraken"&gt;
&lt;h2&gt;2. GitKraken&lt;/h2&gt;
&lt;p&gt;GitKraken is a comprehensive graphical enterprise suite focusing on enhancing Git functionalities.&lt;/p&gt;
&lt;div class="sd-container-fluid sd-sphinx-override sd-mb-4 docutils"&gt;
&lt;div class="sd-row docutils"&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;i class="fa-solid fa-face-smile"&gt;&lt;/i&gt; Key Pros:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Intuitive Interface&lt;/strong&gt;: GitKraken offers an easy-to-understand graphical interface, making it accessible for both beginners and experienced users.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Visualization&lt;/strong&gt;: Users can visualize their Git repository and workflow, aiding in a better understanding of branching, merging, and commits.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;i class="fa-solid fa-face-frown"&gt;&lt;/i&gt; Key Cons:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Cost&lt;/strong&gt;: While GitKraken offers a free version, more advanced features require a paid subscription.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-link"&gt;&lt;/i&gt; More Details: &lt;a class="reference external" href="https://www.gitkraken.com/"&gt;GitKraken Official Reference&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="sourcetree"&gt;
&lt;h2&gt;3. SourceTree&lt;/h2&gt;
&lt;p&gt;SourceTree is a freely available Git GUI developed and maintained by Atlassian, offering an intuitive interface for Git repository management.&lt;/p&gt;
&lt;div class="sd-container-fluid sd-sphinx-override sd-mb-4 docutils"&gt;
&lt;div class="sd-row docutils"&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;i class="fa-solid fa-face-smile"&gt;&lt;/i&gt; Key Pros:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;User-Friendly&lt;/strong&gt;: SourceTree offers an intuitive, user-friendly interface, making it easy to understand for users new to Git.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Visual Representation&lt;/strong&gt;: Users can visualize the Git workflow, including branches, commits, and merges, aiding in understanding repository history.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;i class="fa-solid fa-face-frown"&gt;&lt;/i&gt; Key Cons:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Atlassian Like&lt;/strong&gt;: Limited compatibility with non-Atlassian tools.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Features&lt;/strong&gt;: Limited advanced features.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Resources Usage&lt;/strong&gt;: Resource-intensive, slowing down your computer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;UI/UX&lt;/strong&gt;: Complex interface, overwhelming for beginners.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;OS Supports Limited&lt;/strong&gt;: Available only for Windows and macOS.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-link"&gt;&lt;/i&gt; More Details: &lt;a class="reference external" href="https://www.sourcetreeapp.com/"&gt;SourceTree Official Reference&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="vscode"&gt;
&lt;h2&gt;4. VSCode&lt;/h2&gt;
&lt;p&gt;VSCode, a popular text editor, inherently provides basic Git functionalities in a visual manner.&lt;/p&gt;
&lt;div class="sd-container-fluid sd-sphinx-override sd-mb-4 docutils"&gt;
&lt;div class="sd-row docutils"&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;i class="fa-solid fa-face-smile"&gt;&lt;/i&gt; Key Pros:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Integration&lt;/strong&gt;: Git features are seamlessly integrated into the editor, allowing for efficient version control directly from the coding environment.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;UI/UX&lt;/strong&gt;: Straight and Forward and intuitive interface to perform basic operations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Customization&lt;/strong&gt;: Users can enhance Git capabilities through various extensions available for VSCode.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;i class="fa-solid fa-face-frown"&gt;&lt;/i&gt; Key Cons:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Not Dedicated&lt;/strong&gt;: While it provides Git features, it’s primarily a text editor, so it might lack some advanced Git-specific features.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-link"&gt;&lt;/i&gt; More Details: &lt;a class="reference external" href="https://code.visualstudio.com/docs/sourcecontrol/intro-to-git"&gt;Introduction to Git in VS Code&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="jetbrains-ides"&gt;
&lt;h2&gt;5. JetBrains IDEs&lt;/h2&gt;
&lt;p&gt;JetBrains offers a range of Integrated Development Environments (IDEs) for various programming languages, all of which integrate Git functionalities.&lt;/p&gt;
&lt;div class="sd-container-fluid sd-sphinx-override sd-mb-4 docutils"&gt;
&lt;div class="sd-row docutils"&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;i class="fa-solid fa-face-smile"&gt;&lt;/i&gt; Key Pros:
Pros:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Comprehensive&lt;/strong&gt;: JetBrains IDEs provide a full suite of development tools along with Git integration.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Language Support&lt;/strong&gt;: Each IDE is tailored to specific languages, ensuring a seamless Git experience for the corresponding programming language.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-100 sd-shadow-sm docutils"&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;p class="sd-card-text"&gt;&lt;i class="fa-solid fa-face-frown"&gt;&lt;/i&gt; Key Cons:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Resource Intensive&lt;/strong&gt;: IDEs can be resource-heavy, especially for larger projects or on less powerful machines.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p class="sd-card-text"&gt;&lt;strong&gt;Cost&lt;/strong&gt;: Depends on IDE programming language, it’s only paid version available.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;i class="fa-solid fa-link"&gt;&lt;/i&gt; More Details: &lt;a class="reference external" href="https://www.jetbrains.com/help/idea/using-git-integration.html"&gt;JetBrains IDEA Using Git Integration&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="conclusion-author-opinion"&gt;
&lt;h2&gt;Conclusion (Author Opinion)&lt;/h2&gt;
&lt;p&gt;The Git CLI mastering is fundamental before to use the Graphical solutions. The GUI solutions are made to provides agility in the recurring operations. Only use the Git GUI solutions if you understand that what’s happening behind the scenes. The GUI solutions helps to make the changes, but when a big problems occurs, probably you will needs to use the Git CLI to solve it.&lt;/p&gt;
&lt;p&gt;The choice between Git CLI and graphical interfaces depends on your preference, familiarity with command-line tools, and the specific needs of your project.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;If you works a &lt;strong&gt;Software Developer&lt;/strong&gt;, my suggestion is mastering the use of the tool your IDE provides.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you work several stacks, like me as a &lt;strong&gt;DevOps Engineer&lt;/strong&gt;, I suggest using VSCode Git integration. It provides a simple UX/UI for a quick way to check the commit differences in one place, and the free version of the extension &lt;a class="reference external" href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens"&gt;GitLens&lt;/a&gt;  provides a simple way to perform more advanced operations like &lt;em&gt;squash&lt;/em&gt; and &lt;em&gt;rebase&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For &lt;strong&gt;Release Engineer&lt;/strong&gt; enterprise workloads, the GitKraken sounds like the better chosen because your UI/UX brings pretty historical commits graphics. If you work an Atlassian stack, and Linux workstations are not used, the SourceTree can be a good idea because integrations with Atlassian Stack are available.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;hr class="docutils" /&gt;
&lt;/section&gt;
</content>
    <link href="https://carlosneto.dev/blog/2023/2023-10-19-git-cli-gui/"/>
    <summary>Blog Post Publish Date: 2023/10/19This blog post is aimed at helping you understand the pros and cons of the most popular Git repository clients.</summary>
    <category term="benchmarking" label="benchmarking"/>
    <published>2023-10-19T00:00:00+00:00</published>
  </entry>
</feed>
