<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ZachiNachshon</title>
    <description>The latest articles on DEV Community by ZachiNachshon (@zachinachshon).</description>
    <link>https://dev.to/zachinachshon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F581529%2F334c6ba4-8fc8-497e-bff8-b47bec4ad7d6.jpeg</url>
      <title>DEV Community: ZachiNachshon</title>
      <link>https://dev.to/zachinachshon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zachinachshon"/>
    <language>en</language>
    <item>
      <title>Install Traefik Ingress Controller in Kubernetes</title>
      <dc:creator>ZachiNachshon</dc:creator>
      <pubDate>Sat, 01 Jan 2022 17:40:01 +0000</pubDate>
      <link>https://dev.to/zachinachshon/install-traefik-ingress-controller-in-kubernetes-4g53</link>
      <guid>https://dev.to/zachinachshon/install-traefik-ingress-controller-in-kubernetes-4g53</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo695qey4wsbgggguvjnc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo695qey4wsbgggguvjnc.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;&lt;b&gt;Credits:&lt;/b&gt; Logo by &lt;a href="https://traefik.io/" rel="noopener noreferrer"&gt;traefik.io&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Provide load balancing, SSL termination and name-based virtual hosting on a Kubernetes (k3s) cluster using Traefik ingress controller.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
K8s Controller

&lt;ul&gt;
&lt;li&gt;Prepare&lt;/li&gt;
&lt;li&gt;Install&lt;/li&gt;
&lt;li&gt;Uninstall&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Traefik Dashboard

&lt;ul&gt;
&lt;li&gt;Certificate&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Ingress&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Demo Application

&lt;ul&gt;
&lt;li&gt;Prepare&lt;/li&gt;
&lt;li&gt;Install&lt;/li&gt;
&lt;li&gt;Uninstall&lt;/li&gt;
&lt;li&gt;Certificate&lt;/li&gt;
&lt;li&gt;Ingress&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Summary&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The domain referenced in this post is MY_DOMAIN, please change accordingly. If you interested in a local-only work mode, you don’t have to pay for a new domain, just decide on a name and use it. For example, if your desired domain is homelab.com, replace MY_DOMAIN with homelab.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.zachinachshon.com/rpi-installation/" rel="noopener noreferrer"&gt;Setting Up a Raspberry Pi Cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.zachinachshon.com/k3s-installation/" rel="noopener noreferrer"&gt;Install Rancher K3s on Raspberry Pi Cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.zachinachshon.com/helm-installation/" rel="noopener noreferrer"&gt;Installing Helm, Kubernetes Package Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.zachinachshon.com/cert-manager/" rel="noopener noreferrer"&gt;Install Certificate Manager Controller in Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkr5imnnmd344pfqz24b3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkr5imnnmd344pfqz24b3.png" alt="traefik-overview.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is an Ingress Controller?&lt;/strong&gt;&lt;br&gt;
It is an API object that manages external access to a deployed service in a Kubernetes cluster, typically via HTTP/S. It provides load balancing, SSL termination and name-based virtual hosting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why would I want to use an Ingress Controller?&lt;/strong&gt;&lt;br&gt;
These are some of the immediate benefits using an ingress controller:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Simplify the way internal services interact with each other and re-route when required by  changing only the Traefik routing rule. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Instead of relying on an IP address that might change, simply define a hosted name e.g. &lt;code&gt;serviceA.domain.com&lt;/code&gt; to address &lt;code&gt;serviceA&lt;/code&gt; and use that on other internal services and/or from outside the cluster when in need to call &lt;code&gt;serviceA&lt;/code&gt;.&lt;br&gt;Isn't &lt;code&gt;serviceA.domain.com&lt;/code&gt; easier to remember than &lt;code&gt;192.169.200.xxx&lt;/code&gt; ?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allows HTTPS traffic from outside the Kubernetes cluster while terminating encryption and allowing HTTP traffic between services within the cluster&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Load balance traffic between services hosted outside the Kubernetes cluster&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Traefik is using custom cluster resource definitions. These CRDs gets installed via Traefik 2 Helm Chart. For CRDs schema click &lt;a href="https://docs.traefik.io/routing/providers/kubernetes-crd/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2 id="k8s-controller"&gt;K8s Controller&lt;/h2&gt;

&lt;p&gt;Traefik is a Kubernetes controller that manage the access to cluster services by supporting the &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/" rel="noopener noreferrer"&gt;Ingress specification&lt;/a&gt;. It receives requests on behalf of your system and finds out which components are responsible for handling them.&lt;/p&gt;

&lt;h3 id="traefik-prepare"&gt;Prepare&lt;/h3&gt;

&lt;p&gt;We want Kubernetes to create the Traefik pod on the master node. In order to do that, we'll have to label that node and use &lt;code&gt;nodeSelector&lt;/code&gt; attribute when installing Traefik Helm chart.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Get all nodes names and labels&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes &lt;span class="nt"&gt;--show-labels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt; &lt;/p&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Label &lt;code&gt;kmaster&lt;/code&gt; node with &lt;code&gt;node-type=master&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl label nodes kmaster node-type&lt;span class="o"&gt;=&lt;/span&gt;master
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; To remove a label, use the same command with dash after the label name e.g. &lt;code&gt;kubectl label nodes kmaster node-type-&lt;/code&gt;."&lt;/p&gt;
&lt;/blockquote&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Verify that label had been created successfully&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes &lt;span class="nt"&gt;--show-labels&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;node-type
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ol&gt;


&lt;p&gt; &lt;/p&gt;

&lt;h3 id="traefik-install"&gt;Install&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;code&gt;traefik&lt;/code&gt; namespace&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace traefik
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the &lt;code&gt;containous&lt;/code&gt; Helm repository hosting the Traefik charts metadata&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add traefik https://containous.github.io/traefik-helm-chart
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update local Helm chart repository cache&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo update
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Search for latest &lt;code&gt;traefik/traefik&lt;/code&gt; official Helm chart version&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm search repo traefik

&lt;span class="c"&gt;# NAME              CHART VERSION   APP VERSION&lt;/span&gt;
&lt;span class="c"&gt;# traefik/traefik   9.1.1           2.2.8      &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the Traefik Helm chart using the version from previous step&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm upgrade &lt;span class="nt"&gt;--install&lt;/span&gt; traefik &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--namespace&lt;/span&gt; traefik &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; dashboard.enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; rbac.enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; nodeSelector.node-type&lt;span class="o"&gt;=&lt;/span&gt;master &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"additionalArguments={--api.dashboard=true,--log.level=INFO,--providers.kubernetesingress.ingressclass=traefik-internal,--serversTransport.insecureSkipVerify=true}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    traefik/traefik &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--version&lt;/span&gt; 9.1.1
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify installation&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Make sure all traefik deployed pods are running&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;--namespace&lt;/span&gt; traefik

&lt;span class="c"&gt;# Make sure custom resources *.traefik.containo.us were created successfully &lt;/span&gt;
kubectl get crd | &lt;span class="nb"&gt;grep &lt;/span&gt;traefik
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="traefik-uninstall"&gt;Uninstall&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Remove &lt;code&gt;traefik&lt;/code&gt; from the cluster&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm uninstall traefik &lt;span class="nt"&gt;--namespace&lt;/span&gt; traefik
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clear the &lt;code&gt;traefik&lt;/code&gt; namespace&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete namespaces traefik
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2 id="traefik-dashboard"&gt;Traefik Dashboard&lt;/h2&gt;

&lt;p&gt;Dashboard is installed but disabled by default for security reasons. We will want to avoid using the &lt;code&gt;kubectl proxy-forward&lt;/code&gt; option and allow the dashboard via HTTPS with proper TLS/Cert.&lt;/p&gt;

&lt;h3 id="certificate"&gt;Certificate&lt;/h3&gt;

&lt;p&gt;We will create a certificate using &lt;code&gt;cert-manager&lt;/code&gt; to allow accessing the Traefik dashboard via the hosted name &lt;code&gt;traefik.MY_DOMAIN.com&lt;/code&gt; within our home network. Create a self signed certificate as described in &lt;a href="https://blog.zachinachshon.com/cert-manager/#self-signed-certificate" rel="noopener noreferrer"&gt;here&lt;/a&gt; under &lt;code&gt;traefik&lt;/code&gt; namespace.&lt;/p&gt;

&lt;p&gt;Verify that a TLS secret had been created for the certificate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret MY_DOMAIN-com-cert-secret &lt;span class="nt"&gt;--namespace&lt;/span&gt; traefik
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h3 id="dashboard-auth"&gt;Authentication&lt;/h3&gt;

&lt;p&gt;We will create a user / password basic authentication, please read &lt;a href="https://doc.traefik.io/traefik/v1.7/configuration/backends/kubernetes/#authentication" rel="noopener noreferrer"&gt;here&lt;/a&gt; if you wish to use a different method.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Generate a flat-file that stores a username and password for basic authentication&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'

# Change these credentials to your own
export TRAEFIK_UI_USER=admin
export TRAEFIK_UI_PASS=dashboard
export DESTINATION_FOLDER=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;/temp/traefik-ui-creds

# Backup credentials to local files (in case you'll forget them later on)
mkdir -p &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DESTINATION_FOLDER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
echo &lt;/span&gt;&lt;span class="nv"&gt;$TRAEFIK_UI_USER&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DESTINATION_FOLDER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;/traefik-ui-user.txt
echo &lt;/span&gt;&lt;span class="nv"&gt;$TRAEFIK_UI_PASS&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DESTINATION_FOLDER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;/traefik-ui-pass.txt

htpasswd -Bbn &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TRAEFIK_UI_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TRAEFIK_UI_PASS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="sh"&gt;
    &amp;gt; &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DESTINATION_FOLDER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;/htpasswd

unset TRAEFIK_UI_USER TRAEFIK_UI_PASS DESTINATION_FOLDER
&lt;/span&gt;&lt;span class="no"&gt;
EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a Kubernetes secret based on the basic authentication file&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret generic traefik-dashboard-auth-secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/temp/traefik-ui-creds/htpasswd &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; traefik
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="dashboard-ingress"&gt;Ingress&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create an &lt;code&gt;IngressRoute&lt;/code&gt; for accessing the dashboard, make sure to replace &lt;code&gt;MY_DOMAIN&lt;/code&gt; with your domain name&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cat &amp;lt;&amp;lt;EOF | kubectl apply -f -&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik.containo.us/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IngressRoute&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik-dashboard&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;entryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;websecure&lt;/span&gt;
  &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rule&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Host(\`traefik.MY_DOMAIN.com\`) &amp;amp;&amp;amp; (PathPrefix(\`/api\`) || PathPrefix(\`/dashboard\`))&lt;/span&gt;
      &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api@internal&lt;/span&gt;
          &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TraefikService&lt;/span&gt;
      &lt;span class="na"&gt;middlewares&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik-dashboard-auth&lt;/span&gt; &lt;span class="c1"&gt;# Referencing the BasicAuth middleware&lt;/span&gt;
          &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MY_DOMAIN-com-cert-secret&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik.containo.us/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Middleware&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik-dashboard-auth&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;basicAuth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik-dashboard-auth-secret&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check that resources were created successfully&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# IngressRoute&lt;/span&gt;
kubectl describe ingressroute traefik-dashboard &lt;span class="nt"&gt;--namespace&lt;/span&gt; traefik

&lt;span class="c"&gt;# Middleware&lt;/span&gt;
kubectl describe middleware traefik-dashboard-auth &lt;span class="nt"&gt;--namespace&lt;/span&gt; traefik
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check there are no error logs&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl logs &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;--namespace&lt;/span&gt; traefik | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"^traefik"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--namespace&lt;/span&gt; traefik
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set a hosted name &lt;code&gt;traefik.MY_DOMAIN.com&lt;/code&gt; on a client machine (laptop/desktop)&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Edit the file using `sudo /etc/hosts`&lt;/span&gt;
&lt;span class="c"&gt;# Append manualy to the existing k3s hosted name &lt;/span&gt;
111.222.333.444 kmaster, traefik.MY_DOMAIN.com

&lt;span class="c"&gt;# Alternatively, add a new hosted name entry with a one-liner&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"111.222.333.444&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;traefik.MY_DOMAIN.com"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Replace &lt;code&gt;111.222.333.444&lt;/code&gt; with the &lt;code&gt;k3s&lt;/code&gt; master node IP address and &lt;code&gt;MY_DOMAIN&lt;/code&gt; with your domain name.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open browser at &lt;a href="https://traefik.MY_DOMAIN.com/dashboard/" rel="noopener noreferrer"&gt;https://traefik.MY_DOMAIN.com/dashboard/&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you encounter an untrusted certificate warning, please follow the cert-manager post &lt;a href="https://blog.zachinachshon.com/cert-manager#cert-trust" rel="noopener noreferrer"&gt;Trust section&lt;/a&gt; for instructions on how to trust the &lt;code&gt;traefik&lt;/code&gt; certificate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dashboard:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F521pl9abudvgjuir8tj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F521pl9abudvgjuir8tj5.png" alt="err-dashboard-img"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2 id="demo-app"&gt;Demo Application&lt;/h2&gt;

&lt;p&gt;We will deploy an example &lt;code&gt;whoami&lt;/code&gt; application that returns basic information about the client issuing the request. It'll allow us to check that Traefik was deployed successfully by addressing our demo application using &lt;code&gt;whoami.MY_DOMAIN.com&lt;/code&gt;. &lt;/p&gt;

&lt;h3 id="demo-prepare"&gt;Prepare&lt;/h3&gt;

&lt;p&gt;Before deploying our application to the Kubernetes cluster, we will have to define which node(s) are eligible of assigning pods for it. &lt;/p&gt;

&lt;p&gt;We will use the &lt;code&gt;node-type=master&lt;/code&gt; label created at the &lt;code&gt;Prepare&lt;/code&gt; step of the Traefik installation. To assign the &lt;code&gt;whoami&lt;/code&gt; pod(s) to the &lt;code&gt;kmaster&lt;/code&gt; node, we will use the &lt;code&gt;nodeSelector&lt;/code&gt; attribute on a Kubernetes resource (&lt;code&gt;Deployment&lt;/code&gt; / &lt;code&gt;Pod&lt;/code&gt;) with that label.&lt;/p&gt;

&lt;h3 id="demo-install"&gt;Install&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;code&gt;playground&lt;/code&gt; namespace&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace playground
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create Kubernetes &lt;code&gt;Deployment&lt;/code&gt; and &lt;code&gt;Service&lt;/code&gt; with x2 running instances of the &lt;code&gt;whoami&lt;/code&gt; application&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cat &amp;lt;&amp;lt;EOF | kubectl apply -f -&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;whoami&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;playground&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;containous&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;whoami&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;containous&lt;/span&gt;
      &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;whoami&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;containous&lt;/span&gt;
        &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;whoami&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;nodeSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;master"&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;containouswhoami&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;containous/whoami&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;whoami&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;playground&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;containous&lt;/span&gt;
    &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;whoami&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Please refer to the official docs to read more about Kubernetes &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/" rel="noopener noreferrer"&gt;&lt;code&gt;Deployment&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/" rel="noopener noreferrer"&gt;&lt;code&gt;Service&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify both &lt;code&gt;Deployment&lt;/code&gt; and &lt;code&gt;Service&lt;/code&gt; were created successfully&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Deployment should indicate 2/2 running pods &lt;/span&gt;
kubectl get deployments &lt;span class="nt"&gt;-n&lt;/span&gt; playground

&lt;span class="c"&gt;# Service should indicate a cluster IP on port 80 without an external IP&lt;/span&gt;
kubectl get services &lt;span class="nt"&gt;-n&lt;/span&gt; playground

&lt;span class="c"&gt;# Check that there are x2 running pods on kmaster node&lt;/span&gt;
kubectl describe pods &lt;span class="nb"&gt;whoami&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; playground | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Status:"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="demo-uninstall"&gt;Uninstall&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Follow the installation instructions for &lt;code&gt;Deployment&lt;/code&gt;, &lt;code&gt;Service&lt;/code&gt;, &lt;code&gt;IngressRoute&lt;/code&gt;, &lt;code&gt;Middleware&lt;/code&gt; and execute both scripts while replacing the &lt;code&gt;kubectl&lt;/code&gt; action:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Instead of using apply&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | kubectl apply -f -  
...
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Replace with delete&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | kubectl delete -f - 
...
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clear the &lt;code&gt;playground&lt;/code&gt; namespace&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete namespaces playground
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="demo-certificate"&gt;Certificate&lt;/h3&gt;

&lt;p&gt;We will have to create a certificate using &lt;code&gt;cert-manager&lt;/code&gt; to allow accessing the &lt;code&gt;whoami&lt;/code&gt; application using the hosted name &lt;code&gt;whoami.MY_DOMAIN.com&lt;/code&gt; within our home network. Create a self signed certificate as described in &lt;a href="https://blog.zachinachshon.com/cert-manager/#self-signed-certificate" rel="noopener noreferrer"&gt;here&lt;/a&gt; under &lt;code&gt;playground&lt;/code&gt; namespace.&lt;/p&gt;

&lt;p&gt;Verify that a TLS secret had been created for the certificate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret MY_DOMAIN-com-cert-secret &lt;span class="nt"&gt;--namespace&lt;/span&gt; playground
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h3 id="demo-ingress"&gt;Ingress&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create ingress entry points to allow secure communication using &lt;code&gt;HTTPS&lt;/code&gt; and allowing &lt;code&gt;HTTP&lt;/code&gt; with &lt;code&gt;HTTPS&lt;/code&gt; redirect middleware (make sure to replace &lt;code&gt;MY_DOMAIN&lt;/code&gt; with your domain name)&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cat &amp;lt;&amp;lt;EOF | kubectl apply -f -&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik.containo.us/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IngressRoute&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;whoami-https&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;playground&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;entryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;websecure&lt;/span&gt;
  &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Host(\`whoami.MY_DOMAIN.com\`)&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rule&lt;/span&gt;
      &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
      &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;whoami&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MY_DOMAIN-com-cert-secret&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik.containo.us/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IngressRoute&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;whoami&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;playground&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;entryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
  &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Host(\`whoami.MY_DOMAIN.com\`)&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rule&lt;/span&gt;
      &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
      &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# This IngressRoute will be never called due to the redirect middleware&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;whoami&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;middlewares&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https-redirect&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik.containo.us/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Middleware&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https-redirect&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;playground&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redirectScheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https&lt;/span&gt;
    &lt;span class="na"&gt;permanent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify success status of HTTP routers and middlewares using the traefik dashboard&lt;/p&gt;

&lt;p&gt;HTTP Routers:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuetk5uo0aq6mt1vb0cig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuetk5uo0aq6mt1vb0cig.png" alt="traefik-http-routers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;HTTP Middlewares:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ta1tcpjr8564pycp7eu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ta1tcpjr8564pycp7eu.png" alt="traefik-http-middlewares"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set a hosted name &lt;code&gt;whoami.MY_DOMAIN.com&lt;/code&gt; on a client machine (laptop/desktop)&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Edit the file using `sudo /etc/hosts`&lt;/span&gt;
&lt;span class="c"&gt;# Append manualy to the existing k3s hosted name &lt;/span&gt;
111.222.333.444 kmaster, traefik.MY_DOMAIN.com, whoami.MY_DOMAIN.com

&lt;span class="c"&gt;# Alternatively, add a new hosted name entry with a one-liner&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"111.222.333.444&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;whoami.MY_DOMAIN.com"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Replace &lt;code&gt;111.222.333.444&lt;/code&gt;  with the &lt;code&gt;k3s&lt;/code&gt; master node IP address and &lt;code&gt;MY_DOMAIN&lt;/code&gt; with your domain name.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open a web-browser at &lt;a href="https://whoami.MY_DOMAIN.com" rel="noopener noreferrer"&gt;&lt;code&gt;https://whoami.MY_DOMAIN.com&lt;/code&gt;&lt;/a&gt; and check that you can get a response from the service(s)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Open &lt;a href="http://whoami.MY_DOMAIN.com" rel="noopener noreferrer"&gt;&lt;code&gt;http://whoami.MY_DOMAIN.com&lt;/code&gt;&lt;/a&gt; to check the &lt;code&gt;HTTP&lt;/code&gt; -&amp;gt; &lt;code&gt;HTTPS&lt;/code&gt; redirect.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;




&lt;h3 id="summary"&gt;Summary&lt;/h3&gt;

&lt;p&gt;Congratulations on completing a major part of deploying an ingress controller on your Kubernetes cluster ! 💪&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What now?&lt;/strong&gt; Now that you are familiar with how to use Traefik as an ingress controller, you can simplify the inter-services communication within the cluster and expose applications APIs / Web-UI and such via &lt;code&gt;HTTPS&lt;/code&gt; in no time.&lt;/p&gt;

&lt;p&gt;Please leave your comment, suggestion or any other input you think is relevant to this post in the discussion below.&lt;/p&gt;




&lt;p&gt;Like this post?&lt;br&gt;
You can find more by:&lt;/p&gt;

&lt;p&gt;Checking out my blog: &lt;a href="https://blog.zachinachshon.com" rel="noopener noreferrer"&gt;https://blog.zachinachshon.com&lt;/a&gt;&lt;br&gt;
Following me on twitter: &lt;a class="mentioned-user" href="https://dev.to/zachinachshon"&gt;@zachinachshon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! ❤️&lt;/p&gt;

</description>
      <category>traefik</category>
      <category>kubernetes</category>
      <category>k3s</category>
      <category>ingress</category>
    </item>
    <item>
      <title>Install Certificate Manager Controller in Kubernetes</title>
      <dc:creator>ZachiNachshon</dc:creator>
      <pubDate>Sun, 02 May 2021 19:37:09 +0000</pubDate>
      <link>https://dev.to/zachinachshon/install-certificate-manager-controller-in-kubernetes-4h7c</link>
      <guid>https://dev.to/zachinachshon/install-certificate-manager-controller-in-kubernetes-4h7c</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1e7czag0bi7ht3q4f2bc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1e7czag0bi7ht3q4f2bc.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;&lt;b&gt;Credits:&lt;/b&gt; Logo by &lt;a href="https://cert-manager.io/" rel="noopener noreferrer"&gt;cert-manager.io&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Automate the process of issuing public key certificates from multiple sources, ensuring they are valid, up to date and renew before expiration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
K8s Controller

&lt;ul&gt;
&lt;li&gt;Prepare&lt;/li&gt;
&lt;li&gt;Install&lt;/li&gt;
&lt;li&gt;Uninstall&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Self Signed

&lt;ul&gt;
&lt;li&gt;Issuer&lt;/li&gt;
&lt;li&gt;Certificate&lt;/li&gt;
&lt;li&gt;Secrets&lt;/li&gt;
&lt;li&gt;
Trust &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Advanced

&lt;ul&gt;
&lt;li&gt;Share Secrets between Namespaces&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Summary&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This post is a quick start guide for deploying and using &lt;code&gt;cert-manager&lt;/code&gt; on a Kubernetes cluster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/zachinachshon/install-rancher-k3s-on-raspberry-pi-cluster-1i06"&gt;Install Rancher K3s on Raspberry Pi Cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/zachinachshon/installing-helm-kubernetes-package-manager-3fi0"&gt;Installing Helm, Kubernetes Package Manager&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why do we need to worry about certificates?&lt;/strong&gt; When declaring a domain name i.e &lt;code&gt;my-website.domain.com&lt;/code&gt; and addressing it from either internal network and/or public internet, the devices used to perform the call (web browsers, internal services, containers etc..) would require to check its validity. In order to do that, the domain name should have a certificate that is issued and trusted to operate securely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why do we need a certificate manager?&lt;/strong&gt; Certificate validity has its expiration date, which means certificates have to get renewed. It might be a cumbersome task when there are many certificates to handle. This is the reason &lt;code&gt;cert-manager&lt;/code&gt; exists, to help with issuing certificates from a variety of sources, such as &lt;a href="https://letsencrypt.org/" rel="noopener noreferrer"&gt;Let’s Encrypt&lt;/a&gt;, a simple signing key pair or self signed. It will ensure certificates are valid, up to date and attempt to renew certificates at a configured time before expiry.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The domain referenced in this post is &lt;code&gt;MY_DOMAIN&lt;/code&gt;, please change accordingly. If you interested in a local-only work mode, you don't have to pay for a new domain, just decide on a name and use it. For example, if your desired domain is &lt;code&gt;homelab.com&lt;/code&gt;, replace &lt;code&gt;MY_DOMAIN&lt;/code&gt; with &lt;code&gt;homelab&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2 id="k8s-controller"&gt;K8s Controller&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;cert-manager&lt;/code&gt; is a Kubernetes controller that manage the certificate aspect of a cluster state. It looks after the state of certificates on a specific cluster and issue new ones or request to renew existing ones before expiration.&lt;/p&gt;

&lt;h3 id="prepare"&gt;Prepare&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; This preparation step is relevant only if you wish to deploy the &lt;code&gt;cert-manager&lt;/code&gt; pod on a specific cluster node such as master for example, otherwise please continue to the next Install step.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We want Kubernetes to create the &lt;code&gt;cert-manager&lt;/code&gt; pod on the master node. In order to do that, we'll have to label that node and use &lt;code&gt;nodeSelector&lt;/code&gt; attribute when installing &lt;code&gt;cert-manager&lt;/code&gt; Helm chart.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Get all nodes names and labels&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes &lt;span class="nt"&gt;--show-labels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Label &lt;code&gt;kmaster&lt;/code&gt; node with &lt;code&gt;node-type=master&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl label nodes kmaster node-type&lt;span class="o"&gt;=&lt;/span&gt;master
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I'll refer to the master node named &lt;code&gt;kmaster&lt;/code&gt; as specified in the &lt;a href="https://dev.to/zachinachshon/setting-up-a-raspberry-pi-cluster-1oa0"&gt;RPi cluster installation post&lt;/a&gt;, replace the name with the one you have assigned to the master node on your Kubernetes cluster.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify that label had been created successfully&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes &lt;span class="nt"&gt;--show-labels&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;node-type
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="install"&gt;Install&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;code&gt;cert-manager&lt;/code&gt; namespace&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace cert-manager
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Disable resource validation on the &lt;code&gt;cert-manager&lt;/code&gt; namespace&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl label namespace cert-manager certmanager.k8s.io/disable-validation&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;cert-manager&lt;/code&gt; deploys a webhook component to perform resource validations on &lt;code&gt;Issuer&lt;/code&gt;, &lt;code&gt;ClusterIssuer&lt;/code&gt; and &lt;code&gt;Certificate&lt;/code&gt;. This webhook shouldn't run on the same namespace the &lt;code&gt;cert-manager&lt;/code&gt; is deployed on.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the required Helm repository&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add jetstack https://charts.jetstack.io
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update your local Helm chart repository cache&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo update
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Search for latest &lt;code&gt;jetstack/cert-manager&lt;/code&gt; official Helm chart version&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm search repo cert-manager

&lt;span class="c"&gt;# NAME                      CHART VERSION     APP VERSION&lt;/span&gt;
&lt;span class="c"&gt;# jetstack/cert-manager     v1.2.0            v1.2.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the &lt;code&gt;cert-manager&lt;/code&gt; Helm chart using the version from previous step&lt;/p&gt;

&lt;p&gt;Without node affinity:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm upgrade &lt;span class="nt"&gt;--install&lt;/span&gt; cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--namespace&lt;/span&gt; cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--version&lt;/span&gt; v1.2.0 &lt;span class="se"&gt;\&lt;/span&gt;
    jetstack/cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;installCRDs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;p&gt;With node affinity (if you have followed the &lt;code&gt;Prepare&lt;/code&gt; step):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm upgrade &lt;span class="nt"&gt;--install&lt;/span&gt; cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--namespace&lt;/span&gt; cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--version&lt;/span&gt; v1.2.0 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; nodeSelector.node-type&lt;span class="o"&gt;=&lt;/span&gt;master &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; webhook.nodeSelector.node-type&lt;span class="o"&gt;=&lt;/span&gt;master &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; cainjector.nodeSelector.node-type&lt;span class="o"&gt;=&lt;/span&gt;master &lt;span class="se"&gt;\&lt;/span&gt;
    jetstack/cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;installCRDs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Make sure you are running on Kubernetes &amp;gt; &lt;code&gt;v1.19&lt;/code&gt; to have the &lt;code&gt;installCRDs&lt;/code&gt; flag available.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify installation&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Make sure all cert-manager deployed pods are running&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;--namespace&lt;/span&gt; cert-manager

&lt;span class="c"&gt;# Make sure custom resources *.cert-manager.io were created successfully &lt;/span&gt;
kubectl get crd | &lt;span class="nb"&gt;grep &lt;/span&gt;cert-manager

&lt;span class="c"&gt;# Verify that ClusterIssuer is non-namespaced scoped ('false')&lt;/span&gt;
&lt;span class="c"&gt;# so it can be used to issue Certificates across all namespaces&lt;/span&gt;
kubectl api-resources | &lt;span class="nb"&gt;grep &lt;/span&gt;clusterissuers
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="uninstall"&gt;Uninstall&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Remove &lt;code&gt;cert-manager&lt;/code&gt; from the cluster&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm uninstall cert-manager &lt;span class="nt"&gt;--namespace&lt;/span&gt; cert-manager
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clear the namespace&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete namespaces cert-manager
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2 id="self-signed"&gt;Self Signed&lt;/h2&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What?&lt;/strong&gt; The self signed issuer does not represent a &lt;a href="https://en.wikipedia.org/wiki/Certificate_authority" rel="noopener noreferrer"&gt;certificate authority&lt;/a&gt; as such, but instead denotes that certificates will be signed through “self signing” using a given private key. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; This means that the provided private key of the resulting certificate will be used to sign its own certificate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When?&lt;/strong&gt; This Issuer type is useful for bootstrapping the CA certificate key pair for some Private Key Infrastructure (PKI), or for otherwise creating simple certificates. Clients consuming these certificates have no way to trust this certificate since there is no CA signer apart from itself, and as such, would be forced to trust the certificate as is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who?&lt;/strong&gt; Clients consuming these certificates could be services deployed to our local Kubernetes cluster that are exposed from outside the cluster such as Kubernetes dashboard, Jenkins UI, Private Docker Registry UI etc..&lt;/p&gt;

&lt;h3 id="self-signed-issuer"&gt;Issuer&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a certificate &lt;code&gt;ClusterIssuer&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cat &amp;lt;&amp;lt;EOF | kubectl apply -f -&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cert-manager.io/v1alpha2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIssuer&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MY_DOMAIN-ca-issuer&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selfSigned&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; &lt;code&gt;cert-manager&lt;/code&gt; issues certificates through an &lt;code&gt;Issuer&lt;/code&gt;. The &lt;code&gt;Issuer&lt;/code&gt; can issue certificates for the namespace it is created on, but a &lt;code&gt;ClusterIssuer&lt;/code&gt; can create certificates for any namespace.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Wait for status to become &lt;code&gt;Ready&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get clusterissuers MY_DOMAIN-ca-issuer &lt;span class="nt"&gt;-o&lt;/span&gt; wide
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="self-signed-certificate"&gt;Certificate&lt;/h3&gt;

&lt;p&gt;When creating a new certificate, make sure to create one on a named namespace. It'll get verified by &lt;code&gt;cert-manager&lt;/code&gt; even-though it exists on a different namespace since we're referencing a &lt;code&gt;ClusterIssuer&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Replace &lt;code&gt;MY_NAMESPACE&lt;/code&gt; with the namespace of your choise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;strong&gt;&lt;em&gt;namespaced&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;X.509&lt;/code&gt; certificate (check &lt;a href="https://cert-manager.io/docs/concepts/certificate/" rel="noopener noreferrer"&gt;here&lt;/a&gt; for official schema)&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cat &amp;lt;&amp;lt;EOF | kubectl apply -f -&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cert-manager.io/v1alpha2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Certificate&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MY_DOMAIN-com-cert&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MY_NAMESPACE&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MY_DOMAIN-com-cert-secret&lt;/span&gt;
  &lt;span class="na"&gt;isCA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;commonName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*.MY_DOMAIN.com'&lt;/span&gt;
  &lt;span class="na"&gt;organization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MY_DOMAIN&lt;/span&gt;
  &lt;span class="na"&gt;dnsNames&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MY_DOMAIN.com&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*.MY_DOMAIN.com'&lt;/span&gt;
  &lt;span class="na"&gt;keySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2048&lt;/span&gt;
  &lt;span class="na"&gt;keyAlgorithm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rsa&lt;/span&gt;
  &lt;span class="na"&gt;issuerRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MY_DOMAIN-ca-issuer&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIssuer&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check certificate status is &lt;code&gt;Issued&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe certificate MY_DOMAIN-com-cert &lt;span class="nt"&gt;--namespace&lt;/span&gt; MY_NAMESPACE
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check that secret &lt;code&gt;MY_DOMAIN-com-cert-secret&lt;/code&gt; was created successfully&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret &lt;span class="nt"&gt;--namespace&lt;/span&gt; MY_NAMESPACE
kubectl get secret MY_DOMAIN-com-cert-secret &lt;span class="nt"&gt;-o&lt;/span&gt; yaml &lt;span class="nt"&gt;--namespace&lt;/span&gt; MY_NAMESPACE
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(Optional)&lt;/strong&gt;: When in need to delete a certificate&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Delete certificate&lt;/span&gt;
kubectl delete certificate MY_DOMAIN-com-cert &lt;span class="nt"&gt;--namespace&lt;/span&gt; MY_NAMESPACE

&lt;span class="c"&gt;# Delete the auto generated secret&lt;/span&gt;
kubectl delete secret MY_DOMAIN-com-cert-secret &lt;span class="nt"&gt;--namespace&lt;/span&gt; MY_NAMESPACE
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="cert-secrets"&gt;Secrets&lt;/h3&gt;

&lt;p&gt;Follow these instructions to export the &lt;code&gt;cert-manager&lt;/code&gt; generated certificate secrets as local files.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Sometimes there is a need to use the secret values from outside the Kubernetes cluster. Such example is setting the &lt;code&gt;cert_file&lt;/code&gt; as a trusted certificate on a client machine (laptop / desktop), specially when using a self signed certificate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a local destination folder&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/temp/MY_NAMESPACE/cert-secrets
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MY_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;insert-domain-name-here&amp;gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MY_NAMESPACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;insert-namespace-here&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Export the certificate secrets&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;cert_file&lt;/strong&gt; - client certificate path used for authentication&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MY_DOMAIN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-com-cert-secret&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MY_NAMESPACE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.tls\.crt}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/temp/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MY_NAMESPACE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/cert-secrets/cert_file.crt
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;key_file&lt;/strong&gt; - client key path used for authentication&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MY_DOMAIN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-com-cert-secret&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MY_NAMESPACE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.tls\.key}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/temp/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MY_NAMESPACE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/cert-secrets/key_file.key
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ca_file&lt;/strong&gt; - CA certificate path used to verify the remote server cert file&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MY_DOMAIN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-com-cert-secret&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MY_NAMESPACE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.ca\.crt}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/temp/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MY_NAMESPACE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/cert-secrets/ca_file.crt
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check that x3 secrets exported successfully&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lah&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/temp/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MY_NAMESPACE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/cert-secrets
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clear exported variables&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;unset &lt;/span&gt;MY_DOMAIN MY_NAMESPACE
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="cert-trust"&gt;Trust&lt;/h3&gt;

&lt;p&gt;When addressing a Kubernetes ingress controller resource that had been signed with a self signed certificate secret, clients such as web-browsers would warn us of an invalid certificate authority or invalid certificate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ERR_CERT_AUTHORITY_INVALID&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flh9ijea1nciv53xmns1q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flh9ijea1nciv53xmns1q.png" alt="err-cert-authority-invalid"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ERR_CERT_INVALID&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3ll5h42k5xx3nd6rklu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3ll5h42k5xx3nd6rklu.png" alt="err-cert-invalid"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Since we are the ones that created the certificate we would like to tell our clients (laptop/desktop) to trust it. This is how you trust a certificate on &lt;strong&gt;macOS&lt;/strong&gt;, for other operating systems please refer to official documentation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the certificate file in macOS &lt;code&gt;Keychain Access&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;CLI: &lt;code&gt;open $HOME/temp/${MY_NAMESPACE}/cert-secrets/cert_file.crt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Other: double click on the &lt;code&gt;cert_file.crt&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Double click on the &lt;code&gt;*.MY_DOMAIN.com&lt;/code&gt; certificate and expand the &lt;code&gt;Trust&lt;/code&gt; section&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Within the &lt;code&gt;When using this certificate&lt;/code&gt; select &lt;code&gt;Always Trust&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open a web-browser and navigate to one of the domain names using the self signed certificate and verify that there are no errors&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2 id="advanced"&gt;Advanced&lt;/h2&gt;

&lt;p&gt;This section contains non conventional tip &amp;amp; tricks, use it cautiously.&lt;/p&gt;

&lt;h3 id="namespace-secrets"&gt;Share Secrets between Namespaces&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;When?&lt;/strong&gt; Use when in need to copy a &lt;code&gt;cert-manager&lt;/code&gt; generated certificate secret to a different namespace than the one it was created on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; Useful when you need to reference the secret in some kind of ingress controller under its authentication spec which exists on a different namespace.&lt;/p&gt;

&lt;p&gt;In order to solve this limitation we simple need to copy the secret to a different namespace as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret MY_DOMAIN-com-cert-secret &lt;span class="nt"&gt;-n&lt;/span&gt; SOURCE_NAMESPACE &lt;span class="nt"&gt;-o&lt;/span&gt; yaml &lt;span class="se"&gt;\&lt;/span&gt;
| &lt;span class="nb"&gt;sed &lt;/span&gt;s/&lt;span class="s2"&gt;"namespace: SOURCE_NAMESPACE"&lt;/span&gt;/&lt;span class="s2"&gt;"namespace: DESTINATION-NAMESPACE"&lt;/span&gt;/&lt;span class="se"&gt;\&lt;/span&gt;
| kubectl apply &lt;span class="nt"&gt;-n&lt;/span&gt; DESTINATION-NAMESPACE &lt;span class="nt"&gt;-f&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; It is not advised to copy secrets manually since &lt;code&gt;cert-manager&lt;/code&gt; would lose track of the secret as it was copied manually and when the time to renew is due, the secret would become invalid.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;

&lt;p&gt;This post was a sneak peak on how to create certificates and manage their state using &lt;code&gt;cert-manager&lt;/code&gt; Kubernetes controller. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What now?&lt;/strong&gt; Now that you have a local self signed certificate, you can continue to create an ingress controller and expose internal web services within your home network such as Kubernetes dashboard, Jenkins UI, your web application UI with their own virtual hosted names i.e. &lt;code&gt;kubernetes.MY_DOMAIN.com&lt;/code&gt;, &lt;code&gt;jenkins.MY_DOMAIN.com&lt;/code&gt;,  &lt;code&gt;mydashboard.MY_DOMAIN.com&lt;/code&gt; etc..&lt;/p&gt;

&lt;p&gt;Please leave your comment, suggestion or any other input you think is relevant to this post in the discussion below.&lt;/p&gt;




&lt;p&gt;Like this post?&lt;br&gt;
You can find more by:&lt;/p&gt;

&lt;p&gt;Checking out my blog: &lt;a href="https://blog.zachinachshon.com" rel="noopener noreferrer"&gt;https://blog.zachinachshon.com&lt;/a&gt;&lt;br&gt;
Following me on twitter: &lt;a class="mentioned-user" href="https://dev.to/zachinachshon"&gt;@zachinachshon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! ❤️&lt;/p&gt;

</description>
      <category>certificate</category>
      <category>kubernetes</category>
      <category>certmanager</category>
      <category>ca</category>
    </item>
    <item>
      <title>Installing Helm, Kubernetes Package Manager</title>
      <dc:creator>ZachiNachshon</dc:creator>
      <pubDate>Sun, 02 May 2021 07:22:28 +0000</pubDate>
      <link>https://dev.to/zachinachshon/installing-helm-kubernetes-package-manager-3fi0</link>
      <guid>https://dev.to/zachinachshon/installing-helm-kubernetes-package-manager-3fi0</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgs3grpue1pw51as7jkfu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgs3grpue1pw51as7jkfu.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;&lt;b&gt;Credits:&lt;/b&gt; Logo by &lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;helm.sh&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Learn how to configure the deployment of multiple Kubernetes resources as a single unit using Helm package manager.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Helm CLI

&lt;ul&gt;
&lt;li&gt;
Install &lt;/li&gt;
&lt;li&gt;Uninstall&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Charts

&lt;ul&gt;
&lt;li&gt;Search&lt;/li&gt;
&lt;li&gt;Install&lt;/li&gt;
&lt;li&gt;Upgrade&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Summary&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This post is a &lt;em&gt;TL;DR&lt;/em&gt; for sharing basic Helm v3 usage which is required for the reader just to get started, for additional information please follow the &lt;a href="https://helm.sh/docs/" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h1 id="helm-cli"&gt;Helm-CLI&lt;/h1&gt;

&lt;p&gt;Mostly &lt;em&gt;TL;DRs&lt;/em&gt; for installing the Helm CLI.&lt;/p&gt;

&lt;h3 id="install"&gt;Install&lt;/h3&gt;

&lt;p&gt;Install using your favorite package manager, consult with the &lt;a href="https://helm.sh/docs/intro/install/" rel="noopener noreferrer"&gt;official docs&lt;/a&gt; for additional information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# tl;dr for macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;helm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3 id="uninstall"&gt;Uninstall&lt;/h3&gt;

&lt;p&gt;Follow the removal instructions of the package manager you've used previously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# tl;dr for macOS&lt;/span&gt;
brew uninstall helm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1 id="charts"&gt;Charts&lt;/h1&gt;

&lt;p&gt;Helm Charts are the actual &lt;em&gt;"packages"&lt;/em&gt; managed by Helm. A chart is a just another YAML file which consists of custom attributes that eventually gets converted into Kubernetes YAML resources and deployed into the cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; Instead of managing multiple resources on our own and their respective Kubernetes YAML resource files, we can wrap them all into a single Helm chart and address them as a unit, meaning, install/update/uninstall using a single command. Charts can go from a simple hello-world application to a fully blown web application i.e servers, databases, cache etc..&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On the following sections, I'll use &lt;code&gt;traefik&lt;/code&gt; proxy as an example Helm chart.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id="charts-search"&gt;Search&lt;/h3&gt;

&lt;p&gt;In order to be able to search for charts we'll have to find its respective Helm repository that returns its metadata. Search in &lt;a href="https://artifacthub.io/" rel="noopener noreferrer"&gt;artifacthub.io&lt;/a&gt; for the Helm repository for the chart that you wish to use.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;We'll add the &lt;code&gt;containous&lt;/code&gt; Helm repository hosting the &lt;code&gt;traefik&lt;/code&gt; charts metadata&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add traefik https://containous.github.io/traefik-helm-chart
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update local Helm chart repository cache&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo update
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Search for a specific chart by name&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm search repo traefik

&lt;span class="c"&gt;# NAME              CHART VERSION   APP VERSION&lt;/span&gt;
&lt;span class="c"&gt;# traefik/traefik   9.1.1           2.2.8   &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2 id="charts-install"&gt;Install&lt;/h2&gt;

&lt;p&gt;Install a Helm chart directly from the command line. For additional installation options read &lt;a href="https://helm.sh/docs/helm/helm_install/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;traefik &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--namespace&lt;/span&gt; traefik &lt;span class="se"&gt;\&lt;/span&gt;
    traefik/traefik &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--version&lt;/span&gt; 9.1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; It is possible to use the same Helm command for both install &amp;amp; upgrade by supplying the flag &lt;code&gt;--install&lt;/code&gt;. If a release doesn't already exist by the supplied name, an install command would trigger.&lt;br&gt;Replace &lt;code&gt;helm install&lt;/code&gt; with &lt;code&gt;helm upgrade --install&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id="charts-upgrade"&gt;Upgrade&lt;/h3&gt;

&lt;p&gt;Upgrade or override existing values of the previously deployed chart with a new chart release. For additional upgrade options read &lt;a href="https://helm.sh/docs/helm/helm_upgrade/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm upgrade traefik &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--namespace&lt;/span&gt; traefik &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"additionalArguments={--log.level=DEBUG}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    traefik/traefik &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--version&lt;/span&gt; 9.1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When updating partial attributes on a pre-installed Helm chart, the Kubernetes resource pods which relates to these specific attributes would get created anew to apply the new changes.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1 id="summary"&gt;Summary&lt;/h1&gt;

&lt;p&gt;This post is just the tip of the iceberg regarding Helm charts possibilities, please refer to the official &lt;a href="https://helm.sh/docs/" rel="noopener noreferrer"&gt;Helm docs&lt;/a&gt; for further reading.&lt;/p&gt;

&lt;p&gt;Please leave your comment, suggestion or any other input you think is relevant to this post in the discussion below.&lt;/p&gt;




&lt;p&gt;Like this post?&lt;br&gt;
You can find more by:&lt;/p&gt;

&lt;p&gt;Checking out my blog: &lt;a href="https://blog.zachinachshon.com" rel="noopener noreferrer"&gt;https://blog.zachinachshon.com&lt;/a&gt;&lt;br&gt;
Following me on twitter: &lt;a class="mentioned-user" href="https://dev.to/zachinachshon"&gt;@zachinachshon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! ❤️&lt;/p&gt;

</description>
      <category>helm</category>
      <category>charts</category>
      <category>kubernetes</category>
      <category>packagemanager</category>
    </item>
    <item>
      <title>Install Rancher K3s on Raspberry Pi Cluster</title>
      <dc:creator>ZachiNachshon</dc:creator>
      <pubDate>Mon, 05 Apr 2021 19:55:23 +0000</pubDate>
      <link>https://dev.to/zachinachshon/install-rancher-k3s-on-raspberry-pi-cluster-1i06</link>
      <guid>https://dev.to/zachinachshon/install-rancher-k3s-on-raspberry-pi-cluster-1i06</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flmqakct3mglc9iqwj2wt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flmqakct3mglc9iqwj2wt.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;&lt;b&gt;Credits:&lt;/b&gt; Logo by &lt;a href="https://cncf-branding.netlify.app/projects/k3s/" rel="noopener noreferrer"&gt;cncf-branding&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Install a Rancher Labs Kubernetes distribution (k3s) on a Raspberry Pi cluster.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Master Server

&lt;ul&gt;
&lt;li&gt;
Install &lt;/li&gt;
&lt;li&gt;
Uninstall &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Worker Node

&lt;ul&gt;
&lt;li&gt;
Join a Cluster &lt;/li&gt;
&lt;li&gt;
Uninstall &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Utilities

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kubectl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k9s&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Summary&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This post refers to laptop / desktop as &lt;strong&gt;&lt;em&gt;client machines&lt;/em&gt;&lt;/strong&gt;. These are the clients used to connect to the Raspberry Pi master / worker nodes remotely. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/zachinachshon/setting-up-a-raspberry-pi-cluster-1oa0"&gt;Setting Up a Raspberry Pi Cluster&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6irzgkhqmi4jym5rhs6q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6irzgkhqmi4jym5rhs6q.png" alt="rpi-cluster-numbered"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h1 id="k3s-master-server"&gt;Master Server&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;What is a Kubernetes master node?&lt;/strong&gt; A master node is a server that controls and manages a set of worker nodes, in our case it is the Raspberry Pi that controls the rest of the Raspberry Pi(s) on our cluster.&lt;/p&gt;

&lt;h2 id="k3s-master-install"&gt;Install&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;SSH into the Raspberry Pi server that is intended to operate as the Kubernetes master. It should be the one named &lt;code&gt;kmaster&lt;/code&gt; as instructed by this &lt;a href="https://dev.to/zachinachshon/setting-up-a-raspberry-pi-cluster-1oa0#rpi-configuration"&gt;post&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Connect to RPi server that operates as the k8s master&lt;/span&gt;
ssh pi@kmaster
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to install a plain version of &lt;code&gt;k3s&lt;/code&gt; without &lt;code&gt;traefik&lt;/code&gt; load balancer and &lt;code&gt;k8s-dashboard&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sfL&lt;/span&gt; https://get.k3s.io | &lt;span class="nv"&gt;INSTALL_K3S_EXEC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;" --no-deploy traefik --no-deploy kubernetes-dashboard"&lt;/span&gt;  sh -
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We will install a plain version of &lt;code&gt;k3s&lt;/code&gt; without Traefik load balancer and/or Kubernetes dashboard. These should be covered by other dedicated blog posts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify that &lt;code&gt;k3s&lt;/code&gt; was installed successfully. Run the following commands from within the RPi master server&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check for status - active (running)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status k3s

&lt;span class="c"&gt;# Check for status - Ready&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;kubectl get nodes

&lt;span class="c"&gt;# Optional - check that there are no error logs&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/syslog
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(Optional):&lt;/strong&gt; Run when in need to restart &lt;code&gt;k3s&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Restart k3s&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart k3s
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;k3s&lt;/code&gt; service is automatically started and restarted during installation. The install script will install &lt;code&gt;k3s&lt;/code&gt; and additional utilities, such as &lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;crictl&lt;/code&gt;, &lt;code&gt;k3s-killall.sh&lt;/code&gt;, and &lt;code&gt;k3s-uninstall.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; During installation &lt;code&gt;kubectl&lt;/code&gt; on the master server will be aliased to the command &lt;code&gt;k3s kubectl&lt;/code&gt; so that we can use the pre-packaged version of &lt;code&gt;kubectl&lt;/code&gt;. &lt;code&gt;k3s&lt;/code&gt; uses a container runtime called &lt;code&gt;containerd&lt;/code&gt; directly (no &lt;code&gt;docker&lt;/code&gt;), interact using &lt;code&gt;crictl&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2 id="k3s-master-uninstall"&gt;Uninstall&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;SSH into the &lt;code&gt;k3s&lt;/code&gt; master server&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh pi@kmaster
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Uninstall &lt;code&gt;k3s&lt;/code&gt; by executing the following script:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/local/bin/k3s-uninstall.sh
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Rancher &lt;code&gt;k3s&lt;/code&gt; service configuration can be found at &lt;code&gt;/etc/rancher/k3s/k3s.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Container runtime &lt;code&gt;containerd&lt;/code&gt; configuration can be found at &lt;code&gt;/var/lib/rancher/k3s/agent/etc/containerd/config.toml&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h1 id="k3s-worker-node"&gt;Worker Node&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;What is a Kubernetes worker node?&lt;/strong&gt; These are Raspberry Pi servers that act as workload runtimes i.e. run our applications, jobs, whatever we require them to run but they aren't the ones that manage the cluster, just the ones that &lt;em&gt;"get the job done"&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id="k3s-worker-join"&gt;Join a Cluster&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Extract the &lt;code&gt;k3s&lt;/code&gt; join cluster token&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# SSH to the RPi master server&lt;/span&gt;
ssh pi@kmaster

&lt;span class="c"&gt;# Extract the join token&lt;/span&gt;
&lt;span class="nb"&gt;sudo cat&lt;/span&gt; /var/lib/rancher/k3s/server/node-token

&lt;span class="c"&gt;# Alternatively, you can run this on-liner directly from a client machine&lt;/span&gt;
ssh pi@kmaster &lt;span class="s2"&gt;"sudo cat /var/lib/rancher/k3s/server/node-token"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Find the &lt;code&gt;k3s&lt;/code&gt; master server IP address that is assigned to &lt;code&gt;kmaster&lt;/code&gt;, either from the server itself or from a client machine if you've followed this &lt;a href="https://dev.to/zachinachshon/setting-up-a-raspberry-pi-cluster-1oa0#rpi-configuration"&gt;post&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# From the RPi master server&lt;/span&gt;
ip addr show eth0 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"inet&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;/ &lt;span class="nt"&gt;-f1&lt;/span&gt;

&lt;span class="c"&gt;# Alternatively, from a client machine&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/hosts | &lt;span class="nb"&gt;grep &lt;/span&gt;kmaster | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SSH into a Raspberry Pi server intended to be used as a Kubernetes worker node. It would be the one named &lt;code&gt;knode&amp;lt;number&amp;gt;&lt;/code&gt; as instructed on this &lt;a href="https://dev.to/zachinachshon/setting-up-a-raspberry-pi-cluster-1oa0#rpi-configuration"&gt;post&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Connect to a k3s worker node&lt;/span&gt;
ssh pi@knode&amp;lt;number&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to install &lt;code&gt;k3s-agent&lt;/code&gt; and join the worker node to an existing cluster&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Replace MASTER-IP-ADDRESS with the master server IP address from previous step&lt;/span&gt;
&lt;span class="c"&gt;# Replace JOIN-TOKEN with the join token from previous step&lt;/span&gt;
curl &lt;span class="nt"&gt;-sfL&lt;/span&gt; http://get.k3s.io | &lt;span class="nv"&gt;K3S_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://&amp;lt;MASTER-IP-ADDRESS&amp;gt;:6443 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;K3S_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;JOIN-TOKEN&amp;gt; sh -
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify that &lt;code&gt;k3s-agent&lt;/code&gt; was installed successfully. Run the following commands from within the RPi worker server&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check for status - active (running)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status k3s-agent

&lt;span class="c"&gt;# Optional - check that there are no error logs&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/syslog
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Repeat the above steps for every Raspberry Pi board intended to be used as a Kubernetes worker node&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2 id="k3s-worker-uninstall"&gt;Uninstall&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;SSH into the &lt;code&gt;k3s&lt;/code&gt; worker node&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh pi@knode&amp;lt;number&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Uninstall &lt;code&gt;k3s-agent&lt;/code&gt; by executing the following script:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/local/bin/k3s-agent-uninstall.sh
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h1 id="k3s-utilities"&gt;Utilities&lt;/h1&gt;

&lt;p&gt;These are common utilities that should get installed on client machines to interacts with &lt;code&gt;k3s&lt;/code&gt; master server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why do I need to install them?&lt;/strong&gt;&lt;br&gt;
You'll want to interacts with Kubernetes in order to deploy services, execute Helm charts and/or use utilities that grant you cluster visibility.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you are planning to interact with &lt;code&gt;k3s&lt;/code&gt; on a CI environment, make sure that the agent image you are using in the pipeline includes utilities such as &lt;code&gt;kubectl&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2 id="k3s-kubectl"&gt;kubectl&lt;/h2&gt;

&lt;p&gt;Install &lt;code&gt;kubectl&lt;/code&gt;, a command-line-interface tool that allows you to run commands against a remote &lt;code&gt;k3s&lt;/code&gt; cluster.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;On a client machine, create a new empty &lt;code&gt;k3s&lt;/code&gt; config file&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/k3s 
&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/k3s/config
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/k3s/config  &lt;span class="c"&gt;# Set limited user permissions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt; &lt;/p&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Copy the &lt;code&gt;k3s&lt;/code&gt; cluster configuration from the RPi master server&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh pi@kmaster &lt;span class="s2"&gt;"sudo cat /etc/rancher/k3s/k3s.yaml"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/k3s/config
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt; &lt;/p&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Edit the &lt;code&gt;k3s&lt;/code&gt; config file on the client machine and change the remote IP address of the &lt;code&gt;k3s&lt;/code&gt; master from &lt;code&gt;localhost/127.0.0.1&lt;/code&gt; to &lt;code&gt;kmaster&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Edit master config&lt;/span&gt;
vim &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/k3s/config

&lt;span class="c"&gt;# Search for the 'server' attribute located in - &lt;/span&gt;
&lt;span class="c"&gt;# clusters:&lt;/span&gt;
&lt;span class="c"&gt;# - cluster:&lt;/span&gt;
&lt;span class="c"&gt;#   server: https://127.0.0.1:6443 or https://localhost:6443&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Change 'server' value to https://kmaster:6443&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Make sure &lt;code&gt;kmaster&lt;/code&gt; is properly defined as a host name in &lt;code&gt;/etc/hosts&lt;/code&gt;, otherwise - use the RPi master server IP address.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Install &lt;code&gt;kubectl&lt;/code&gt; as described in the &lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# tl;dr - macOS only&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;kubectl

&lt;span class="c"&gt;# Verify client version&lt;/span&gt;
kubectl version &lt;span class="nt"&gt;--client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt; &lt;/p&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Export &lt;code&gt;k3s&lt;/code&gt; config file path as &lt;code&gt;KUBECONFIG&lt;/code&gt; environment variable and by doing that set the &lt;code&gt;kubectl&lt;/code&gt; context to use the RPi &lt;code&gt;k3s&lt;/code&gt; cluster&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/k3s/config
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Add the export command into a &lt;code&gt;.bash_profile&lt;/code&gt; / &lt;code&gt;.bashrc&lt;/code&gt; file. This way every new shell session would have the &lt;code&gt;k3s&lt;/code&gt; cluster config set as the &lt;code&gt;kubectl&lt;/code&gt; active context. &lt;em&gt;&lt;strong&gt;Optional:&lt;/strong&gt; check &lt;a href="https://dev.to/zachinachshon/configure-local-environment-using-dotfiles-1km0"&gt;this post&lt;/a&gt; to manage your dotfiles in style.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Verify that &lt;code&gt;kubectl&lt;/code&gt; was installed properly and can communicate with the RPi master server&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes

&lt;span class="c"&gt;# Expect the following respones as success:&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# NAME      STATUS   ROLES                  AGE   VERSION&lt;/span&gt;
&lt;span class="c"&gt;# knodeX    Ready    &amp;lt;none&amp;gt;                 10m   v1.20.4+k3s1&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="c"&gt;# knode1    Ready    &amp;lt;none&amp;gt;                 23m   v1.20.4+k3s1&lt;/span&gt;
&lt;span class="c"&gt;# kmaster   Ready    control-plane,master   52m   v1.20.4+k3s1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt; &lt;/p&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;(Optional)&lt;/strong&gt;: read &lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/" rel="noopener noreferrer"&gt;here&lt;/a&gt; for additional information about &lt;code&gt;kubectl&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2 id="k3s-k9s"&gt;k9s&lt;/h2&gt;

&lt;p&gt;Install &lt;code&gt;k9s&lt;/code&gt;, a terminal UI that interacts with the &lt;code&gt;k3s&lt;/code&gt; cluster, increase velocity by saving you from typing repetitive commands and/or the need to alias common ones. It allows easy navigation, observation and management - all in one package.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install as instructed on the official repository &lt;a href="https://github.com/derailed/k9s" rel="noopener noreferrer"&gt;docs&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# tl;dr for macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;k9s
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Make sure &lt;code&gt;$KUBECONFIG&lt;/code&gt; is properly defined and set to &lt;code&gt;k3s&lt;/code&gt; config path.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In case you are working on a single cluster and it is the &lt;code&gt;default&lt;/code&gt; one, move to the next step, otherwise, if you are working on multiple clusters and/or using a cluster which isn't named &lt;code&gt;default&lt;/code&gt;, change the &lt;code&gt;currentContext&lt;/code&gt; and &lt;code&gt;currentCluster&lt;/code&gt; attributes on the &lt;code&gt;k9s&lt;/code&gt; config file to the proper cluster values.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;k9s&lt;/code&gt; configuration file can be found at &lt;code&gt;$HOME/.k9s/config.yml&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;k9s&lt;/code&gt; on a fresh shell session and verify that you can connect the &lt;code&gt;k3s&lt;/code&gt; cluster successfully &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;(Optional)&lt;/strong&gt;: read &lt;a href="https://k9scli.io/" rel="noopener noreferrer"&gt;here&lt;/a&gt; for additional information about &lt;code&gt;k9s&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;




&lt;h3 id="summary"&gt;Summary&lt;/h3&gt;

&lt;p&gt;Well done for successfully installing a Kubernetes cluster on top of your Raspberry Pi cluster ! 👏&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What now?&lt;/strong&gt; Check back for future posts explaining how to install a load balancer, certificate manager and a private docker registry on that cluster.&lt;/p&gt;

&lt;p&gt;Please leave your comment, suggestion or any other input you think is relevant to this post in the discussion below.&lt;/p&gt;




&lt;p&gt;Like this post?&lt;br&gt;
You can find more by:&lt;/p&gt;

&lt;p&gt;Checking out my blog: &lt;a href="https://blog.zachinachshon.com" rel="noopener noreferrer"&gt;https://blog.zachinachshon.com&lt;/a&gt;&lt;br&gt;
Following me on twitter: &lt;a class="mentioned-user" href="https://dev.to/zachinachshon"&gt;@zachinachshon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! ❤️&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>rancher</category>
      <category>k3s</category>
      <category>raspberrypi</category>
    </item>
    <item>
      <title>Setting Up a Raspberry Pi Cluster</title>
      <dc:creator>ZachiNachshon</dc:creator>
      <pubDate>Sun, 28 Feb 2021 20:18:55 +0000</pubDate>
      <link>https://dev.to/zachinachshon/setting-up-a-raspberry-pi-cluster-1oa0</link>
      <guid>https://dev.to/zachinachshon/setting-up-a-raspberry-pi-cluster-1oa0</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fveceory0gt2nh0gfwefd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fveceory0gt2nh0gfwefd.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;&lt;b&gt;Credits:&lt;/b&gt; Logo created by &lt;a href="https://www.raspberrypi.org/" rel="noopener noreferrer"&gt;raspberrypi.org&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Installation instructions for setting up a local Raspberry Pi cluster at your home desk.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Operating System on SD Card &lt;/li&gt;
&lt;li&gt;Network Setup&lt;/li&gt;
&lt;li&gt;RPi Configuration&lt;/li&gt;
&lt;li&gt;
SSH Config

&lt;ul&gt;
&lt;li&gt;Client&lt;/li&gt;
&lt;li&gt;RPi Server&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Verification&lt;/li&gt;

&lt;li&gt;Troubleshooting&lt;/li&gt;

&lt;li&gt;Summary&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; For the time this post was published, there isn't an official &lt;code&gt;Raspberry Pi OS&lt;/code&gt; image that supports a 64 bit system. There are &lt;a href="https://downloads.raspberrypi.org/raspios_arm64/images/" rel="noopener noreferrer"&gt;beta versions&lt;/a&gt; but with limitations. This blog post won't cover them until officially released.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fig95j6mkgnopt3zwpq02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fig95j6mkgnopt3zwpq02.png" alt="rpi-cluster-orig"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2 id="os-install"&gt;Operating System on SD Card&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Connect the SD card to your local machine (laptop / desktop)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Download &lt;code&gt;Raspberry Pi OS Lite (Debian Buster)&lt;/code&gt; image from &lt;a href="https://www.raspberrypi.org/software/operating-systems/#raspberry-pi-os-32-bit" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Flash OS image using &lt;code&gt;Raspberry Pi Imager&lt;/code&gt; as specified in &lt;a href="https://www.raspberrypi.org/software/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzv3xq58ylonw9tbosbk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzv3xq58ylonw9tbosbk.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After flashing, eject and re-connect the SD card again to your local machine&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check for &lt;code&gt;boot&lt;/code&gt; connected device&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mount the SD card &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal session and create a text file named &lt;code&gt;ssh&lt;/code&gt; in the boot partition&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="c"&gt;# If you are working on macOS&lt;/span&gt;
  &lt;span class="nb"&gt;sudo touch&lt;/span&gt; /Volumes/boot/ssh

  &lt;span class="c"&gt;# Other Unix-like operating system&lt;/span&gt;
  &lt;span class="nb"&gt;sudo touch&lt;/span&gt; &amp;lt;path-to-rpi-boot-volume&amp;gt;/ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Eject the SD card and connect it to the Raspberry Pi&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2 id="network-setup"&gt;Network Setup&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Power up the RPi and connect directly to the home router&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the router network &lt;a href="http://192.168.1.1/" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Your router dashboard address might differ, check with your router manufacture guide.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Verify under &lt;code&gt;LAN settings&lt;/code&gt; -&amp;gt;  &lt;code&gt;Client List&lt;/code&gt; that there is a new &lt;code&gt;raspberrypi&lt;/code&gt; client &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Assign a static IP address using this &lt;a href="http://blog.zachinachshon.com/rpi-network/" rel="noopener noreferrer"&gt;guide&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2 id="rpi-configuration"&gt;RPi Configuration&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;SSH into the server using &lt;code&gt;ssh pi@&amp;lt;RPI-IP-ADDRESS&amp;gt;&lt;/code&gt; with an IP address from previous step and the default password &lt;code&gt;raspberry&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set the GPU memory split to 16MB by editing the RPi configuration file &lt;code&gt;sudo vi /boot/config.txt&lt;/code&gt; and appending &lt;code&gt;gpu_mem=16&lt;/code&gt; to it&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We specify the minimum memory possible to reserve for GPU (display) since we won’t require user interface.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reboot using &lt;code&gt;sudo reboot&lt;/code&gt; for changes to take effect&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SSH again to the RPI server and type &lt;code&gt;sudo raspi-config&lt;/code&gt;. Edit the following settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change the password for the &lt;code&gt;pi&lt;/code&gt; user (&lt;strong&gt;recommended&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Set the hostname to your liking&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure SSH server is enabled&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you are planning on using this server as a Kubernetes master - name it &lt;code&gt;kmaster&lt;/code&gt;. If it is a Kubernetes node, name it &lt;code&gt;knode&amp;lt;number&amp;gt;&lt;/code&gt; while &lt;code&gt;&amp;lt;number&amp;gt;&lt;/code&gt; is the next in-line number of your cluster stack.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvdms0c9mmk55uawnldpr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvdms0c9mmk55uawnldpr.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Close the SSH session and reconnect to the RPi server again&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify hostname was properly set and force manual replacement, if required. From the RPi terminal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;cat /etc/hostname&lt;/code&gt; and check for &lt;code&gt;kmaster&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;cat /etc/hosts&lt;/code&gt; and check for &lt;code&gt;127.0.1.1 kmaster&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;hostname&lt;/code&gt; which should return &lt;code&gt;kmaster&lt;/code&gt;, otherwise run &lt;code&gt;sudo hostname kmaster&lt;/code&gt; and check again&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Use &lt;code&gt;kmaster&lt;/code&gt; or &lt;code&gt;knode&amp;lt;number&amp;gt;&lt;/code&gt; according to the server type if you are setting up a Kubernetes cluster.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(Optional):&lt;/strong&gt; Install your favourite utilities on the RPi server:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="c"&gt;# Use Vim as a text editor&lt;/span&gt;
  &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;vim
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(Optional):&lt;/strong&gt; Set your preferred aliases on &lt;code&gt;~/.bashrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="c"&gt;# Open bash run command file for editing&lt;/span&gt;
  vim ~/.bashrc

  &lt;span class="c"&gt;# List all files/directories including hidden ones with size unit suffixes &lt;/span&gt;
  &lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;l&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ls -lah"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(Optional):&lt;/strong&gt; If you are planning to install the Rancher &lt;code&gt;k3s&lt;/code&gt; version of Kubernetes, you should enable a few container features by adding them &lt;em&gt;to the end of the &lt;code&gt;/boot/cmdline.txt&lt;/code&gt; file&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="c"&gt;# Edit file with sudo&lt;/span&gt;
  &lt;span class="nb"&gt;sudo &lt;/span&gt;vim /boot/cmdline.txt

  &lt;span class="c"&gt;# Append the following&lt;/span&gt;
  &lt;span class="nv"&gt;cgroup_enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cpuset &lt;span class="nv"&gt;cgroup_memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;cgroup_enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;




&lt;h2 id="ssh-config"&gt;SSH Config&lt;/h2&gt;

&lt;p&gt;We need to configure secure shell access for client &amp;lt;-&amp;gt; RPi server communication. It will allow us to access the RPi server from client machines such as our laptop and also allow secure communication between the RPi server and our locally installed utilities.&lt;/p&gt;

&lt;h3 id="ssh-client"&gt;Client&lt;/h3&gt;

&lt;p&gt;These instructions are relevant to the computer being used to connect to the RPi server:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create directory &lt;code&gt;~/.ssh&lt;/code&gt; if it doesn't exists and cd into it&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;ssh-keygen&lt;/code&gt; (with name &lt;code&gt;kmaster OR knode&amp;lt;number&amp;gt;&lt;/code&gt;, no passphrase)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the private key &lt;code&gt;kmaster&lt;/code&gt; to the ssh agent (select between permanent/temporary)&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-add ~/.ssh/kmaster      &lt;span class="c"&gt;# Add temporary to keychain&lt;/span&gt;
ssh-add &lt;span class="nt"&gt;-K&lt;/span&gt; ~/.ssh/kmaster   &lt;span class="c"&gt;# Add permanently to keychain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This will allow a secure communication without prompting for the password every time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the public key to RPi server&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="c"&gt;# Master node&lt;/span&gt;
  ssh-copy-id &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/kmaster.pub pi@&amp;lt;RPI-IP-ADDRESS&amp;gt;

  &lt;span class="c"&gt;# Agent node&lt;/span&gt;
  ssh-copy-id &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/knode&amp;lt;number&amp;gt;.pub pi@&amp;lt;RPI-IP-ADDRESS&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; For this step you will need to authenticate with your password. A file named &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; is auto-created with the public key content on the RPi server.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;(Optional):&lt;/strong&gt; If you have defined a static IP for the RPi server as described in &lt;a href="http://blog.zachinachshon.com/rpi-network/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, add named hosts records on your client machine:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Use names instead of IP addresses&lt;/span&gt;
   &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;MASTER-IP-ADDRESS&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;kmaster"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts
   &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;NODE-IP-ADDRESS&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;knode1"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h3 id="ssh-rpi-server"&gt;RPi Server&lt;/h3&gt;

&lt;p&gt;These instructions are intended for the RPi server to enable SSH communication:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Disable SSH &lt;code&gt;PasswordAuthentication&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Edit &lt;code&gt;ssh_config&lt;/code&gt; by &lt;code&gt;sudo vim /etc/ssh/ssh_config&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Uncomment &lt;code&gt;PasswordAuthentication&lt;/code&gt; by removing the &lt;code&gt;#&lt;/code&gt; prefix&lt;/li&gt;
&lt;li&gt;Change its value to &lt;code&gt;no&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Make sure &lt;code&gt;PasswordAuthentication&lt;/code&gt; is properly aligned (4 spaces)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Restart SSH server&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;sudo&lt;/span&gt; /etc/init.d/ssh restart
   &lt;span class="nb"&gt;sudo&lt;/span&gt; /etc/init.d/ssh status   &lt;span class="c"&gt;# Verify SSH server is running&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2 id="verification"&gt;Verification&lt;/h2&gt;

&lt;p&gt;To verify everything is set-up correctly, try to connect from the client machine i.e. laptop to the RPi server with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh pi@kmaster
ssh pi@knode&amp;lt;number&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2 id="troubleshooting"&gt;Troubleshooting&lt;/h2&gt;

&lt;h3 id="trouble-locale"&gt;Locale&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What?&lt;/strong&gt; Locale errors/warnings when connecting to a RPi server and/or running &lt;code&gt;locale&lt;/code&gt; on a server node. These are a few example errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;setlocale: LC_ALL: cannot change locale &lt;span class="o"&gt;(&lt;/span&gt;en_US.UTF-8&lt;span class="o"&gt;)&lt;/span&gt;
locale: Cannot &lt;span class="nb"&gt;set &lt;/span&gt;LC_CTYPE to default locale: No such file or directory
locale: Cannot &lt;span class="nb"&gt;set &lt;/span&gt;LC_MESSAGES to default locale: No such file or directory
locale: Cannot &lt;span class="nb"&gt;set &lt;/span&gt;LC_ALL to default locale: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; We'll set our locale to &lt;code&gt;en_US&lt;/code&gt; by running the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SSH to the RPi server&lt;/li&gt;
&lt;li&gt;Edit the &lt;code&gt;locale.gen&lt;/code&gt; file using &lt;code&gt;sudo vi /etc/locale.gen&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Uncomment the line with &lt;code&gt;en_US.UTF-8&lt;/code&gt; by removing the &lt;code&gt;#&lt;/code&gt; character (make sure there are no leading spaces)&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;sudo locale-gen en_US.UTF-8&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;sudo update-locale en_US.UTF-8&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;locale&lt;/code&gt; and make sure there are no errors/warnings&lt;/li&gt;
&lt;/ol&gt;




&lt;h3 id="summary"&gt;Summary&lt;/h3&gt;

&lt;p&gt;By the end of this post you should have a working headless (non-GUI) Raspberry Pi(s) connected to your home network with SSH communication available, good job ! 👏&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What now?&lt;/strong&gt; You are welcome to check back for a future blog post on how to install Kubernetes on top of your amazing RPi cluster.&lt;/p&gt;

&lt;p&gt;Please leave your comment, suggestion or any other input you think is relevant to this post in the discussion below.&lt;/p&gt;




&lt;p&gt;Like this post?&lt;br&gt;
You can find more by:&lt;/p&gt;

&lt;p&gt;Checking out my blog: &lt;a href="https://blog.zachinachshon.com" rel="noopener noreferrer"&gt;https://blog.zachinachshon.com&lt;/a&gt;&lt;br&gt;
Following me on twitter: &lt;a class="mentioned-user" href="https://dev.to/zachinachshon"&gt;@zachinachshon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! ❤️&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>rpi</category>
      <category>ssh</category>
    </item>
    <item>
      <title>Configure Local Environment Using Dotfiles</title>
      <dc:creator>ZachiNachshon</dc:creator>
      <pubDate>Fri, 19 Feb 2021 08:32:26 +0000</pubDate>
      <link>https://dev.to/zachinachshon/configure-local-environment-using-dotfiles-1km0</link>
      <guid>https://dev.to/zachinachshon/configure-local-environment-using-dotfiles-1km0</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rfoNSsO1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o0fpzhsdf0jbapv4beki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rfoNSsO1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o0fpzhsdf0jbapv4beki.png" width="840" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;&lt;b&gt;Credits:&lt;/b&gt; &lt;a href="https://github.com/jglovier/dotfiles-logo"&gt;Logo&lt;/a&gt; created by &lt;a href="https://github.com/jglovier"&gt;Joel Glovier&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Automate your local dev configuration in style on a personal, work or any other machine using a &lt;a href="https://zachinachshon.com/dotfiles-cli/"&gt;dedicated CLI utility&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="tl-dr"&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;Using a dotfiles repository has never been so easy. Interact with any dotfiles repository using a well defined CLI utility rather than executing random scripts.&lt;/p&gt;

&lt;p&gt;Control the following items from a Git backed repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.dotfiles (aliases, exports, functions, shell RC files and others...)&lt;/li&gt;
&lt;li&gt;Settings and configurations (&lt;code&gt;.gitconfig&lt;/code&gt;, &lt;code&gt;.vimrc&lt;/code&gt; and others...)&lt;/li&gt;
&lt;li&gt;Homebrew (packages, casks, services, drivers)&lt;/li&gt;
&lt;li&gt;macOS/Linux settings and preferences&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="long"&gt;LONG&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Incentive&lt;/li&gt;
&lt;li&gt;Solution&lt;/li&gt;
&lt;li&gt;What's included?&lt;/li&gt;
&lt;li&gt;Homebrew&lt;/li&gt;
&lt;li&gt;
dotfiles

&lt;ul&gt;
&lt;li&gt;Session Folder&lt;/li&gt;
&lt;li&gt;Custom Folder&lt;/li&gt;
&lt;li&gt;Home Folder&lt;/li&gt;
&lt;li&gt;Transient Folder&lt;/li&gt;
&lt;li&gt;Shell Folder&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;macOS/Linux settings override&lt;/li&gt;
&lt;li&gt;Demo&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is a .dotfile?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is a standard text file usually located within the &lt;code&gt;$HOME&lt;/code&gt; folder. It contains a dot prefix in its name and is a hidden file which you can list with &lt;code&gt;ls -a&lt;/code&gt; on a *nix system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is a Homebrew package / cask?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Homebrew is a package manager for macOS, it allows you to install CLI utilities (packages) &amp;amp; GUI applications (casks).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why override macOS settings?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is relevant if you have custom macOS settings. These are usually the settings that you change from the macOS GUI (Graphical User Interface) or from the terminal. You should back these custom settings so you'll be able to restore them on every new/formatted machine.&lt;/p&gt;




&lt;h3 id="incentive"&gt;Incentive&lt;/h3&gt;

&lt;p&gt;We have all been there, being lost in the sea of information on our constant learning experience on a daily basis. When we identify a command / script / keybinding  that it is useful to us, we'll add it to a note taking application hoping to remember where it is stored for the next time.&lt;/p&gt;

&lt;p&gt;The same goes for local applications and CLI utilities. When receiving a new machine or going through an OS re-install, we start a manual installation process of all the applications and CLI utilities we are used to, hopefully not to miss a few.&lt;/p&gt;

&lt;p&gt;And what about run command files (&lt;code&gt;*rc&lt;/code&gt;) such as &lt;code&gt;zshrc&lt;/code&gt; and &lt;code&gt;bashrc&lt;/code&gt; for example. They usually contain custom content such as terminal colors, plugins etc.. which you'll want to restore so you'll feel right at home within your comfortable shell.&lt;/p&gt;

&lt;p&gt;It's not a simple task trying to keep our magic scripts that are scattered all over the place, either if its changing a system setting we've got used to or the repetitive command we're constantly searching for in Google since we cannot find the time to add a snippet for it since we cannot seem to find the appropriate note-taking-application-to-rule-them-all.&lt;/p&gt;




&lt;h3 id="solution"&gt;Solution&lt;/h3&gt;

&lt;p&gt;Automate your local development environment installations/updates with just a few terminal commands. Manage everything under a Git backed repository so you'll keep your setting in a centralized location, detached from any machine and enjoy all the benefits a version control has to offer.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does keeping dotfiles under a GitHub repo helpful?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even though the dotfiles are located under the folder you have cloned the Git repository into, by using symlinks (symbolic link / soft link) we're able to link them to &lt;code&gt;$HOME&lt;/code&gt; or any other folder and every change within the repository is reflected on every new terminal shell session since these files get sourced with their latest content.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Additionally, you can call &lt;code&gt;dotfiles reload&lt;/code&gt; on the existing session without the need to create a new session.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;How do we start?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are a lot of dotfiles repositories publicly available in GitHub. People share their own managed dotfiles, either if it's a single script you'll have to add to the &lt;code&gt;.bash_profile&lt;/code&gt; or other more complex alternatives that require a different setup.&lt;/p&gt;

&lt;p&gt;In this blog post I'll share an &lt;a href="https://github.com/ZachiNachshon/dotfiles-example"&gt;example dotfiles solution&lt;/a&gt; similar to the one I'm using which is managed by a &lt;a href="https://zachinachshon.com/dotfiles-cli/"&gt;dotfiles-cli&lt;/a&gt; utility that allows its users some ease of mind when it comes to local environment setup.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Please feel free to fork my &lt;a href="https://github.com/ZachiNachshon/dotfiles-example"&gt;dotfiles GitHub repository&lt;/a&gt; and add your own content on top of it !&lt;/p&gt;

&lt;p&gt;In case you wish to check the repository out before forking, you are welcome to clone and change it to suite your needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git clone https://github.com/ZachiNachshon/dotfiles-example.git ~/&amp;lt;my-codebase-folder&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why using a dotfiles CLI utility?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Simplify the complex dotfiles repository wiring by separating the files from the management layer&lt;/li&gt;
&lt;li&gt;Use a dedicated CLI utility to control all aspects of the dotfiles repository with ease&lt;/li&gt;
&lt;li&gt;Having a coherent dotfiles structure that is easy to get familiar with&lt;/li&gt;
&lt;li&gt;Allow a generic CLI to control multiple dotfiles repositories (private and public)&lt;/li&gt;
&lt;li&gt;Avoid from running arbitrary scripts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;How to install the dotfiles CLI?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Install dotfiles-cli via Homebrew, pre-built release or from sources. Please head over to the &lt;a href="https://zachinachshon.com/dotfiles-cli/docs/latest/getting-started/download/"&gt;download section&lt;/a&gt; for further installation information.&lt;/p&gt;




&lt;h3 id="whats-included"&gt;What's included?&lt;/h3&gt;

&lt;p&gt;A well organized folder layout containing categorized scripts with minimum overhead to glue them all together using the dotfiles-cli utility.&lt;/p&gt;

&lt;p&gt;An example of an expected repository structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;.&lt;/span&gt;
   ├── ...
   ├── brew   
   │   ├── casks.txt
   │   ├── drivers.txt
   │   ├── packages.txt
   │   ├── services.txt
   │   └── taps.txt
   │
   ├── dotfiles               
   │   ├── custom  
   │   │   ├── .my-company  
   │   │   └── ...
   │   ├── home
   │   │   ├── .gitconfig       
   │   │   ├── .vimrc
   │   │   └── ...
   │   ├── session 
   │   │   ├── .aliases
   │   │   └── ...
   │   ├── shell
   │   │   ├── .zshrc
   │   │   └── ...
   │   └── transient
   │       └── .secrets
   │
   ├── os
   │   ├── linux
   │   │   ├── key_bindings.sh
   │   │   └── ...
   │   └── mac
   │       ├── finder_settings.sh  
   │       └── ...
   │
   ├── plugins
   │   ├── zsh
   │   │   ├── oh_my_zsh.sh  
   │   │   └── ...
   │   └── bash 
   │       ├── dummy.sh
   │       └── ...
   └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Run &lt;code&gt;dotfiles structure&lt;/code&gt; to get a reminder on the expected dotfiles repo structure.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3 id="homebrew"&gt;Homebrew&lt;/h3&gt;

&lt;p&gt;Automate the installation process of favorite CLI utilities &amp;amp; GUI applications by using Homebrew macOS package manager. All you will have to do is to update simple text files with the package, cask, driver or service you wish to install. Installation and updates takes place whenever &lt;code&gt;dotfiles brew &amp;lt;option&amp;gt;&lt;/code&gt; command is being called.&lt;/p&gt;

&lt;p&gt;To add a CLI utility (a.k.a Homebrew package):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search for a desired package in &lt;a href="https://formulae.brew.sh/cask/"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add the package name to the &lt;a href="https://github.com/ZachiNachshon/dotfiles-example/blob/master/brew/packages.txt"&gt;packages.txt&lt;/a&gt; file&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;dotfiles brew packages&lt;/code&gt; to install/update&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To add a GUI application (a.k.a Homebrew cask):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search for a desired cask in &lt;a href="https://formulae.brew.sh/formula/"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add the cask name to the &lt;a href="https://github.com/ZachiNachshon/dotfiles-example/blob/master/brew/casks.txt"&gt;casks.txt&lt;/a&gt; file&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;dotfiles brew casks&lt;/code&gt; to install/update&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Similar flow for installing drivers and services via Homebrew.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3 id="dotfiles"&gt;dotfiles&lt;/h3&gt;

&lt;h4 id="session-folder"&gt;Session Folder&lt;/h4&gt;

&lt;p&gt;A folder containing dotfiles which are relevant across all machines (work, personal etc..) that get sourced along on every new shell session or upon &lt;code&gt;dotfiles reload&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Example of such session files I use (you can add your own):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.aliases&lt;/code&gt; - define shortcuts for commonly used *nix commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# -=-= Custom =-=-&lt;/span&gt;
   &lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;l&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ls -lah"&lt;/span&gt; 
   &lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;dl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cd ~/Downloads"&lt;/span&gt;
   &lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cd &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/src/github.com"&lt;/span&gt;

   &lt;span class="c"&gt;# -=-= ZSH =-=-&lt;/span&gt;
   &lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;zshconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"vim ~/.zshrc"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.functions&lt;/code&gt; - add new utility scrips to the functions collection so they'll be available right away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Create a new directory and cd into it&lt;/span&gt;
   mkd&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
     &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
     &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.paths&lt;/code&gt; - add new environment variables you wish to make available on new shell sessions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# -=-= Kubernetes =-=-&lt;/span&gt;
   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/k3s/config

   &lt;span class="c"&gt;# -=-= Path =-=-&lt;/span&gt;
   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/bin:/usr/bin:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JAVA_HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/bin:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GOROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/bin:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/bin:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h4 id="custom-folder"&gt;Custom Folder&lt;/h4&gt;

&lt;p&gt;A folder containing custom files to symlink, usually relevant to specific machines e.g. work related or personal etc..&lt;/p&gt;

&lt;p&gt;I have added an example file to the repository (you can and should add your own):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.my-company&lt;/code&gt; - a work related content that should get sourced on every new shell session&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;alias &lt;/span&gt;work-repos&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cd codebase/my-company-repos"&lt;/span&gt; 

   work&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"I work at ACME Corporation."&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h4 id="home-folder"&gt;Home Folder&lt;/h4&gt;

&lt;p&gt;A folder containing files that should get symlinked from the dotfiles GitHub repository to the &lt;code&gt;$HOME&lt;/code&gt; folder for the same reasons we have described in here. A few examples are &lt;code&gt;.gitignore_global, .gitconfig, .vimrc&lt;/code&gt; and others...&lt;/p&gt;

&lt;p&gt;To add such file as described:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Place it within &lt;code&gt;&amp;lt;repo-root&amp;gt;/dotfiles/home/&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;dotfiles sync home&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id="transient-folder"&gt;Transient Folder&lt;/h4&gt;

&lt;p&gt;A folder containing transient files, meaning, files that shouldn't get checked-in into source control or symlinked anywhere but should get sourced along on every new shell session.&lt;/p&gt;

&lt;p&gt;You can use this to export ENV vars with sensitive information such as secrets to become available on newly opened shells. Files under &lt;code&gt;transient&lt;/code&gt; folder are git ignored by default to prevent from committing to a source control.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id="shell-folder"&gt;Shell Folder&lt;/h4&gt;

&lt;p&gt;A folder containing run command files (&lt;code&gt;*rc&lt;/code&gt;) such as &lt;code&gt;zshrc&lt;/code&gt; and &lt;code&gt;bashrc&lt;/code&gt; for example. They usually contain custom content such as terminal colours, plugins etc.. which you'll probably want to backup and restore once needed.&lt;/p&gt;

&lt;p&gt;To add such file as described:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Place it within &lt;code&gt;&amp;lt;repo-root&amp;gt;/dotfiles/shell/&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;dotfiles sync shell&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Only the relevant shell &lt;code&gt;*.rc&lt;/code&gt; file is being symlinked to &lt;code&gt;$HOME&lt;/code&gt; folder, depends on the shell you are using. Currently supported shells are: &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;zsh&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3 id="macos-linux-settings"&gt;macOS/Linux Settings Override&lt;/h3&gt;

&lt;p&gt;These are usually settings you have changed from the macOS/Linux GUI (Graphical User Interface) or from the terminal. In case you do have custom macOS/Linux settings, you'll probably want to back them via GitHub repository to be able to restore on-demand. &lt;/p&gt;

&lt;p&gt;Restoring these settings could take place either on new machine, after OS re-install or in case you have tweaked some knobs and forgot how to revert.&lt;/p&gt;

&lt;p&gt;To override macOS/Linux settings and preferences:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find the terminal command corresponding to the setting you wish to backup&lt;/li&gt;
&lt;li&gt;Add the command to a script files under:

&lt;ul&gt;
&lt;li&gt;macOS - &lt;code&gt;&amp;lt;repo-root&amp;gt;/dotfiles/os/mac&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Linux - &lt;code&gt;&amp;lt;repo-root&amp;gt;/dotfiles/os/linux&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;dotfiles os &amp;lt;mac/linux&amp;gt;&lt;/code&gt; to override settings and preferences&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Check out macOS settings &lt;a href="https://github.com/ZachiNachshon/dotfiles-example/tree/master/os/mac"&gt;examples in here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h4 id="demo"&gt;Demo&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://zachinachshon.com/dotfiles-cli/docs/latest/assets/gif/dotfiles-cli.gif"&gt;Click here&lt;/a&gt; for a running demo.&lt;/p&gt;




&lt;h4 id="summary"&gt;Summary&lt;/h4&gt;

&lt;p&gt;I hope this post helped to shed some light on how dotfiles could be useful for your local environment and you have found the way I'm automating dotfiles with Homebrew &amp;amp; macOS overrides insightful and helpful.&lt;/p&gt;

&lt;p&gt;Please leave your comment, suggestion or any other input you think is relevant to this post in the discussion below.&lt;/p&gt;




&lt;p&gt;Like this post?&lt;br&gt;
You can find more by:&lt;/p&gt;

&lt;p&gt;Checking out my blog: &lt;a href="https://blog.zachinachshon.com"&gt;https://blog.zachinachshon.com&lt;/a&gt;&lt;br&gt;
Following me on twitter: &lt;a class="mentioned-user" href="https://dev.to/zachinachshon"&gt;@zachinachshon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! ❤️&lt;/p&gt;

</description>
      <category>dotfiles</category>
      <category>shell</category>
      <category>local</category>
      <category>homebrew</category>
    </item>
  </channel>
</rss>
