<?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: Justin</title>
    <description>The latest articles on DEV Community by Justin (@gjrdiesel).</description>
    <link>https://dev.to/gjrdiesel</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%2F22192%2Fae89945f-a062-43bb-8d50-a2c73dbae555.png</url>
      <title>DEV Community: Justin</title>
      <link>https://dev.to/gjrdiesel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gjrdiesel"/>
    <language>en</language>
    <item>
      <title>Benchmarking &amp; diagraming my home network</title>
      <dc:creator>Justin</dc:creator>
      <pubDate>Sun, 02 Apr 2023 20:33:05 +0000</pubDate>
      <link>https://dev.to/gjrdiesel/home-network-speed-diagraming-my-home-network-aip</link>
      <guid>https://dev.to/gjrdiesel/home-network-speed-diagraming-my-home-network-aip</guid>
      <description>&lt;p&gt;Every once and a while I have to call my ISP and renegotiate my internet speed.&lt;/p&gt;

&lt;p&gt;Before this last call, I was at 300/300 Mbps, which I thought was quite fast and ridiculous.&lt;/p&gt;

&lt;p&gt;I call or chat online when I start seeing offers for new customers well exceeding the performance or beneath the price I'm paying.&lt;/p&gt;

&lt;p&gt;I've learned to expect they will not give you the same price as new customers, as they are introductory offers and only last 1 year or so. But using that as leverage can spark up some pretty good deals.&lt;/p&gt;

&lt;p&gt;So I was paying $90 for 300/300, I saw new customers were offered $45 for 1G/1G.&lt;/p&gt;

&lt;p&gt;Popped in a chat with my ISP and now I'm at 2G/2G for $90.&lt;/p&gt;

&lt;p&gt;Now with 2G/2G, I'm going back and reevaluating my network and seeing if I need to address any issues.&lt;/p&gt;

&lt;p&gt;Thus, this network diagram was born. It's still slightly a WIP, I want to better document the actual ISP provided equipment and connections, but I'm going out to see the new D&amp;amp;D movie with any luck, so this will have to do for now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t6BE9EJf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2cd2y49vi4g5u1xu6bzu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t6BE9EJf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2cd2y49vi4g5u1xu6bzu.png" alt="Network diagram outlining network speeds between certain devices" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing I've found interesting is my M1 MBP is not performing as well as my desktop that is running in nearly the same configuration. I might need to check cables there. Happy to see the desktop is reaching nearly gig speeds at least.&lt;/p&gt;

&lt;p&gt;Now to ponder about upgrading the internal network to support 2.5G (:sad-face:).&lt;/p&gt;

&lt;p&gt;I didn't think I'd be here doing this. I thought 1 Gig Fiber was out of reach even but it seems things are changing quickly.&lt;/p&gt;

&lt;p&gt;If you haven't reached out to your ISP lately, I'd highly suggest doing so.&lt;/p&gt;

</description>
      <category>homelab</category>
    </item>
    <item>
      <title>🥇 The ultimate kubernetes homelab setup</title>
      <dc:creator>Justin</dc:creator>
      <pubDate>Sun, 13 Nov 2022 15:28:05 +0000</pubDate>
      <link>https://dev.to/gjrdiesel/the-ultimate-kubernetes-homelab-setup-558f</link>
      <guid>https://dev.to/gjrdiesel/the-ultimate-kubernetes-homelab-setup-558f</guid>
      <description>&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;Want an easy way to deploy and control kubernetes in a totally sealed homelab environment?&lt;/p&gt;

&lt;p&gt;Let's get started with k3, gitlab, proxmox, ansible and terraform to automate our homelab infrastructure for repeatability and expandability.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sections
&lt;/h1&gt;

&lt;p&gt;Some of the sections we're going to cover for build out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;proxmox&lt;/li&gt;
&lt;li&gt;terraform&lt;/li&gt;
&lt;li&gt;ansible&lt;/li&gt;
&lt;li&gt;k3

&lt;ul&gt;
&lt;li&gt;dns&lt;/li&gt;
&lt;li&gt;test deploys&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;gitlab&lt;/li&gt;

&lt;li&gt;sshportal&lt;/li&gt;

&lt;li&gt;persistant volumes&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Proxmox
&lt;/h2&gt;

&lt;p&gt;The baseline for this project is going to be a proxmox server. We could run k3 bare metal, but proxmox gives us the ability to practice spinning up and down k8 nodes of different sizes/restraints via terraform. Plus if anything goes wrong, we just destroy a proxmox vm and start fresh, which happens in minutes rather then having to reinstall an entire OS on baremetal.&lt;/p&gt;

&lt;p&gt;We're going to need/want to &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup a cloud-init image to be our base image for terraform&lt;/li&gt;
&lt;li&gt;Network a remote cluster via tailscale (vpn) [optional redundancy]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So with proxmox, there's not much to it other then to follow the installation instructions: &lt;a href="https://www.proxmox.com/en/proxmox-ve/get-started" rel="noopener noreferrer"&gt;https://www.proxmox.com/en/proxmox-ve/get-started&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download ISO image&lt;/li&gt;
&lt;li&gt;Boot from USB or CD/DVD&lt;/li&gt;
&lt;li&gt;Configure the host machine (make sure you can login web ui)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have it so you can log into a web ui with something like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

https://[your_ip_here]:8006/


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fa2bezjllceiwrwp854th.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%2Fa2bezjllceiwrwp854th.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can decide if you want to setup the remote cluster, one day I'll document how to network the two with tailscale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloud init
&lt;/h3&gt;

&lt;p&gt;With proxmox setup, we need setup a base image to be our "golden image" for terraform to use to deploy new nodes etc.&lt;/p&gt;

&lt;p&gt;For that, I'd suggest following a tutorial like this:&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=shiIi38cJe4" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=shiIi38cJe4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.technotim.live/posts/cloud-init-cloud-image/" rel="noopener noreferrer"&gt;https://docs.technotim.live/posts/cloud-init-cloud-image/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I followed the tutorial pretty closely and even used the suggested image id of &lt;code&gt;8000&lt;/code&gt;. So you'll see that being used in the next terraform section.&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%2Fy6otsn2ik8i1nueqeyl8.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%2Fy6otsn2ik8i1nueqeyl8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cloud init gives us the ability to spin up a base image with predefined username/password, accepted ssh keys, and ip configuration. As well as gives us the ability to override those settings using the terraform provider.&lt;/p&gt;

&lt;p&gt;So with all that, let's jump into the terraform section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform
&lt;/h2&gt;

&lt;p&gt;So now that our local has it's own "cloud", i.e. we have proxmox instead of AWS or GCP for our "unlimited" VMs, we need a way to orchestrate them.&lt;/p&gt;

&lt;p&gt;Part of the big switch to kubernetes is, it enables us to not care about the underlying infrastructure. For example, on AWS, if we have a kubernetes node that runs out of diskspace, we can expect aws to launch a new node and kubernetes move the current workload to the new node.&lt;/p&gt;

&lt;p&gt;Terraform is what will enable us to recreate some of that underlying node scalability via proxmox.&lt;/p&gt;

&lt;p&gt;Ultimately we'll have something like &lt;code&gt;var.node_count = 3&lt;/code&gt;, let's say we want to deploy more pods but we've reached the max kubernetes will deploy to our current 3 cluster setup. We can change to &lt;code&gt;var.node_count = 5&lt;/code&gt; and let terraform handle rolling that out across proxmox and ensure everything is kept uniform.&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%2Fmuwe07elssmy8mw2f08w.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%2Fmuwe07elssmy8mw2f08w.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Terraform gives the ability to easily create and manage a grouping of different size nodes for experimenting with changing workloads or setting certain node affinity on kubernetes.&lt;/p&gt;

&lt;p&gt;Some of the groups I've made so far are like the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"prxmx_api_nodes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"prxmx_worker_nodes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"prxmx_worker_xl_nodes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"prxmx_worker_xxl_nodes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That enables the following types of workgroups that scale to certain resources:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;cores&lt;/th&gt;
&lt;th&gt;ram&lt;/th&gt;
&lt;th&gt;hd space&lt;/th&gt;
&lt;th&gt;hd type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;worker_nodes&lt;/td&gt;
&lt;td&gt;4c&lt;/td&gt;
&lt;td&gt;4G&lt;/td&gt;
&lt;td&gt;30G&lt;/td&gt;
&lt;td&gt;hdd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;worker_xl_nodes&lt;/td&gt;
&lt;td&gt;12c&lt;/td&gt;
&lt;td&gt;12G&lt;/td&gt;
&lt;td&gt;80G&lt;/td&gt;
&lt;td&gt;hdd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;worker_ssd_xl_nodes&lt;/td&gt;
&lt;td&gt;12c&lt;/td&gt;
&lt;td&gt;12G&lt;/td&gt;
&lt;td&gt;80G&lt;/td&gt;
&lt;td&gt;ssd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;worker_xxl_nodes&lt;/td&gt;
&lt;td&gt;24c&lt;/td&gt;
&lt;td&gt;24G&lt;/td&gt;
&lt;td&gt;160G&lt;/td&gt;
&lt;td&gt;hdd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;worker_ssd_xxl_nodes&lt;/td&gt;
&lt;td&gt;24c&lt;/td&gt;
&lt;td&gt;24G&lt;/td&gt;
&lt;td&gt;160G&lt;/td&gt;
&lt;td&gt;ssd&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In my setup, I have "xeon" 2 socket, 32 core proxmox server, 96gb ram, and 12 core intel i7, 16gb ram "river" server.&lt;/p&gt;

&lt;p&gt;Terraform has to know to deploy these different VMs across both river and xeon proxmox servers, but once those nodes get added to kubernetes (k8s), the implementation is abstracted from k8s. K8s will deploy across any/all available nodes.&lt;/p&gt;

&lt;p&gt;But having a good selection of different types of nodes really helps it feel like a full replacement for AWS. And even if you don't use all the different workgroups, having them available but scaled back definitely helps for organization and scalability once it's needed.&lt;/p&gt;

&lt;p&gt;Most of my workload might run on &lt;code&gt;worker_xl_nodes&lt;/code&gt; which that workgroup can exist across both my &lt;code&gt;river&lt;/code&gt; and &lt;code&gt;xeon&lt;/code&gt; servers, so if I need to scale down my &lt;code&gt;xeon&lt;/code&gt; server, kubernetes can migrate everything to the &lt;code&gt;river&lt;/code&gt; server for maintenance. &lt;/p&gt;

&lt;p&gt;But some applications need lots of cores and ram that &lt;code&gt;river&lt;/code&gt; doesn't have, so without having another large &lt;code&gt;xeon&lt;/code&gt; like server, scaling down my &lt;code&gt;worker_xxl_nodes&lt;/code&gt; is going to result in downtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ansible
&lt;/h2&gt;

&lt;p&gt;One of the other things we'll also do here is use ansible to setup each of our nodes to our liking via &lt;code&gt;provisioner "local-exec"&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"proxmox_vm_qemu"&lt;/span&gt; &lt;span class="s2"&gt;"api_nodes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prxmx_api_nodes&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"k3-api-${count.index + 1}"&lt;/span&gt;
  &lt;span class="nx"&gt;desc&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"k3-api-${count.index + 1}"&lt;/span&gt;
  &lt;span class="nx"&gt;target_node&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"xeon"&lt;/span&gt;

  &lt;span class="nx"&gt;clone&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu-cloud"&lt;/span&gt;

  &lt;span class="nx"&gt;os_type&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cloud-init"&lt;/span&gt;
  &lt;span class="nx"&gt;ipconfig0&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ip=10.0.4.${count.index + 1}/16,gw=10.0.2.1"&lt;/span&gt;
  &lt;span class="nx"&gt;nameserver&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.1.1.1"&lt;/span&gt;
  &lt;span class="nx"&gt;searchdomain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.1.1.1"&lt;/span&gt;

  &lt;span class="nx"&gt;cores&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
  &lt;span class="nx"&gt;memory&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"4096"&lt;/span&gt;

  &lt;span class="nx"&gt;disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;storage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disk&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scsi"&lt;/span&gt;
    &lt;span class="nx"&gt;size&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"30G"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ignore_changes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;ciuser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;sshkeys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"local-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sleep 30 &amp;amp;&amp;amp; ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -u knox -i '10.0.4.${count.index + 1},' playbook.yml"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What this means is, after each successful setup of our proxmox vm, we'll run a ansible playbook to install the tool to our liking.&lt;/p&gt;

&lt;p&gt;Whether that's k3 itself, or neovim and exporting the editor to use neovim on each VM. Ansible enables that.&lt;/p&gt;

&lt;h2&gt;
  
  
  K3
&lt;/h2&gt;

&lt;p&gt;So with our hypervisor (proxmox) chosen, vm layout designed (terraform + ansible), we're ready to start on the kubernetes cluster, which is the thing that will handle our actual workloads/applications.&lt;/p&gt;

&lt;p&gt;For this iteration, I'm using k3 for its total ease of use.&lt;/p&gt;

&lt;p&gt;Using terraform, I deploy one api server (high-availability to come later) with terraform something like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# cat main.tf&lt;/span&gt;

&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;proxmox&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Telmate/proxmox"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2.9.11"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"proxmox"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;pm_api_url&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://10.0.2.3:8006/api2/json"&lt;/span&gt;
  &lt;span class="nx"&gt;pm_tls_insecure&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"prxmx_api_nodes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"disk"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hdd-12tb"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"proxmox_vm_qemu"&lt;/span&gt; &lt;span class="s2"&gt;"api_nodes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prxmx_api_nodes&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"k3-api-${count.index + 1}"&lt;/span&gt;
  &lt;span class="nx"&gt;desc&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"k3-api-${count.index + 1}"&lt;/span&gt;
  &lt;span class="nx"&gt;target_node&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"xeon"&lt;/span&gt;

  &lt;span class="nx"&gt;clone&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu-cloud"&lt;/span&gt;

  &lt;span class="nx"&gt;os_type&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cloud-init"&lt;/span&gt;
  &lt;span class="nx"&gt;ipconfig0&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ip=10.0.4.${count.index + 1}/16,gw=10.0.2.1"&lt;/span&gt;
  &lt;span class="nx"&gt;nameserver&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.1.1.1"&lt;/span&gt;
  &lt;span class="nx"&gt;searchdomain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.1.1.1"&lt;/span&gt;

  &lt;span class="nx"&gt;cores&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
  &lt;span class="nx"&gt;memory&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"4096"&lt;/span&gt;

  &lt;span class="nx"&gt;disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;storage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disk&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scsi"&lt;/span&gt;
    &lt;span class="nx"&gt;size&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"30G"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ignore_changes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;ciuser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;sshkeys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"local-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sleep 30 &amp;amp;&amp;amp; ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -u knox -i '10.0.4.${count.index + 1},' playbook.yml"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And a playbook like&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;# cat playbook.yml&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt;

- name: Install k3
  gather_facts: &lt;span class="nb"&gt;true
  &lt;/span&gt;hosts: all
  tasks:
    - name: Install k3
      shell: curl &lt;span class="nt"&gt;-sfL&lt;/span&gt; https://get.k3s.io | sh -


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once it's deployed, in my case it'll deploy it to a static ip &lt;code&gt;10.0.4.1&lt;/code&gt;, so I'll ssh to it, and grab the k3 token to add future nodes to the k3 cluster.&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;# cat /var/lib/rancher/k3s/server/token&lt;/span&gt;
K100.......


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So now I spin up some &lt;code&gt;worker_xl_nodes&lt;/code&gt;, once they're finished, I ssh in and run&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&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;K3S_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://10.0.4.1:6443 &lt;span class="nv"&gt;K3S_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'K100.....'&lt;/span&gt; sh -


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once that's finished and I have the workers I need, can verify with&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;# kubectl get nodes&lt;/span&gt;

NAME              STATUS   ROLES                  AGE   VERSION
k3-worker-xxl-1   Ready    &amp;lt;none&amp;gt;                 12h   v1.25.3+k3s1
k3-worker-xl-1    Ready    &amp;lt;none&amp;gt;                 12h   v1.25.3+k3s1
k3-worker-3       Ready    &amp;lt;none&amp;gt;                 14h   v1.25.3+k3s1
k3-worker-2       Ready    &amp;lt;none&amp;gt;                 14h   v1.25.3+k3s1
k3-api-1          Ready    control-plane,master   14h   v1.25.3+k3s1
k3-worker-1       Ready    &amp;lt;none&amp;gt;                 14h   v1.25.3+k3s1


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  DNS
&lt;/h3&gt;

&lt;p&gt;Now one other thing is, I setup a DNS A record to point&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

*.kube.reaz.io -&amp;gt; 10.0.4.1


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Because I use tailscale for everything, I have no need for exposing the IP to the outside world.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test deploys
&lt;/h3&gt;

&lt;p&gt;One other thing I'll often use is this little &lt;code&gt;deploy.sh&lt;/code&gt; helper script for quickly slinging out an docker image to an entire k8s deployment.&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;# cat deploy.sh&lt;/span&gt;

&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;

kubectl create deployment &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$image&lt;/span&gt;
kubectl expose deployment &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80 &lt;span class="nt"&gt;--target-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$port&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;LoadBalancer
kubectl create ingress &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="nt"&gt;--rule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;.kube.reaz.io/*=&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;:80"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The usage is like&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;## Example: sh deploy.sh [image] [name] [port]&lt;/span&gt;

&lt;span class="c"&gt;# sh deploy.sh linuxserver/nginx nginx 80&lt;/span&gt;

&lt;span class="c"&gt;# curl nginx.kube.reaz.io&lt;/span&gt;
    &amp;lt;html&amp;gt;
        &amp;lt;&lt;span class="nb"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &amp;lt;title&amp;gt;Welcome to our server&amp;lt;/title&amp;gt;
...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Gitlab
&lt;/h2&gt;

&lt;p&gt;With that in place, we're ready to deploy a test gitlab env. I like to use gitlab as a self-hosted/self-contained git for my local stuff that I don't want on the public web (mainly for things that aren't serious, just a nice playground).&lt;/p&gt;

&lt;p&gt;But I do like keeping things in source control and using pipelines where possible.&lt;/p&gt;

&lt;p&gt;So using the deploy script earlier, we'll just use the quick and easy docker image for right now and until we get persistent volumes setup, we'll just be careful to not put anything in there we aren't afraid to lose (in the event kubernetes reschedules the pods, without persist storage, everything in the gitlab container will be lost -- we'll setup storage later, for now lets just get to use more of our cluster).&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;# sh deploy.sh gitlab/gitlab-ee:latest gitlab 80&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once that finishes up after about 5-10 minutes, I'm able to access it at &lt;a href="https://gitlab.kube.reaz.io" rel="noopener noreferrer"&gt;https://gitlab.kube.reaz.io&lt;/a&gt; but I'm greeted with a login prompt.&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%2Fhr8cyzlhkqq6304ulj2e.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%2Fhr8cyzlhkqq6304ulj2e.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the steps to track down the initial root password the image creates:&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;# kubectl get pods&lt;/span&gt;
NAME                         READY   STATUS    RESTARTS   AGE
gitlab-54955459cb-52wr4      1/1     Running   0          12h

&lt;span class="c"&gt;# kubectl exec -it gitlab-54955459cb-52wr4 -- bash&lt;/span&gt;

&lt;span class="c"&gt;# cat /etc/gitlab/initial_root_password&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With that in place, we can start to put some of our work into gitlab and eventually start to build some pipelines, just remember anything in there will be destroyed until we setup persistent storage.&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%2Fvl2vif8ew4xw9s9zpys1.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%2Fvl2vif8ew4xw9s9zpys1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are lots of different ways to host gitlab or even use gitlab.com, but for this case, I really wanted to say I'm hosting everything in k8s. &lt;/p&gt;

&lt;p&gt;The downside I've run into with that though is &lt;code&gt;ssh&lt;/code&gt; isn't splittable the way &lt;code&gt;http&lt;/code&gt; and &lt;code&gt;ingress&lt;/code&gt; is. I.e. you can't just route ssh traffic from gitlab.kube.reaz.io using an ingress controller. Or at least I couldn't find a way with k3's traefik ingress controller for the 15mins I looked. You can get fancy with haproxy. I found a different way.&lt;/p&gt;

&lt;h2&gt;
  
  
  sshportal
&lt;/h2&gt;

&lt;p&gt;sshportal (&lt;a href="https://github.com/moul/sshportal" rel="noopener noreferrer"&gt;https://github.com/moul/sshportal&lt;/a&gt;) is in my mind, a ssh gateway. It's a little cumbersome to setup but it accomplishes routing ssh the way we'll want within a kubernetes cluster with replication.&lt;/p&gt;

&lt;p&gt;I use the kubernetes cli tools as starting point for my yaml:&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 deployment a pod&lt;/span&gt;
&lt;span class="c"&gt;# kubectl create deployment --image moul/sshportal sshportal&lt;/span&gt;

&lt;span class="c"&gt;# Create a service&lt;/span&gt;
&lt;span class="c"&gt;# kubectl expose deployment sshportal --port=2222 --target-port=2222 --name sshportal --type=LoadBalancer&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So next, I'm going to rework my api node to replace it's normal sshd server with this new sshportal service we just deployed to k8s.&lt;/p&gt;

&lt;p&gt;So these next couple modifications need to go on to live in our ansible playbook for the api-nodes, but here is the run down:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="c"&gt;# On the api node, gonna move the sshd port off of 22&lt;/span&gt;&lt;br&gt;
vi /etc/ssh/sshd_config&lt;br&gt;
&lt;span class="c"&gt;# Change&lt;/span&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Port 22
&lt;span class="nt"&gt;---&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;Port 2233&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;span class="c"&gt;# Next we're going to change the k3 server to allow us&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# to set a nodePort for ssh&lt;/span&gt;&lt;br&gt;
vi /etc/systemd/system/k3s.service&lt;br&gt;
&lt;span class="c"&gt;# Change&lt;/span&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/bin/k3s &lt;span class="se"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;   server &lt;span class="se"&gt;&amp;lt;/span&amp;gt;
&lt;span class="nt"&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/bin/k3s &lt;span class="se"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;   server &lt;span class="se"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;   &lt;span class="nt"&gt;--kube-apiserver-arg&lt;/span&gt; service-node-port-range&lt;span class="o"&gt;=&lt;/span&gt;10-32767&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;span class="c"&gt;# Then reload k3s&lt;/span&gt;&lt;br&gt;
systemctl daemon-reload&lt;br&gt;
systemctl restart k3s&lt;/p&gt;

&lt;p&gt;&lt;span class="c"&gt;# Then once k3 restarts,&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# we're going to edit the sshportal service&lt;/span&gt;&lt;br&gt;
kubectl edit svc sshportal&lt;br&gt;
&lt;span class="c"&gt;# And change the nodeport to be 22&lt;/span&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;- nodePort: 31840
&lt;span class="nt"&gt;---&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;- nodePort: 22&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;span class="c"&gt;# From there you can grab the invite code from the logs&lt;/span&gt;&lt;br&gt;
kubectl logs deployment/sshportal&lt;br&gt;
&lt;span class="c"&gt;# 2022/11/13 11:21:21 info: system migrated&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# 2022/11/13 11:21:21 info 'admin' user created, use the user 'invite:DU6oTsAy8E1vQd9d' to associate a public key with this account&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# 2022/11/13 11:21:21 info: SSH Server accepting connections on :2222, idle-timout=0s&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c"&gt;# With that you should be able to ssh to the api-node &lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# with the invite code and setup the 'admin' account&lt;/span&gt;&lt;br&gt;
ssh 10.0.4.1 &lt;span class="nt"&gt;-l&lt;/span&gt; invite:DU6oTsAy8E1vQd9d&lt;/p&gt;

&lt;p&gt;&lt;span class="c"&gt;# With the admin account, you can start to create ssh jumps&lt;/span&gt;&lt;br&gt;
ssh 10.0.4.1 &lt;span class="nt"&gt;-l&lt;/span&gt; admin&lt;br&gt;
&lt;span class="c"&gt;#&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;#    __________ _____           __       &lt;strong&gt;&lt;/strong&gt;&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;#   / _&lt;em&gt;/ _&lt;/em&gt;/ // / _ _  _&lt;strong&gt;&lt;em&gt;/ /&lt;/em&gt;&lt;/strong&gt;_ &lt;em&gt;/ /&lt;/em&gt;&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;#  _\ _\ \/ _  / _&lt;em&gt;/ _ \/ _&lt;/em&gt;/ &lt;strong&gt;/ _ '/ /&lt;/strong&gt;&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# /&lt;em&gt;/&lt;/em&gt;&lt;strong&gt;/&lt;em&gt;//&lt;/em&gt;/_/   _&lt;/strong&gt;/&lt;em&gt;/  _&lt;/em&gt;/_,&lt;em&gt;/&lt;/em&gt;/&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;#&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;#&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# config&amp;gt; host ls&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;#   ID | NAME |           URL            |        KEY        |  GROUPS  |   UPDATED   |   CREATED   | COMMENT | HOP |  # LOGGING&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# -----+------+--------------------------+-------------------+---------+-------------+-------------+---------+-----+-------------&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;#   1 | knox | ssh://&lt;a href="mailto:knox@10.0.4.1"&gt;knox@10.0.4.1&lt;/a&gt;:2233 | nervous_goldstine | default | 3 hours ago | 4 hours ago |         |     | everything&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# Total: 1 hosts.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c"&gt;# Then for me, I can use&lt;/span&gt;&lt;br&gt;
ssh &lt;a href="mailto:knox@10.0.4.1"&gt;knox@10.0.4.1&lt;/a&gt;&lt;br&gt;
&lt;span class="c"&gt;# Which will jump from sshportal to ssh://&lt;a href="mailto:knox@10.0.4.1"&gt;knox@10.0.4.1&lt;/a&gt;:2233&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# Then I can add gitlab's ssh://git@gitlab&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# And wahla! A single ssh endpoint that I can use for multiple different services.&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# Just don't forget to go back and&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# setup mysql replication and/or &lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# occasionally backup the configs&lt;/span&gt;&lt;br&gt;
ssh &lt;a href="mailto:admin@10.0.4.1"&gt;admin@10.0.4.1&lt;/a&gt; config backup &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/sshportal/backup.json&lt;br&gt;
ssh &lt;a href="mailto:admin@10.0.4.1"&gt;admin@10.0.4.1&lt;/a&gt; config restore ~/.sshportal/backup.json &lt;span class="nt"&gt;--confirm&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h1&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Persistent Volumes&lt;br&gt;
&lt;/h1&gt;

&lt;p&gt;What good is all this kubernetes setup if nothing persists?&lt;/p&gt;

&lt;p&gt;Ideally everything would be ephemeral as possible. But somethings like databases need file storage. So let's set that up.&lt;/p&gt;

&lt;p&gt;[ coming next ]&lt;/p&gt;

</description>
    </item>
    <item>
      <title>🔧 Using git with a local ssh server</title>
      <dc:creator>Justin</dc:creator>
      <pubDate>Sun, 13 Nov 2022 15:06:16 +0000</pubDate>
      <link>https://dev.to/gjrdiesel/using-git-with-a-local-ssh-server-2e0f</link>
      <guid>https://dev.to/gjrdiesel/using-git-with-a-local-ssh-server-2e0f</guid>
      <description>&lt;p&gt;Let's say you're ripping on a project and maybe have two computers at home or you don't feel like sharing the project you're working on to github/gitlab just yet.&lt;/p&gt;

&lt;p&gt;You can use git + ssh to keep those two projects in sync and its super easy.&lt;/p&gt;

&lt;p&gt;This is also a good way to work projects across computers without having to rsync or scp files.&lt;/p&gt;

&lt;p&gt;Assuming you have a git project and a ssh connection already, you are pretty much all set. Just follow these instructions:&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;# on the remote server copy down what git you do have _or_&lt;/span&gt;
&lt;span class="c"&gt;# create a new folder/git to sync it to&lt;/span&gt;
ssh user@host &lt;span class="s2"&gt;"git init --bare /mnt/foo/bar/my-project.git"&lt;/span&gt;

&lt;span class="c"&gt;# then add that remote server&lt;/span&gt;
git remote add origin ssh://user@host/mnt/foo/bar/my-project.git

&lt;span class="c"&gt;# and finally, push to it!&lt;/span&gt;
git push origin master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep in mind, if you already have an origin, you can rename that to something like "local-server", then you have a "git push local-server" for local testing or "git push origin" for when its time to really deploy.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>📦 Set up minio w/ Laravel (Local S3)</title>
      <dc:creator>Justin</dc:creator>
      <pubDate>Sun, 13 Nov 2022 15:02:57 +0000</pubDate>
      <link>https://dev.to/gjrdiesel/set-up-minio-w-laravel-local-s3-2cj4</link>
      <guid>https://dev.to/gjrdiesel/set-up-minio-w-laravel-local-s3-2cj4</guid>
      <description>&lt;h1&gt;
  
  
  Example docker-compose.yml for development
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.9"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.web&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./:/app&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:8000"&lt;/span&gt;
  &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;minio&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;quay.io/minio/minio&lt;/span&gt;
      &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;server /data --console-address :9001&lt;/span&gt;
      &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;MINIO_ROOT_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;minio&lt;/span&gt;
          &lt;span class="na"&gt;MINIO_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;minio123&lt;/span&gt;
      &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:9000/minio/health/live"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
          &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
          &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;20s&lt;/span&gt;
          &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./storage/data:/data&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9005:9000"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9006:9001"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now with that in place, setup a &lt;code&gt;.env&lt;/code&gt;&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;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;minio
&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;minio123
&lt;span class="nv"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east-1
&lt;span class="nv"&gt;AWS_BUCKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;testbucket
&lt;span class="nv"&gt;AWS_USE_PATH_STYLE_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;AWS_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://minio:9000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that is it! You are ready to rock. Just remember to set&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;FILESYSTEM_DRIVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;s3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And let it eat!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Storage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;files&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stick around and later we'll learn how to upload files there!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>aws</category>
      <category>s3</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>Getting started with twirp 🦜</title>
      <dc:creator>Justin</dc:creator>
      <pubDate>Mon, 18 Apr 2022 11:51:43 +0000</pubDate>
      <link>https://dev.to/gjrdiesel/getting-started-with-twirp-2155</link>
      <guid>https://dev.to/gjrdiesel/getting-started-with-twirp-2155</guid>
      <description>&lt;p&gt;Twirp is a library that has all the strictly typed power of gRPC but retains the flexibility of HTTP/JSON for communication. &lt;/p&gt;

&lt;p&gt;You define your routes, requests, and responses in one &lt;code&gt;*.proto&lt;/code&gt; protobuf file.&lt;/p&gt;

&lt;p&gt;Then all consumers of your API can import the protobuf file and generate their own client library in their language of choice.&lt;/p&gt;

&lt;p&gt;Let's set up a quick and example.&lt;/p&gt;

&lt;p&gt;First things first, you'll need to install protobuf, so on mac, start with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;protobuf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that installed, you'll need to make sure you have golang setup as well as &lt;code&gt;GOBIN&lt;/code&gt; defined; These are some reasonable defaults to add to your &lt;code&gt;.zshrc&lt;/code&gt;:&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;GOBIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"~/go/bin"&lt;/span&gt;
&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;:~/go/bin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next you'll need to install the protobuf binary plugins to generate the files for both twirp and golang;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/twitchtv/twirp/protoc-gen-twirp@latest
go &lt;span class="nb"&gt;install &lt;/span&gt;google.golang.org/protobuf/cmd/protoc-gen-go@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm building a project called "ProxyMe" so I'll use it as an example; Let's start with a simple protobuf file to define the routes I'll start with;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;reaz&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io.proxyme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;option&lt;/span&gt; &lt;span class="na"&gt;go_package&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"reaz.io/proxyme"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;ProxyMe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;ListProxies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ListReq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ListRes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;ListReq&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;ListRes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's important to note is;&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;# We'll need this later, this is just a namespace for my projects, yours might be more like '{username}.com.{project_name}'&lt;/span&gt;
package reaz.io.proxyme&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the rest of the file goes onto describe a single &lt;code&gt;ListProxies&lt;/code&gt; endpoint that accepts a &lt;code&gt;ListReq&lt;/code&gt; (List request) and returns a &lt;code&gt;ListReq&lt;/code&gt; List Response.&lt;/p&gt;

&lt;p&gt;Now let's use twirp and protobuf to automatically scaffold some code for us;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;protoc &lt;span class="nt"&gt;--go_out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--twirp_out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; ProxyMe.proto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, we'll have something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;├── ProxyMe.proto
└── reaz.io
    └── proxyme
        ├── ProxyMe.pb.go
        └── ProxyMe.twirp.go

2 directories, 3 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original file we wrote, &lt;code&gt;ProxyMe.proto&lt;/code&gt;, and the namespace we used pointing to a &lt;code&gt;.pb.go&lt;/code&gt; which is the go generated portion and &lt;code&gt;*.twirp.go&lt;/code&gt; is the twirp magic that contains a HTTP service.&lt;/p&gt;

&lt;p&gt;With that, let's make a &lt;code&gt;server.go&lt;/code&gt; to actually implement the endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;

    &lt;span class="n"&gt;pb&lt;/span&gt; &lt;span class="s"&gt;"proxyme/reaz.io/proxyme"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ProxyMeServer&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ProxyMeServer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ListProxies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListReq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListRes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListRes&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Hello "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"8011"&lt;/span&gt;

&lt;span class="c"&gt;// Run the implementation in a local server&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;twirpHandler&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewProxyMeServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ProxyMeServer&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="c"&gt;// You can use any mux you like - NewHelloWorldServer gives you an http.Handler.&lt;/span&gt;
    &lt;span class="n"&gt;mux&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServeMux&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;// The generated code includes a method, PathPrefix(), which&lt;/span&gt;
    &lt;span class="c"&gt;// can be used to mount your service on a mux.&lt;/span&gt;
    &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twirpHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PathPrefix&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;twirpHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Listening on http://0.0.0.0:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;twirpHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PathPrefix&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, we're mostly importing our generated code, adding a &lt;code&gt;ListProxies&lt;/code&gt; that uses our &lt;code&gt;ListReq&lt;/code&gt;, &lt;code&gt;ListRes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now to run it with the following:&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;go run server.go
2022/04/18 07:07:16 Listening on http://0.0.0.0:8011/twirp/reaz.io.proxyme.ProxyMe/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With it running, we can test it using the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://0.0.0.0:8011/twirp/reaz.io.proxyme.ProxyMe/ListProxies &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "subject": "mike"
}'&lt;/span&gt;

&lt;span class="c"&gt;# Should see the following for a response&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"text"&lt;/span&gt;: &lt;span class="s2"&gt;"Hello mike"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now just generate client code in other languages and use the new service!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Clients in other languages can also be generated by using the respective protoc plugins defined by their languages, for example --twirp_ruby_out.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And can use protobuf or json, all generated from one protobuf file!&lt;/p&gt;

&lt;p&gt;Learn more about &lt;a href="https://twitchtv.github.io/twirp/docs/example.html#use-the-client"&gt;how to generate client code here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>grpc</category>
      <category>api</category>
    </item>
    <item>
      <title>🪖 Keycloak instance on docker</title>
      <dc:creator>Justin</dc:creator>
      <pubDate>Sat, 18 Dec 2021 14:23:25 +0000</pubDate>
      <link>https://dev.to/gjrdiesel/setup-a-production-grade-keycloak-instance-on-docker-19dk</link>
      <guid>https://dev.to/gjrdiesel/setup-a-production-grade-keycloak-instance-on-docker-19dk</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%2Fthmjf8mvw603gxtoedgl.gif" 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%2Fthmjf8mvw603gxtoedgl.gif" alt="princess bride: begin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stand up a keycloak instance w/ username: admin, password: pa55word. With postgres as a database. And SSL forwarding (for use with like cloudflare).&lt;/p&gt;

&lt;p&gt;Create this docker-compose file i.e. &lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&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;postgres&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres_data:/var/lib/postgresql/data&lt;/span&gt;
      &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
        &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
        &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
  &lt;span class="na"&gt;keycloak&lt;/span&gt;&lt;span class="pi"&gt;:&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;quay.io/keycloak/keycloak:latest&lt;/span&gt;
      &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;DB_VENDOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POSTGRES&lt;/span&gt;
        &lt;span class="na"&gt;DB_ADDR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
        &lt;span class="na"&gt;DB_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
        &lt;span class="na"&gt;DB_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
        &lt;span class="na"&gt;DB_SCHEMA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;public&lt;/span&gt;
        &lt;span class="na"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
        &lt;span class="na"&gt;KEYCLOAK_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
        &lt;span class="na"&gt;KEYCLOAK_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pa55w0rd&lt;/span&gt;
        &lt;span class="na"&gt;PROXY_ADDRESS_FORWARDING&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;
        &lt;span class="c1"&gt;# Uncomment the line below if you want to specify JDBC parameters. The parameter below is just an example, and it shouldn't be used in production without knowledge. It is highly recommended that you read the PostgreSQL JDBC driver documentation in order to use it.&lt;/span&gt;
        &lt;span class="c1"&gt;#JDBC_PARAMS: "ssl=true"&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="s"&gt;8010:8080&lt;/span&gt;
      &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then run &lt;code&gt;docker-compose up -d&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that in place, you just need to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make a new realm&lt;/li&gt;
&lt;li&gt;Make a new client&lt;/li&gt;
&lt;li&gt;Add a valid redirect uri to the client (from the application you are wishing to secure)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then you can add the following type of logic for authentication on your browser facing application&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;keycloak&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;keycloak&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Keycloak&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://[key-cloak-instance]/auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;realm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[your-realm]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[your-client]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;keycloak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;checkLoginIframe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keycloak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenParsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;family_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;keycloak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenParsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;given_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;keycloak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenParsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;given_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;keycloak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenParsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;family_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;keycloak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenParsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preferred_username&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;// Set axios header for auth&lt;/span&gt;
            &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;keycloak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not Authenticated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;keycloak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hide&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;show&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hide&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;show&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onhashchange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then on your server (nodejs/express in this case), add the following logic:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express-jwt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://[keycloak-instance]/auth/realms/[your-realm]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`-----BEGIN PUBLIC KEY-----\r\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\r\n-----END PUBLIC KEY-----`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jwtMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;algorithms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RS256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And then you can either add the middleware globally&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jwtMiddleware&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Or add it to a specific route&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/endpoint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jwtMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;something-secure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now good luck to those pesky people that were previously trying to storm your castle!&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%2Fxf5puxaet3totw41mbjp.gif" 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%2Fxf5puxaet3totw41mbjp.gif" alt="princess bride: bye boys! have fun storming the castle"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>docker</category>
      <category>authentication</category>
      <category>express</category>
    </item>
    <item>
      <title>🐳 Debugging Docker Engine Connection Issues on MacOS</title>
      <dc:creator>Justin</dc:creator>
      <pubDate>Mon, 13 Dec 2021 12:22:40 +0000</pubDate>
      <link>https://dev.to/gjrdiesel/debugging-docker-engine-connection-issues-on-macos-24ko</link>
      <guid>https://dev.to/gjrdiesel/debugging-docker-engine-connection-issues-on-macos-24ko</guid>
      <description>&lt;p&gt;So I was working on adding an insecure local registry to push images to and I managed to bomb my local docker installation.&lt;/p&gt;

&lt;p&gt;The Docker desktop app wouldn't pull up at all and I couldn't find any logs or even find docker running with something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ps aux | &lt;span class="nb"&gt;grep &lt;/span&gt;docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I kept getting this error;&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;docker ps

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So after enough googling, I found the place the logs were stored was around &lt;code&gt;~/Library/Containers/com.docker.docker/Data/log/&lt;/code&gt;, it was this log specifically that lead me to the right place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat ~/Library/Containers/com.docker.docker/Data/log/vm/docker.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This showed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
2021-12-12T16:45:46Z docker time="2021-12-12T16:45:46Z" level=error msg="(5c94f517) 0ff224fd-DockerdPKG C&amp;lt;-S a2f49a7f-VMAPI GET /engine/daemon.json (9.946583ms): while parsing JSON from /Users/justin/.docker/daemon.json: invalid character '\"' after object key:value pair"
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ah! I made a typo when updated my &lt;code&gt;daemon.json&lt;/code&gt; to allow an insecure repository! After patching that up, I needed a way to restart docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;killall Docker &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; open /Applications/Docker.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, it was all ready to go! Hopefully it helps you track down any issues for you!&lt;/p&gt;

</description>
      <category>docker</category>
    </item>
    <item>
      <title>🚰 Docker connection reset during builds</title>
      <dc:creator>Justin</dc:creator>
      <pubDate>Sat, 27 Nov 2021 17:31:16 +0000</pubDate>
      <link>https://dev.to/gjrdiesel/docker-connection-reset-during-builds-5i1</link>
      <guid>https://dev.to/gjrdiesel/docker-connection-reset-during-builds-5i1</guid>
      <description>&lt;p&gt;Recently while trying to deploy a test docker container on my homelab's docker instance, I kept running into an error similar to this one&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Caused by: java.net.SocketException: Connection reset
 at java.net.SocketInputStream.read(SocketInputStream.java:210)
 at java.net.SocketInputStream.read(SocketInputStream.java:141)
 at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a bunch of digging, I ran into this medium article; &lt;a href="https://medium.com/swlh/fix-a-random-network-connection-reset-issue-in-docker-kubernetes-5c57a11de170"&gt;https://medium.com/swlh/fix-a-random-network-connection-reset-issue-in-docker-kubernetes-5c57a11de170&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It summarized it's findings pretty well and walked through the whole discovery process. Something I appreciate.&lt;/p&gt;

&lt;p&gt;Ultimately for it, it boiled down to running these commands to backup, flush, and reset iptables&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Keep a backup of the current rules incase we have to go back
iptables-save &amp;gt; /root/firewall.rules

# Flush all the current rules
iptables -F
iptables -X
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT

# Confirm the rules are now empty
iptables -L
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I had to restart docker with &lt;code&gt;service docker restart&lt;/code&gt; for docker to reset it's own iptable rules. But from then on, no more connection reset issues.&lt;/p&gt;

&lt;p&gt;In the event you need to roll back, you can use&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iptables-resoter &amp;lt; /root/firewall.rules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now get back to building something awesome!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>networking</category>
    </item>
    <item>
      <title>🖥 Using vim as a laravel/phpstorm developer</title>
      <dc:creator>Justin</dc:creator>
      <pubDate>Sat, 03 Jul 2021 13:45:02 +0000</pubDate>
      <link>https://dev.to/gjrdiesel/using-vim-as-a-laravel-phpstorm-developer-d44</link>
      <guid>https://dev.to/gjrdiesel/using-vim-as-a-laravel-phpstorm-developer-d44</guid>
      <description>&lt;p&gt;These are my quick notes on how to start seriously using vim as a laravel developer. My goal is to replace phpstorm w/ vim and match the feature set. For what I'm doing now, microservices in laravel/php, phpstorm is struggling to keep up.&lt;/p&gt;

&lt;p&gt;My apologies they're a bit of a mess as they're undergoing, but wanted to get them out there if anyone has some feedback or suggestions I'd love to hear it!&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%2Fntl8nvvpyrkposcyxe9k.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%2Fntl8nvvpyrkposcyxe9k.png" alt="Screenshot of VIM Setup underway" width="800" height="579"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Using vim as a laravel developer
Ok, using vim as a phpstorm replacment, this is prettyyyy close.

Here are the steps and the useful parts to know.

# First things first, install awesome-vim, this gets 90% of the stuff you need and its maintained
git clone --depth=1 https://github.com/amix/vimrc.git ~/.vim_runtime
sh ~/.vim_runtime/install_awesome_vimrc.sh

# Next I'd say you're going to want phpunit baked in and here is an awesome plugin for that:
git clone https://github.com/c9s/phpunit.vim.git ~/.vim_runtime/my_plugins/phpunit.vim

# Run inside vim: -- this will let you click and drag panes to resize or jump between (learning vim more and being able to jump panes
# makes this unecessary, but i'm not that good yet.)
:set mouse=a

# So now some sections on easy key commands/shortcuts

## Splitting VIM screen Horizontally and Vertically
To open a new VIM window next to the existing one, press &amp;lt;Ctrl&amp;gt;+&amp;lt;w&amp;gt; then press &amp;lt;v&amp;gt;.

## Move panes around vim (left/right or top/bottom)
Ctrl w + L - Move the current window to the "far right"
Ctrl w + H - Move the current window to the "far left"
Ctrl w + J - Move the current window to the "very bottom"
Ctrl w + K - Move the current window to the "very top"

## Copying everything into clipboard
gg"*yG

## Indenting all the code
# Still need to look into a more serious formatter like:
https://github.com/vim-autoformat/vim-autoformat
gg=G

# AwesomeVIM Leader Key Shortcut
You'll see vim plugins mention &amp;lt;leader&amp;gt;, that &amp;lt;leader&amp;gt; for awesome view is "," so whenever you see leader hit that key.

## phpunit
### Set the path of phpunit (most cases for me, vendor/bin/phpunit)
let g:phpunit_bin = 'phpunit'

### Shortcuts
&amp;lt;leader&amp;gt;ta - Run all test cases
&amp;lt;leader&amp;gt;ts - Switch between source &amp;amp; test file
&amp;lt;leader&amp;gt;tf - Run current test case class

# Folding
`zo` to open folding
`zc` to close folding

# NerdTREE
&amp;lt;leader&amp;gt;nn - Toggles NerdTREE
While inside NerdTREE hit "m" to do a number of modifications from renaming, deleting or adding files.

# Terminal
Typing ":term" will open a terminal pane similar to phpstorm.

Of course you can reference https://github.com/amix/vimrc for more tips and tricks, such as &amp;lt;leader&amp;gt;w to write a file or &amp;lt;Ctrl+f&amp;gt; to fuzzy search for a file.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'd like to make a video to accompany this setup to better describe how to use vim in this way so let me know if that's anything you'd like to see and best of luck in the new vim setup!&lt;/p&gt;

</description>
      <category>vim</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Getting around Brew's "Error: Calling Non-checksummed download of..."</title>
      <dc:creator>Justin</dc:creator>
      <pubDate>Thu, 08 Oct 2020 11:52:11 +0000</pubDate>
      <link>https://dev.to/gjrdiesel/getting-around-brew-s-error-calling-non-checksummed-download-of-17fl</link>
      <guid>https://dev.to/gjrdiesel/getting-around-brew-s-error-calling-non-checksummed-download-of-17fl</guid>
      <description>&lt;p&gt;I ran into this error while trying to &lt;a href="https://dev.to/gjrdiesel/installing-curl-with-http3-on-macos-2di2"&gt;install curl w/ http3 support&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install --HEAD -s https://raw.githubusercontent.com/cloudflare/homebrew-cloudflare/master/curl.rb

Error: Calling Non-checksummed download of curl formula file from an arbitrary URL is disabled! Use 'brew extract' or 'brew create' and 'brew tap-new' to create a formula file in a tap on GitHub instead.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The suggested fixes, &lt;code&gt;brew extract&lt;/code&gt;, &lt;code&gt;brew create&lt;/code&gt;, or &lt;code&gt;brew tap-new&lt;/code&gt; are totally foreign to me and I wasn't sure where to begin. Googling it lead me down a path of more brew related terminology I had never heard of before.&lt;/p&gt;

&lt;p&gt;Easiest way to bypass it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wget [the-url]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install --HEAD -s [the-script]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So from my example above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Instead of
brew install --HEAD -s https://raw.githubusercontent.com/cloudflare/homebrew-cloudflare/master/curl.rb

# Do
wget https://raw.githubusercontent.com/cloudflare/homebrew-cloudflare/master/curl.rb

brew install --HEAD -s curl.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course make sure you only do this for trusted sources!&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Installing curl with http3 on MacOS</title>
      <dc:creator>Justin</dc:creator>
      <pubDate>Thu, 08 Oct 2020 11:34:03 +0000</pubDate>
      <link>https://dev.to/gjrdiesel/installing-curl-with-http3-on-macos-2di2</link>
      <guid>https://dev.to/gjrdiesel/installing-curl-with-http3-on-macos-2di2</guid>
      <description>&lt;p&gt;When you are all said and done with this tutorial, you'll be able to double check http3 support via the curl command like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --http3 https://cloudflare-quic.com -I
HTTP/3 200
date: Fri, 24 Jul 2020 08:10:24 GMT
content-type: text/html
content-length: 106072
set-cookie: __cfduid=d8647a359d68a89c060bde4f373e18cc61595578224; expires=Sun, 23-Aug-20 08:10:24 GMT; path=/; domain=.cloudflare-quic.com; HttpOnly; SameSite=Lax; Secure
cf-request-id: 042178a64b0000188b7b1d9200000001
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
server: cloudflare
cf-ray: 5b7c2a1d4eb5188b-MAN
alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, this currently involves building curl from source, luckily for us, &lt;a href="https://developers.cloudflare.com/http3/intro/curl-brew/" rel="noopener noreferrer"&gt;cloudflare makes this even easier&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Note: You must have &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;https://brew.sh/&lt;/a&gt; installed already
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Clean up any old version of curl you may have already tried to install
brew remove -f curl

# Download the curl ruby install script provided by cloudflare
wget https://raw.githubusercontent.com/cloudflare/homebrew-cloudflare/master/curl.rb

# Install curl via that script from the latest git repos
brew install --HEAD -s curl.rb

# Tell your cli to use the curl version just installed (if you're using zsh, othwerise you might need `~/.bashrc`)
echo 'export PATH="/usr/local/opt/curl/bin:$PATH"' &amp;gt;&amp;gt; ~/.zshrc

# Reload your config
source ~/.zshrc

# Double check it's using the right curl
which curl # Should output "/usr/local/opt/curl/bin/curl"

# Double check http3
$ curl --version | grep HTTP3
  Features: alt-svc AsynchDNS brotli HTTP2 HTTP3 IDN IPv6 Largefile libz MultiSSL NTLM NTLM_WB SSL UnixSockets zstd

# Try curl on any HTTP/3 enabled sites.
curl --http3 https://blog.cloudflare.com -I
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Have fun upgrading all the servers!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Profiling PHP from your Mac</title>
      <dc:creator>Justin</dc:creator>
      <pubDate>Thu, 04 Jun 2020 12:58:47 +0000</pubDate>
      <link>https://dev.to/gjrdiesel/profiling-php-from-your-mac-2fhk</link>
      <guid>https://dev.to/gjrdiesel/profiling-php-from-your-mac-2fhk</guid>
      <description>&lt;p&gt;I'm going to use a few open source tools to accomplish this, but alternatively, blackfire.io is an awesome service that handles all this setup for you if you'd rather pay to skip the elaborate setup.&lt;/p&gt;

&lt;p&gt;The tools we're going to use are xdebug which will log/profile each part of your application to a file and then qcachegrind will let you visually inspect that log to find where the slow downs are happening.&lt;/p&gt;

&lt;p&gt;So on a mac, to get started run&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;# To install xdebug&lt;/span&gt;
pecl &lt;span class="nb"&gt;install &lt;/span&gt;xdebug
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the end of the xdebug install you'll see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20190902/xdebug.so
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where it's telling you to add that to your php.ini file, to find where your php.ini file is, run &lt;code&gt;php -i | grep "Configuration File"&lt;/code&gt; and you should see something like:&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;php &lt;span class="nt"&gt;-i&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Configuration File"&lt;/span&gt;
Configuration File &lt;span class="o"&gt;(&lt;/span&gt;php.ini&lt;span class="o"&gt;)&lt;/span&gt; Path &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; /usr/local/etc/php
Loaded Configuration File &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;none&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now just append that zend_extension line from earlier:&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;echo&lt;/span&gt; &lt;span class="s1"&gt;'zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20190902/xdebug.so'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /usr/local/etc/php/php.ini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run &lt;code&gt;php -v&lt;/code&gt; and you should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@api-local:/srv/app# php &lt;span class="nt"&gt;-v&lt;/span&gt;
PHP 7.4.5 &lt;span class="o"&gt;(&lt;/span&gt;cli&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;built: Apr 23 2020 16:44:34&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt; NTS &lt;span class="o"&gt;)&lt;/span&gt;
Copyright &lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;)&lt;/span&gt; The PHP Group
Zend Engine v3.4.0, Copyright &lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;)&lt;/span&gt; Zend Technologies
    with Xdebug v2.9.6, Copyright &lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;)&lt;/span&gt; 2002-2020, by Derick Rethans
    with Zend OPcache v7.4.5, Copyright &lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;)&lt;/span&gt;, by Zend Technologies
root@api-local:/srv/app#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now with xdebug installed, let's set it up to start profiling requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vi /usr/local/etc/php/php.ini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make yours look something like this now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20190902/xdebug.so
xdebug.profiler_enable=1
xdebug.profiler_enable_trigger=0
xdebug.profiler_output_dir=/tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how &lt;code&gt;xdebug.profiler_enable=1&lt;/code&gt; is on, and &lt;code&gt;xdebug.profiler_enable_trigger=0&lt;/code&gt; is off, that's cause we want to make sure this works and later we can add a chrome extension so we'll only profile when we need.&lt;/p&gt;

&lt;p&gt;So with &lt;code&gt;xdebug.profiler_enable=1&lt;/code&gt; enabled, hit a page on your application and make sure you see something like this in your tmp directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@api-local:/srv/app# &lt;span class="nb"&gt;ls&lt;/span&gt; /tmp/cachegrind&lt;span class="k"&gt;*&lt;/span&gt;
/tmp/cachegrind.out.10812
root@api-local:/srv/app#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now install &lt;code&gt;qcachegrind&lt;/code&gt; so you can inspect the dumped profile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;qcachegrind
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that installed, use your spotlight shortcut (cmd+spacebar) and type qcachegrind to find and open the visualizer tool, and use the tool to open the &lt;code&gt;/tmp/cachegrind.out.10812&lt;/code&gt; file that gets made:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ns4MMVk4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/l93a3zsqi8gqhocz6s2k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ns4MMVk4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/l93a3zsqi8gqhocz6s2k.png" alt="Screenshot of qcachegrind" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now with it knowingly working, you can edit your &lt;code&gt;php.ini&lt;/code&gt; to disable profiler always and set it to enable trigger.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vi /usr/local/etc/php/php.ini

&lt;span class="nv"&gt;zend_extension&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/lib/php/extensions/no-debug-non-zts-20190902/xdebug.so
xdebug.profiler_enable&lt;span class="o"&gt;=&lt;/span&gt;0
xdebug.profiler_enable_trigger&lt;span class="o"&gt;=&lt;/span&gt;1
xdebug.profiler_output_dir&lt;span class="o"&gt;=&lt;/span&gt;/tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use a extension like &lt;a href="https://chrome.google.com/webstore/detail/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc"&gt;xdebug helper for chrome&lt;/a&gt; to toggle it when needed.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
